From c2c0b5d0011d37771b34bd661a0e7509807ad79d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 2 Oct 2002 23:36:51 -0700 Subject: PCI: remove pcibios_find_class() --- include/linux/pci.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/linux/pci.h b/include/linux/pci.h index ba2e997304fd..ec8f0686e24a 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -533,7 +533,6 @@ int pcibios_write_config_word (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned short val); int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned int val); -int pcibios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *dev_fn); int pcibios_find_device (unsigned short vendor, unsigned short dev_id, unsigned short index, unsigned char *bus, unsigned char *dev_fn); @@ -661,8 +660,6 @@ void pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t addr); #ifndef CONFIG_PCI static inline int pcibios_present(void) { return 0; } -static inline int pcibios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *dev_fn) -{ return PCIBIOS_DEVICE_NOT_FOUND; } #define _PCI_NOP(o,s,t) \ static inline int pcibios_##o##_config_##s (u8 bus, u8 dfn, u8 where, t val) \ -- cgit v1.2.3 From 4a66ae8251604a7c8a262e8d2302a26de62a2b16 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 2 Oct 2002 23:45:53 -0700 Subject: PCI: remove pci_find_device() --- drivers/pci/compat.c | 17 ----------------- include/linux/pci.h | 3 --- 2 files changed, 20 deletions(-) (limited to 'include') diff --git a/drivers/pci/compat.c b/drivers/pci/compat.c index 4082c8f087b2..51e9b0828d08 100644 --- a/drivers/pci/compat.c +++ b/drivers/pci/compat.c @@ -19,22 +19,6 @@ pcibios_present(void) return !list_empty(&pci_devices); } -int -pcibios_find_device(unsigned short vendor, unsigned short device, unsigned short index, - unsigned char *bus, unsigned char *devfn) -{ - const struct pci_dev *dev = NULL; - int cnt = 0; - - while ((dev = pci_find_device(vendor, device, dev))) - if (index == cnt++) { - *bus = dev->bus->number; - *devfn = dev->devfn; - return PCIBIOS_SUCCESSFUL; - } - return PCIBIOS_DEVICE_NOT_FOUND; -} - #define PCI_OP(rw,size,type) \ int pcibios_##rw##_config_##size (unsigned char bus, unsigned char dev_fn, \ unsigned char where, unsigned type val) \ @@ -59,4 +43,3 @@ EXPORT_SYMBOL(pcibios_read_config_dword); EXPORT_SYMBOL(pcibios_write_config_byte); EXPORT_SYMBOL(pcibios_write_config_word); EXPORT_SYMBOL(pcibios_write_config_dword); -EXPORT_SYMBOL(pcibios_find_device); diff --git a/include/linux/pci.h b/include/linux/pci.h index ec8f0686e24a..933e62a6813c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -533,9 +533,6 @@ int pcibios_write_config_word (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned short val); int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned int val); -int pcibios_find_device (unsigned short vendor, unsigned short dev_id, - unsigned short index, unsigned char *bus, - unsigned char *dev_fn); /* Generic PCI functions used internally */ -- cgit v1.2.3 From 36be8435c1ad70461e93840606a1b4ef2c9e7f5f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 3 Oct 2002 00:06:24 -0700 Subject: PCI: removed pcibios_present() --- drivers/net/hp100.c | 4 ++-- drivers/net/tulip/de4x5.c | 4 ++-- drivers/pci/compat.c | 8 -------- drivers/pci/syscall.c | 2 +- include/linux/pci.h | 21 ++++++++++----------- 5 files changed, 15 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index 7a23fe6bd93b..de55257a8c7f 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -412,7 +412,7 @@ int __init hp100_probe(struct net_device *dev) /* First: scan PCI bus(es) */ #ifdef CONFIG_PCI - if (pcibios_present()) { + if (pci_present()) { int pci_index; struct pci_dev *pci_dev = NULL; int pci_id_index; @@ -2960,7 +2960,7 @@ static int __init hp100_module_init(void) { int i, cards; - if (hp100_port == 0 && !EISA_bus && !pcibios_present()) + if (hp100_port == 0 && !EISA_bus && !pci_present()) printk("hp100: You should not use auto-probing with insmod!\n"); /* Loop on all possible base addresses */ diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c index 93c863801086..e4cc1f9eaf1f 100644 --- a/drivers/net/tulip/de4x5.c +++ b/drivers/net/tulip/de4x5.c @@ -2190,7 +2190,7 @@ pci_probe(struct net_device *dev, u_long ioaddr) if (lastPCI == NO_MORE_PCI) return; - if (!pcibios_present()) { + if (!pci_present()) { lastPCI = NO_MORE_PCI; return; /* No PCI bus in this machine! */ } @@ -5872,7 +5872,7 @@ count_adapters(void) if (EISA_signature(name, EISA_ID)) j++; } #endif - if (!pcibios_present()) return j; + if (!pci_present()) return j; for (i=0; (pdev=pci_find_class(class, pdev))!= NULL; i++) { vendor = pdev->vendor; diff --git a/drivers/pci/compat.c b/drivers/pci/compat.c index 51e9b0828d08..048c610a9e62 100644 --- a/drivers/pci/compat.c +++ b/drivers/pci/compat.c @@ -13,12 +13,6 @@ /* Obsolete functions, these will be going away... */ -int -pcibios_present(void) -{ - return !list_empty(&pci_devices); -} - #define PCI_OP(rw,size,type) \ int pcibios_##rw##_config_##size (unsigned char bus, unsigned char dev_fn, \ unsigned char where, unsigned type val) \ @@ -35,8 +29,6 @@ PCI_OP(write, byte, char) PCI_OP(write, word, short) PCI_OP(write, dword, int) - -EXPORT_SYMBOL(pcibios_present); EXPORT_SYMBOL(pcibios_read_config_byte); EXPORT_SYMBOL(pcibios_read_config_word); EXPORT_SYMBOL(pcibios_read_config_dword); diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c index c935efd9a933..dd39d23d51ed 100644 --- a/drivers/pci/syscall.c +++ b/drivers/pci/syscall.c @@ -98,7 +98,7 @@ sys_pciconfig_write(unsigned long bus, unsigned long dfn, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!pcibios_present()) + if (!pci_present()) return -ENOSYS; dev = pci_find_slot(bus, dfn); diff --git a/include/linux/pci.h b/include/linux/pci.h index 933e62a6813c..9c12d53f9dc7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -322,15 +322,6 @@ enum pci_mmap_state { #define PCI_ANY_ID (~0) -#define pci_present pcibios_present - - -#define pci_for_each_dev_reverse(dev) \ - for(dev = pci_dev_g(pci_devices.prev); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.prev)) - -#define pci_for_each_bus(bus) \ -for(bus = pci_bus_b(pci_root_buses.next); bus != pci_bus_b(&pci_root_buses); bus = pci_bus_b(bus->node.next)) - /* * The pci_dev structure is used to describe both PCI and ISAPnP devices. */ @@ -503,8 +494,17 @@ struct pci_driver { /* these external functions are only available when PCI support is enabled */ #ifdef CONFIG_PCI +static inline int pci_present(void) +{ + return !list_empty(&pci_devices); +} + #define pci_for_each_dev(dev) \ for(dev = pci_dev_g(pci_devices.next); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.next)) +#define pci_for_each_dev_reverse(dev) \ + for(dev = pci_dev_g(pci_devices.prev); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.prev)) +#define pci_for_each_bus(bus) \ + for(bus = pci_bus_b(pci_root_buses.next); bus != pci_bus_b(&pci_root_buses); bus = pci_bus_b(bus->node.next)) void pcibios_fixup_bus(struct pci_bus *); int pcibios_enable_device(struct pci_dev *, int mask); @@ -520,7 +520,6 @@ void pcibios_fixup_pbus_ranges(struct pci_bus *, struct pbus_set_ranges_data *); /* Backward compatibility, don't use in new code! */ -int pcibios_present(void); int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned char *val); int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn, @@ -656,7 +655,7 @@ void pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t addr); */ #ifndef CONFIG_PCI -static inline int pcibios_present(void) { return 0; } +static inline int pci_present(void) { return 0; } #define _PCI_NOP(o,s,t) \ static inline int pcibios_##o##_config_##s (u8 bus, u8 dfn, u8 where, t val) \ -- cgit v1.2.3 From 20ddfc0047bd762ed32ec42e50b5a14bad02728d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 3 Oct 2002 20:43:42 -0700 Subject: [PATCH] remove _P/_p delaying iops Lets kill these off for good. o Remove OUT_BYTE/IN_BYTE and variants. We defaulted to the fast ones even before o Add read barrier for ppc, it needs it --- drivers/ide/ide-iops.c | 182 ++++++++++++++++++++++++------------------------- drivers/ide/ide.c | 10 --- include/linux/ide.h | 55 --------------- 3 files changed, 90 insertions(+), 157 deletions(-) (limited to 'include') diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 19304b007aad..353e71f6eef7 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -32,135 +32,156 @@ #include -static inline u8 ide_inb (u32 port) +static u8 ide_inb (u32 port) { - return (u8) IN_BYTE(port); + return (u8) inb(port); } -static inline u8 ide_inb_p (u32 port) +static u16 ide_inw (u32 port) { - return (u8) IN_BYTE_P(port); + return (u16) inw(port); } -static inline u16 ide_inw (u32 port) +static void ide_insw (u32 port, void *addr, u32 count) { - return (u16) IN_WORD(port); + return insw(port, addr, count); } -static inline u16 ide_inw_p (u32 port) +static u32 ide_inl (u32 port) { - return (u16) IN_WORD_P(port); + return (u32) inl(port); } -static inline void ide_insw (u32 port, void *addr, u32 count) +static void ide_insl (u32 port, void *addr, u32 count) { - while (count--) { *(u16 *)addr = IN_WORD(port); addr += 2; } + insl(port, addr, count); } -static inline void ide_insw_p (u32 port, void *addr, u32 count) +static void ide_outb (u8 addr, u32 port) { - while (count--) { *(u16 *)addr = IN_WORD_P(port); addr += 2; } + outb(addr, port); } -static inline u32 ide_inl (u32 port) +static void ide_outw (u16 addr, u32 port) { - return (u32) IN_LONG(port); + outw(addr, port); } -static inline u32 ide_inl_p (u32 port) +static void ide_outsw (u32 port, void *addr, u32 count) { - return (u32) IN_LONG_P(port); + outsw(port, addr, count); } -static inline void ide_insl (u32 port, void *addr, u32 count) +static void ide_outl (u32 addr, u32 port) { - ide_insw(port, addr, (count)<<1); -// while (count--) { *(u32 *)addr = IN_LONG(port); addr += 4; } + outl(addr, port); } -static inline void ide_insl_p (u32 port, void *addr, u32 count) +static void ide_outsl (u32 port, void *addr, u32 count) { - ide_insw_p(port, addr, (count)<<1); -// while (count--) { *(u32 *)addr = IN_LONG(port); addr += 4; } + return outsl(port, addr, count); } -static inline void ide_outb (u8 addr, u32 port) +void default_hwif_iops (ide_hwif_t *hwif) { - OUT_BYTE(addr, port); + hwif->OUTB = ide_outb; + hwif->OUTW = ide_outw; + hwif->OUTL = ide_outl; + hwif->OUTSW = ide_outsw; + hwif->OUTSL = ide_outsl; + hwif->INB = ide_inb; + hwif->INW = ide_inw; + hwif->INL = ide_inl; + hwif->INSW = ide_insw; + hwif->INSL = ide_insl; } -static inline void ide_outb_p (u8 addr, u32 port) +EXPORT_SYMBOL(default_hwif_iops); + +static u8 ide_mm_inb (u32 port) { - OUT_BYTE_P(addr, port); + return (u8) readb(port); } -static inline void ide_outw (u16 addr, u32 port) +static u16 ide_mm_inw (u32 port) { - OUT_WORD(addr, port); + return (u16) readw(port); } -static inline void ide_outw_p (u16 addr, u32 port) +static void ide_mm_insw (u32 port, void *addr, u32 count) { - OUT_WORD_P(addr, port); +#ifdef CONFIG_PPC + /* Can we move the barrier out of the loop ? */ + while (count--) { *(u16 *)addr = __raw_readw(port); iobarrier_r(); addr += 2; } +#else /* everything else is sane benh */ + while (count--) { *(u16 *)addr = readw(port); addr += 2; } +#endif } -static inline void ide_outsw (u32 port, void *addr, u32 count) +static u32 ide_mm_inl (u32 port) { - while (count--) { OUT_WORD(*(u16 *)addr, port); addr += 2; } + return (u32) readl(port); } -static inline void ide_outsw_p (u32 port, void *addr, u32 count) +static void ide_mm_insl (u32 port, void *addr, u32 count) { - while (count--) { OUT_WORD_P(*(u16 *)addr, port); addr += 2; } +#ifdef CONFIG_PPC + /* Can we move the barrier out of the loop ? */ + while (count--) { *(u32 *)addr = __raw_readl(port); iobarrier_r(); addr += 4; } +#else /* everything else is sane benh */ + while (count--) { *(u32 *)addr = readl(port); addr += 4; } +#endif } -static inline void ide_outl (u32 addr, u32 port) +static void ide_mm_outb (u8 value, u32 port) { - OUT_LONG(addr, port); + writeb(value, port); } -static inline void ide_outl_p (u32 addr, u32 port) +static void ide_mm_outw (u16 value, u32 port) { - OUT_LONG_P(addr, port); + writew(value, port); } -static inline void ide_outsl (u32 port, void *addr, u32 count) +static void ide_mm_outsw (u32 port, void *addr, u32 count) { - ide_outsw(port, addr, (count)<<1); -// while (count--) { OUT_LONG(*(u32 *)addr, port); addr += 4; } +#ifdef CONFIG_PPC + /* Can we move the barrier out of the loop ? */ + while (count--) { __raw_writew(*(u16 *)addr, port); iobarrier_w(); addr += 2; } +#else /* everything else is sane benh */ + while (count--) { writew(*(u16 *)addr, port); addr += 2; } +#endif } -static inline void ide_outsl_p (u32 port, void *addr, u32 count) +static void ide_mm_outl (u32 value, u32 port) { - ide_outsw_p(port, addr, (count)<<1); -// while (count--) { OUT_LONG_P(*(u32 *)addr, port); addr += 4; } + writel(value, port); } -void default_hwif_iops (ide_hwif_t *hwif) +static void ide_mm_outsl (u32 port, void *addr, u32 count) { - hwif->OUTB = ide_outb; - hwif->OUTBP = ide_outb_p; - hwif->OUTW = ide_outw; - hwif->OUTWP = ide_outw_p; - hwif->OUTL = ide_outl; - hwif->OUTLP = ide_outl_p; - hwif->OUTSW = ide_outsw; - hwif->OUTSWP = ide_outsw_p; - hwif->OUTSL = ide_outsl; - hwif->OUTSLP = ide_outsl_p; - hwif->INB = ide_inb; - hwif->INBP = ide_inb_p; - hwif->INW = ide_inw; - hwif->INWP = ide_inw_p; - hwif->INL = ide_inl; - hwif->INLP = ide_inl_p; - hwif->INSW = ide_insw; - hwif->INSWP = ide_insw_p; - hwif->INSL = ide_insl; - hwif->INSLP = ide_insl_p; +#ifdef CONFIG_PPC + while (count--) { __raw_writel(*(u32 *)addr, port); iobarrier_w(); addr += 4; } +#else /* everything else is sane benh */ + while (count--) { writel(*(u32 *)addr, port); addr += 4; } +#endif } -EXPORT_SYMBOL(default_hwif_iops); +void default_hwif_mmiops (ide_hwif_t *hwif) +{ + hwif->OUTB = ide_mm_outb; + hwif->OUTW = ide_mm_outw; + hwif->OUTL = ide_mm_outl; + hwif->OUTSW = ide_mm_outsw; + hwif->OUTSL = ide_mm_outsl; + hwif->INB = ide_mm_inb; + hwif->INW = ide_mm_inw; + hwif->INL = ide_mm_inl; + hwif->INSW = ide_mm_insw; + hwif->INSL = ide_mm_insl; +} + +EXPORT_SYMBOL(default_hwif_mmiops); void default_hwif_transport (ide_hwif_t *hwif) { @@ -217,7 +238,6 @@ void QUIRK_LIST (ide_drive_t *drive) EXPORT_SYMBOL(QUIRK_LIST); -#if SUPPORT_VLB_SYNC /* * Some localbus EIDE interfaces require a special access sequence * when using 32-bit I/O instructions to transfer data. We call this @@ -233,7 +253,6 @@ void ata_vlb_sync (ide_drive_t *drive, ide_ioreg_t port) } EXPORT_SYMBOL(ata_vlb_sync); -#endif /* SUPPORT_VLB_SYNC */ /* * This is used for most PIO data transfers *from* the IDE interface @@ -244,7 +263,6 @@ void ata_input_data (ide_drive_t *drive, void *buffer, u32 wcount) u8 io_32bit = drive->io_32bit; if (io_32bit) { -#if SUPPORT_VLB_SYNC if (io_32bit & 2) { unsigned long flags; local_irq_save(flags); @@ -252,19 +270,9 @@ void ata_input_data (ide_drive_t *drive, void *buffer, u32 wcount) hwif->INSL(IDE_DATA_REG, buffer, wcount); local_irq_restore(flags); } else -#endif /* SUPPORT_VLB_SYNC */ hwif->INSL(IDE_DATA_REG, buffer, wcount); } else { -#if SUPPORT_SLOW_DATA_PORTS - if (drive->slow) { - u16 *ptr = (u16 *) buffer; - while (wcount--) { - *ptr++ = hwif->INWP(IDE_DATA_REG); - *ptr++ = hwif->INWP(IDE_DATA_REG); - } - } else -#endif /* SUPPORT_SLOW_DATA_PORTS */ - hwif->INSW(IDE_DATA_REG, buffer, wcount<<1); + hwif->INSW(IDE_DATA_REG, buffer, wcount<<1); } } @@ -279,7 +287,6 @@ void ata_output_data (ide_drive_t *drive, void *buffer, u32 wcount) u8 io_32bit = drive->io_32bit; if (io_32bit) { -#if SUPPORT_VLB_SYNC if (io_32bit & 2) { unsigned long flags; local_irq_save(flags); @@ -287,19 +294,9 @@ void ata_output_data (ide_drive_t *drive, void *buffer, u32 wcount) hwif->OUTSL(IDE_DATA_REG, buffer, wcount); local_irq_restore(flags); } else -#endif /* SUPPORT_VLB_SYNC */ hwif->OUTSL(IDE_DATA_REG, buffer, wcount); } else { -#if SUPPORT_SLOW_DATA_PORTS - if (drive->slow) { - u16 *ptr = (u16 *) buffer; - while (wcount--) { - hwif->OUTWP(*ptr++, IDE_DATA_REG); - hwif->OUTWP(*ptr++, IDE_DATA_REG); - } - } else -#endif /* SUPPORT_SLOW_DATA_PORTS */ - hwif->OUTSW(IDE_DATA_REG, buffer, wcount<<1); + hwif->OUTSW(IDE_DATA_REG, buffer, wcount<<1); } } @@ -312,6 +309,7 @@ EXPORT_SYMBOL(ata_output_data); * so if an odd bytecount is specified, be sure that there's at least one * extra byte allocated for the buffer. */ + void atapi_input_bytes (ide_drive_t *drive, void *buffer, u32 bytecount) { ide_hwif_t *hwif = HWIF(drive); diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index f70e0f363410..acc3d795340e 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -1945,24 +1945,14 @@ void ide_unregister (unsigned int index) hwif->OUTB = old_hwif.OUTB; hwif->OUTW = old_hwif.OUTW; hwif->OUTL = old_hwif.OUTL; - hwif->OUTBP = old_hwif.OUTBP; - hwif->OUTWP = old_hwif.OUTWP; - hwif->OUTLP = old_hwif.OUTLP; hwif->OUTSW = old_hwif.OUTSW; - hwif->OUTSWP = old_hwif.OUTSWP; hwif->OUTSL = old_hwif.OUTSL; - hwif->OUTSLP = old_hwif.OUTSLP; hwif->INB = old_hwif.INB; hwif->INW = old_hwif.INW; hwif->INL = old_hwif.INL; - hwif->INBP = old_hwif.INBP; - hwif->INWP = old_hwif.INWP; - hwif->INLP = old_hwif.INLP; hwif->INSW = old_hwif.INSW; - hwif->INSWP = old_hwif.INSWP; hwif->INSL = old_hwif.INSL; - hwif->INSLP = old_hwif.INSLP; #endif hwif->mmio = old_hwif.mmio; diff --git a/include/linux/ide.h b/include/linux/ide.h index 74fff672eaef..7b8ebfe2c8dd 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -302,24 +302,14 @@ typedef struct ide_io_ops_s { void (*OUTB)(u8 addr, u32 port); void (*OUTW)(u16 addr, u32 port); void (*OUTL)(u32 addr, u32 port); - void (*OUTBP)(u8 addr, u32 port); - void (*OUTWP)(u16 addr, u32 port); - void (*OUTLP)(u32 addr, u32 port); void (*OUTSW)(u32 port, void *addr, u32 count); - void (*OUTSWP)(u32 port, void *addr, u32 count); void (*OUTSL)(u32 port, void *addr, u32 count); - void (*OUTSLP)(u32 port, void *addr, u32 count); u8 (*INB)(u32 port); u16 (*INW)(u32 port); u32 (*INL)(u32 port); - u8 (*INBP)(u32 port); - u16 (*INWP)(u32 port); - u32 (*INLP)(u32 port); void (*INSW)(u32 port, void *addr, u32 count); - void (*INSWP)(u32 port, void *addr, u32 count); void (*INSL)(u32 port, void *addr, u32 count); - void (*INSLP)(u32 port, void *addr, u32 count); } ide_io_ops_t; /* @@ -374,41 +364,6 @@ extern int ide_irq_lock; # define ide_get_lock(lock, hdlr, data) do {} while (0) #endif /* IDE_ARCH_LOCK */ -/* - * If the arch-dependant ide.h did not declare/define any OUT_BYTE - * or IN_BYTE functions, we make some defaults here. - */ - -#ifndef HAVE_ARCH_OUT_BYTE -# ifdef REALLY_FAST_IO -# define OUT_BYTE(b,p) outb((b),(p)) -# define OUT_WORD(w,p) outw((w),(p)) -# define OUT_LONG(l,p) outl((l),(p)) -# else -# define OUT_BYTE(b,p) outb_p((b),(p)) -# define OUT_WORD(w,p) outw_p((w),(p)) -# define OUT_LONG(l,p) outl_p((l),(p)) -# endif -# define OUT_BYTE_P(b,p) outb_p((b),(p)) -# define OUT_WORD_P(w,p) outw_p((w),(p)) -# define OUT_LONG_P(l,p) outl_p((l),(p)) -#endif - -#ifndef HAVE_ARCH_IN_BYTE -# ifdef REALLY_FAST_IO -# define IN_BYTE(p) (u8) inb(p) -# define IN_WORD(p) (u16) inw(p) -# define IN_LONG(p) (u32) inl(p) -# else -# define IN_BYTE(p) (u8) inb_p(p) -# define IN_WORD(p) (u16) inw_p(p) -# define IN_LONG(p) (u32) inl_p(p) -# endif -# define IN_BYTE_P(p) (u8) inb_p(p) -# define IN_WORD_P(p) (u16) inw_p(p) -# define IN_LONG_P(p) (u32) inl_p(p) -#endif - /* * Now for the data we need to maintain per-drive: ide_drive_t */ @@ -1011,24 +966,14 @@ typedef struct hwif_s { void (*OUTB)(u8 addr, u32 port); void (*OUTW)(u16 addr, u32 port); void (*OUTL)(u32 addr, u32 port); - void (*OUTBP)(u8 addr, u32 port); - void (*OUTWP)(u16 addr, u32 port); - void (*OUTLP)(u32 addr, u32 port); void (*OUTSW)(u32 port, void *addr, u32 count); - void (*OUTSWP)(u32 port, void *addr, u32 count); void (*OUTSL)(u32 port, void *addr, u32 count); - void (*OUTSLP)(u32 port, void *addr, u32 count); u8 (*INB)(u32 port); u16 (*INW)(u32 port); u32 (*INL)(u32 port); - u8 (*INBP)(u32 port); - u16 (*INWP)(u32 port); - u32 (*INLP)(u32 port); void (*INSW)(u32 port, void *addr, u32 count); - void (*INSWP)(u32 port, void *addr, u32 count); void (*INSL)(u32 port, void *addr, u32 count); - void (*INSLP)(u32 port, void *addr, u32 count); #endif /* dma physical region descriptor table (cpu view) */ -- cgit v1.2.3 From 39ae1835eb59ce0a69cc7994e00a43655cfc47d6 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 3 Oct 2002 20:44:29 -0700 Subject: [PATCH] pass elevator type by reference, not value Ingo spotted this one too, it's a leftover from when the elevator type wasn't a variable. Also don't pass in &q->elevator, it can always be deduced from queue itself of course. --- drivers/block/elevator.c | 10 +++++++--- drivers/block/ll_rw_blk.c | 4 ++-- drivers/s390/block/dasd.c | 6 ++---- include/linux/elevator.h | 4 ++-- 4 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 9237eec87ba4..0b1517f93501 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -217,9 +217,11 @@ struct request *elevator_noop_next_request(request_queue_t *q) /* * general block -> elevator interface starts here */ -int elevator_init(request_queue_t *q, elevator_t *e, elevator_t type) +int elevator_init(request_queue_t *q, elevator_t *type) { - *e = type; + elevator_t *e = &q->elevator; + + memcpy(e, type, sizeof(*e)); INIT_LIST_HEAD(&q->queue_head); q->last_merge = NULL; @@ -230,8 +232,10 @@ int elevator_init(request_queue_t *q, elevator_t *e, elevator_t type) return 0; } -void elevator_exit(request_queue_t *q, elevator_t *e) +void elevator_exit(request_queue_t *q) { + elevator_t *e = &q->elevator; + if (e->elevator_exit_fn) e->elevator_exit_fn(q, e); } diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 01720fdccb1c..e5a0f98423d2 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -1145,7 +1145,7 @@ void blk_cleanup_queue(request_queue_t * q) if (blk_queue_tagged(q)) blk_queue_free_tags(q); - elevator_exit(q, &q->elevator); + elevator_exit(q); memset(q, 0, sizeof(*q)); } @@ -1227,7 +1227,7 @@ int blk_init_queue(request_queue_t *q, request_fn_proc *rfn, spinlock_t *lock) if (blk_init_free_list(q)) return -ENOMEM; - if ((ret = elevator_init(q, &q->elevator, iosched_deadline))) { + if ((ret = elevator_init(q, &iosched_deadline))) { blk_cleanup_queue(q); return ret; } diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index e9278eec247e..6ae8a0053b0a 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -2046,10 +2046,8 @@ dasd_setup_blkdev(dasd_device_t * device) &device->request_queue_lock); if (rc) return rc; - elevator_exit(device->request_queue, &device->request_queue->elevator); - rc = elevator_init(device->request_queue, - &device->request_queue->elevator, - elevator_noop); + elevator_exit(device->request_queue); + rc = elevator_init(device->request_queue, &elevator_noop); if (rc) { blk_cleanup_queue(device->request_queue); return rc; diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 5c6c7db6e97e..8a54a1c5e5af 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -73,8 +73,8 @@ typedef struct blkelv_ioctl_arg_s { #define BLKELVGET _IOR(0x12,106,sizeof(blkelv_ioctl_arg_t)) #define BLKELVSET _IOW(0x12,107,sizeof(blkelv_ioctl_arg_t)) -extern int elevator_init(request_queue_t *, elevator_t *, elevator_t); -extern void elevator_exit(request_queue_t *, elevator_t *); +extern int elevator_init(request_queue_t *, elevator_t *); +extern void elevator_exit(request_queue_t *); extern inline int bio_rq_in_between(struct bio *, struct request *, struct list_head *); extern inline int elv_rq_merge_ok(struct request *, struct bio *); extern inline int elv_try_merge(struct request *, struct bio *); -- cgit v1.2.3 From fcde38d442b6940b8094634835b962c1d8b8a3a7 Mon Sep 17 00:00:00 2001 From: Maxim Krasnyansky Date: Thu, 3 Oct 2002 21:34:13 -0700 Subject: Sync up Bluetooth core with 2.4.x. SMP locking fixes. Support for Hotplug. Support for L2CAP connectionless channels (SOCK_DGRAM). HCI filter handling fixes. Other minor fixes and cleanups. --- include/net/bluetooth/bluetooth.h | 11 +- include/net/bluetooth/hci.h | 36 ++--- include/net/bluetooth/hci_core.h | 13 +- net/bluetooth/af_bluetooth.c | 15 +- net/bluetooth/hci_conn.c | 9 +- net/bluetooth/hci_core.c | 111 ++++++++----- net/bluetooth/hci_event.c | 13 +- net/bluetooth/hci_sock.c | 59 ++++--- net/bluetooth/l2cap.c | 320 +++++++++++++++++++++++--------------- net/bluetooth/lib.c | 2 +- net/bluetooth/sco.c | 130 ++++++++-------- 11 files changed, 411 insertions(+), 308 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index baa076bc2cbb..fd561cca76db 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -50,6 +50,7 @@ #define BTPROTO_HCI 1 #define BTPROTO_SCO 2 #define BTPROTO_RFCOMM 3 +#define BTPROTO_BNEP 4 #define SOL_HCI 0 #define SOL_L2CAP 6 @@ -199,14 +200,4 @@ int hci_sock_cleanup(void); int bterr(__u16 code); -#ifndef MODULE_LICENSE -#define MODULE_LICENSE(x) -#endif - -#ifndef list_for_each_safe -#define list_for_each_safe(pos, n, head) \ - for (pos = (head)->next, n = pos->next; pos != (head); \ - pos = n, n = pos->next) -#endif - #endif /* __BLUETOOTH_H */ diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3fa7b3032c88..b58ebef5397b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -113,10 +113,10 @@ enum { #define ACL_PTYPE_MASK (~SCO_PTYPE_MASK) /* ACL flags */ -#define ACL_CONT 0x0001 -#define ACL_START 0x0002 -#define ACL_ACTIVE_BCAST 0x0010 -#define ACL_PICO_BCAST 0x0020 +#define ACL_CONT 0x01 +#define ACL_START 0x02 +#define ACL_ACTIVE_BCAST 0x04 +#define ACL_PICO_BCAST 0x08 /* Baseband links */ #define SCO_LINK 0x00 @@ -542,7 +542,7 @@ typedef struct { bdaddr_t bdaddr; __u8 role; } __attribute__ ((packed)) evt_role_change; -#define EVT_ROLE_CHANGE_SIZE 1 +#define EVT_ROLE_CHANGE_SIZE 8 #define EVT_PIN_CODE_REQ 0x16 typedef struct { @@ -658,9 +658,15 @@ struct sockaddr_hci { #define HCI_DEV_NONE 0xffff struct hci_filter { - __u32 type_mask; - __u32 event_mask[2]; - __u16 opcode; + unsigned long type_mask; + unsigned long event_mask[2]; + __u16 opcode; +}; + +struct hci_ufilter { + __u32 type_mask; + __u32 event_mask[2]; + __u16 opcode; }; #define HCI_FLT_TYPE_BITS 31 @@ -668,20 +674,6 @@ struct hci_filter { #define HCI_FLT_OGF_BITS 63 #define HCI_FLT_OCF_BITS 127 -#if BITS_PER_LONG == 64 -static inline void hci_set_bit(int nr, void *addr) -{ - *((__u32 *) addr + (nr >> 5)) |= ((__u32) 1 << (nr & 31)); -} -static inline int hci_test_bit(int nr, void *addr) -{ - return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); -} -#else -#define hci_set_bit set_bit -#define hci_test_bit test_bit -#endif - /* Ioctl requests structures */ struct hci_dev_stats { __u32 err_rx; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 15cca8727616..1740cde1287d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -149,7 +149,7 @@ struct hci_conn { extern struct hci_proto *hci_proto[]; extern struct list_head hdev_list; -extern spinlock_t hdev_list_lock; +extern rwlock_t hdev_list_lock; /* ----- Inquiry cache ----- */ #define INQUIRY_CACHE_AGE_MAX (HZ*30) // 30 seconds @@ -339,8 +339,8 @@ static inline void hci_sched_tx(struct hci_dev *hdev) /* ----- HCI protocols ----- */ struct hci_proto { char *name; - __u32 id; - __u32 flags; + unsigned int id; + unsigned long flags; void *priv; @@ -450,12 +450,11 @@ struct hci_pinfo { #define HCI_SFLT_MAX_OGF 4 struct hci_sec_filter { - __u32 type_mask; - __u32 event_mask[2]; - __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; + unsigned long type_mask; + unsigned long event_mask[2]; + unsigned long ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; }; - /* ----- HCI requests ----- */ #define HCI_REQ_DONE 0 #define HCI_REQ_PEND 1 diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index e62e6dfdaf09..d08a2f1cd552 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -27,7 +27,7 @@ * * $Id: af_bluetooth.c,v 1.3 2002/04/17 17:37:15 maxk Exp $ */ -#define VERSION "2.0" +#define VERSION "2.2" #include #include @@ -57,7 +57,7 @@ #endif /* Bluetooth sockets */ -#define BLUEZ_MAX_PROTO 4 +#define BLUEZ_MAX_PROTO 5 static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO]; static kmem_cache_t *bluez_sock_cache; @@ -136,18 +136,18 @@ struct sock *bluez_sock_alloc(struct socket *sock, int proto, int pi_size, int p void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk) { - write_lock(&l->lock); + write_lock_bh(&l->lock); sk->next = l->head; l->head = sk; sock_hold(sk); - write_unlock(&l->lock); + write_unlock_bh(&l->lock); } void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk) { struct sock **skp; - write_lock(&l->lock); + write_lock_bh(&l->lock); for (skp = &l->head; *skp; skp = &((*skp)->next)) { if (*skp == sk) { *skp = sk->next; @@ -155,7 +155,7 @@ void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk) break; } } - write_unlock(&l->lock); + write_unlock_bh(&l->lock); } void bluez_accept_enqueue(struct sock *parent, struct sock *sk) @@ -265,6 +265,9 @@ unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table if (sk->state == BT_CLOSED) mask |= POLLHUP; + if (sk->state == BT_CONNECT || sk->state == BT_CONNECT2) + return mask; + if (sock_writeable(sk)) mask |= POLLOUT | POLLWRNORM | POLLWRBAND; else diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 809ac0dc0c24..536402c9581a 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -73,7 +73,7 @@ void hci_acl_connect(struct hci_conn *conn) bacpy(&cp.bdaddr, &conn->dst); if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) && - inquiry_entry_age(ie) > INQUIRY_ENTRY_AGE_MAX) { + inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { cp.pscan_rep_mode = ie->info.pscan_rep_mode; cp.pscan_mode = ie->info.pscan_mode; cp.clock_offset = ie->info.clock_offset | __cpu_to_le16(0x8000); @@ -188,9 +188,6 @@ int hci_conn_del(struct hci_conn *conn) acl->link = NULL; hci_conn_put(acl); } - - /* Unacked frames */ - hdev->sco_cnt += conn->sent; } else { struct hci_conn *sco = conn->link; if (sco) @@ -220,7 +217,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) BT_DBG("%s -> %s", batostr(src), batostr(dst)); - spin_lock_bh(&hdev_list_lock); + read_lock_bh(&hdev_list_lock); list_for_each(p, &hdev_list) { struct hci_dev *d; @@ -248,7 +245,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) if (hdev) hci_dev_hold(hdev); - spin_unlock_bh(&hdev_list_lock); + read_unlock_bh(&hdev_list_lock); return hdev; } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9e715481bac3..f1485ad896bd 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -66,7 +67,7 @@ rwlock_t hci_task_lock = RW_LOCK_UNLOCKED; /* HCI device list */ LIST_HEAD(hdev_list); -spinlock_t hdev_list_lock; +rwlock_t hdev_list_lock = RW_LOCK_UNLOCKED; /* HCI protocols */ #define HCI_MAX_PROTO 2 @@ -75,7 +76,6 @@ struct hci_proto *hci_proto[HCI_MAX_PROTO]; /* HCI notifiers list */ static struct notifier_block *hci_notifier; - /* ---- HCI notifications ---- */ int hci_register_notifier(struct notifier_block *nb) @@ -93,6 +93,32 @@ void hci_notify(struct hci_dev *hdev, int event) notifier_call_chain(&hci_notifier, event, hdev); } +/* ---- HCI hotplug support ---- */ + +#ifdef CONFIG_HOTPLUG + +static int hci_run_hotplug(char *dev, char *action) +{ + char *argv[3], *envp[5], dstr[20], astr[32]; + + sprintf(dstr, "DEVICE=%s", dev); + sprintf(astr, "ACTION=%s", action); + + argv[0] = hotplug_path; + argv[1] = "bluetooth"; + argv[2] = NULL; + + envp[0] = "HOME=/"; + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp[2] = dstr; + envp[3] = astr; + envp[4] = NULL; + + return call_usermodehelper(argv[0], argv, envp); +} +#else +#define hci_run_hotplug(A...) +#endif /* ---- HCI requests ---- */ @@ -270,7 +296,7 @@ struct hci_dev *hci_dev_get(int index) if (index < 0) return NULL; - spin_lock(&hdev_list_lock); + read_lock(&hdev_list_lock); list_for_each(p, &hdev_list) { hdev = list_entry(p, struct hci_dev, list); if (hdev->id == index) { @@ -280,7 +306,7 @@ struct hci_dev *hci_dev_get(int index) } hdev = NULL; done: - spin_unlock(&hdev_list_lock); + read_unlock(&hdev_list_lock); return hdev; } @@ -699,7 +725,7 @@ int hci_get_dev_list(unsigned long arg) return -ENOMEM; dr = dl->dev_req; - spin_lock_bh(&hdev_list_lock); + read_lock_bh(&hdev_list_lock); list_for_each(p, &hdev_list) { struct hci_dev *hdev; hdev = list_entry(p, struct hci_dev, list); @@ -708,7 +734,7 @@ int hci_get_dev_list(unsigned long arg) if (++n >= dev_num) break; } - spin_unlock_bh(&hdev_list_lock); + read_unlock_bh(&hdev_list_lock); dl->dev_num = n; size = n * sizeof(struct hci_dev_req) + sizeof(__u16); @@ -768,7 +794,7 @@ int hci_register_dev(struct hci_dev *hdev) if (!hdev->open || !hdev->close || !hdev->destruct) return -EINVAL; - spin_lock_bh(&hdev_list_lock); + write_lock_bh(&hdev_list_lock); /* Find first available device id */ list_for_each(p, &hdev_list) { @@ -807,11 +833,12 @@ int hci_register_dev(struct hci_dev *hdev) atomic_set(&hdev->promisc, 0); - hci_notify(hdev, HCI_DEV_REG); - MOD_INC_USE_COUNT; - spin_unlock_bh(&hdev_list_lock); + write_unlock_bh(&hdev_list_lock); + + hci_notify(hdev, HCI_DEV_REG); + hci_run_hotplug(hdev->name, "register"); return id; } @@ -821,13 +848,15 @@ int hci_unregister_dev(struct hci_dev *hdev) { BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type); - spin_lock_bh(&hdev_list_lock); + write_lock_bh(&hdev_list_lock); list_del(&hdev->list); - spin_unlock_bh(&hdev_list_lock); + write_unlock_bh(&hdev_list_lock); hci_dev_do_close(hdev); hci_notify(hdev, HCI_DEV_UNREG); + hci_run_hotplug(hdev->name, "unregister"); + hci_dev_put(hdev); MOD_DEC_USE_COUNT; @@ -1103,14 +1132,13 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int { struct conn_hash *h = &hdev->conn_hash; struct hci_conn *conn = NULL; - int num = 0, min = 0xffff; + int num = 0, min = ~0; struct list_head *p; /* We don't have to lock device here. Connections are always * added and removed with TX task disabled. */ list_for_each(p, &h->list) { struct hci_conn *c; - c = list_entry(p, struct hci_conn, list); if (c->type != type || c->state != BT_CONNECTED @@ -1118,14 +1146,15 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int continue; num++; - if (c->sent < min || type == SCO_LINK) { + if (c->sent < min) { min = c->sent; conn = c; } } if (conn) { - int q = hdev->acl_cnt / num; + int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt); + int q = cnt / num; *quote = q ? q : 1; } else *quote = 0; @@ -1134,6 +1163,25 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int return conn; } +static inline void hci_acl_tx_to(struct hci_dev *hdev) +{ + struct conn_hash *h = &hdev->conn_hash; + struct list_head *p; + struct hci_conn *c; + + BT_ERR("%s ACL tx timeout", hdev->name); + + /* Kill stalled connections */ + list_for_each(p, &h->list) { + c = list_entry(p, struct hci_conn, list); + if (c->type == ACL_LINK && c->sent) { + BT_ERR("%s killing stalled ACL connection %s", + hdev->name, batostr(&c->dst)); + hci_acl_disconn(c, 0x13); + } + } +} + static inline void hci_sched_acl(struct hci_dev *hdev) { struct hci_conn *conn; @@ -1142,21 +1190,19 @@ static inline void hci_sched_acl(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > HZ*5) { - BT_ERR("%s ACL tx timeout", hdev->name); - hdev->acl_cnt++; - } - + /* ACL tx timeout must be longer than maximum + * link supervision timeout (40.9 seconds) */ + if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45)) + hci_acl_tx_to(hdev); + while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) { - while (quote && (skb = skb_dequeue(&conn->data_q))) { + while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); - hci_send_frame(skb); hdev->acl_last_tx = jiffies; - conn->sent++; hdev->acl_cnt--; - quote--; + conn->sent++; } } } @@ -1170,15 +1216,14 @@ static inline void hci_sched_sco(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - while ((conn = hci_low_sent(hdev, SCO_LINK, "e))) { - while (quote && (skb = skb_dequeue(&conn->data_q))) { + while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) { + while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); - hci_send_frame(skb); - //conn->sent++; - //hdev->sco_cnt--; - quote--; + conn->sent++; + if (conn->sent == ~0) + conn->sent = 0; } } } @@ -1205,7 +1250,6 @@ static void hci_tx_task(unsigned long arg) read_unlock(&hci_task_lock); } - /* ----- HCI RX task (incomming data proccessing) ----- */ /* ACL data packet */ @@ -1367,9 +1411,6 @@ static void hci_cmd_task(unsigned long arg) int hci_core_init(void) { - /* Init locks */ - spin_lock_init(&hdev_list_lock); - return 0; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a191d622af63..9445e4543416 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -352,15 +352,12 @@ static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status) hci_dev_lock(hdev); acl = conn_hash_lookup_handle(hdev, handle); - if (!acl || !(sco = acl->link)) { - hci_dev_unlock(hdev); - break; - } + if (acl && (sco = acl->link)) { + sco->state = BT_CLOSED; - sco->state = BT_CLOSED; - - hci_proto_connect_cfm(sco, status); - hci_conn_del(sco); + hci_proto_connect_cfm(sco, status); + hci_conn_del(sco); + } hci_dev_unlock(hdev); } diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 682c84c1623f..90a43a804ba6 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -86,7 +86,7 @@ static struct bluez_sock_list hci_sk_list = { /* Send frame to RAW socket */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) { - struct sock * sk; + struct sock *sk; BT_DBG("hdev %p len %d", hdev, skb->len); @@ -105,13 +105,13 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) /* Apply filter */ flt = &hci_pi(sk)->filter; - if (!hci_test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask)) + if (!test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask)) continue; if (skb->pkt_type == HCI_EVENT_PKT) { register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS); - if (!hci_test_bit(evt, &flt->event_mask)) + if (!test_bit(evt, flt->event_mask)) continue; if (flt->opcode && ((evt == EVT_CMD_COMPLETE && @@ -249,7 +249,7 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long a err = hci_sock_bound_ioctl(sk, cmd, arg); release_sock(sk); return err; - }; + } } static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) @@ -393,12 +393,12 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, err = -EPERM; if (skb->pkt_type == HCI_COMMAND_PKT) { - __u16 opcode = __le16_to_cpu(*(__u16 *)skb->data); - __u16 ogf = cmd_opcode_ogf(opcode) - 1; - __u16 ocf = cmd_opcode_ocf(opcode) & HCI_FLT_OCF_BITS; + u16 opcode = __le16_to_cpu(*(__u16 *)skb->data); + u16 ogf = cmd_opcode_ogf(opcode) - 1; + u16 ocf = cmd_opcode_ocf(opcode) & HCI_FLT_OCF_BITS; if (ogf > HCI_SFLT_MAX_OGF || - !hci_test_bit(ocf, &hci_sec_filter.ocf_mask[ogf])) + !test_bit(ocf, hci_sec_filter.ocf_mask[ogf])) goto drop; } else goto drop; @@ -420,8 +420,8 @@ drop: int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len) { + struct hci_ufilter uf = { .opcode = 0 }; struct sock *sk = sock->sk; - struct hci_filter flt = { opcode: 0 }; int err = 0, opt = 0; BT_DBG("sk %p, opt %d", sk, optname); @@ -454,32 +454,40 @@ int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optva break; case HCI_FILTER: - len = MIN(len, sizeof(struct hci_filter)); - if (copy_from_user(&flt, optval, len)) { + len = MIN(len, sizeof(uf)); + if (copy_from_user(&uf, optval, len)) { err = -EFAULT; break; } if (!capable(CAP_NET_RAW)) { - flt.type_mask &= hci_sec_filter.type_mask; - flt.event_mask[0] &= hci_sec_filter.event_mask[0]; - flt.event_mask[1] &= hci_sec_filter.event_mask[1]; + uf.type_mask &= hci_sec_filter.type_mask; + uf.event_mask[0] &= *((u32 *) hci_sec_filter.event_mask + 0); + uf.event_mask[1] &= *((u32 *) hci_sec_filter.event_mask + 1); } + + { + struct hci_filter *f = &hci_pi(sk)->filter; - memcpy(&hci_pi(sk)->filter, &flt, len); - break; + f->type_mask = uf.type_mask; + f->opcode = uf.opcode; + *((u32 *) f->event_mask + 0) = uf.event_mask[0]; + *((u32 *) f->event_mask + 1) = uf.event_mask[0]; + } + break; default: err = -ENOPROTOOPT; break; - }; - + } + release_sock(sk); return err; } int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { + struct hci_ufilter uf; struct sock *sk = sock->sk; int len, opt; @@ -508,15 +516,24 @@ int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optva break; case HCI_FILTER: - len = MIN(len, sizeof(struct hci_filter)); - if (copy_to_user(optval, &hci_pi(sk)->filter, len)) + { + struct hci_filter *f = &hci_pi(sk)->filter; + + uf.type_mask = f->type_mask; + uf.opcode = f->opcode; + uf.event_mask[0] = *((u32 *) f->event_mask + 0); + uf.event_mask[0] = *((u32 *) f->event_mask + 1); + } + + len = MIN(len, sizeof(uf)); + if (copy_to_user(optval, &uf, len)) return -EFAULT; break; default: return -ENOPROTOOPT; break; - }; + } return 0; } diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 015cc3f06c8a..10df9da60f18 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -25,9 +25,9 @@ /* * BlueZ L2CAP core and sockets. * - * $Id: l2cap.c,v 1.8 2002/04/19 00:01:39 maxk Exp $ + * $Id: l2cap.c,v 1.15 2002/09/09 01:14:52 maxk Exp $ */ -#define VERSION "2.0" +#define VERSION "2.1" #include #include @@ -51,6 +51,7 @@ #include #include +#include #include #include @@ -69,7 +70,7 @@ struct bluez_sock_list l2cap_sk_list = { static int l2cap_conn_del(struct hci_conn *conn, int err); -static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent); +static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent); static void l2cap_chan_del(struct sock *sk, int err); static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len); @@ -177,6 +178,14 @@ static int l2cap_conn_del(struct hci_conn *hcon, int err) return 0; } +static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) +{ + struct l2cap_chan_list *l = &conn->chan_list; + write_lock(&l->lock); + __l2cap_chan_add(conn, sk, parent); + write_unlock(&l->lock); +} + int l2cap_connect(struct sock *sk) { bdaddr_t *src = &bluez_sk(sk)->src; @@ -234,32 +243,26 @@ done: } /* -------- Socket interface ---------- */ -static struct sock *__l2cap_get_sock_by_addr(struct sockaddr_l2 *addr) +static struct sock *__l2cap_get_sock_by_addr(__u16 psm, bdaddr_t *src) { - bdaddr_t *src = &addr->l2_bdaddr; - __u16 psm = addr->l2_psm; struct sock *sk; - for (sk = l2cap_sk_list.head; sk; sk = sk->next) { if (l2cap_pi(sk)->psm == psm && - !bacmp(&bluez_sk(sk)->src, src)) + !bacmp(&bluez_sk(sk)->src, src)) break; } - return sk; } -/* Find socket listening on psm and source bdaddr. +/* Find socket with psm and source bdaddr. * Returns closest match. */ -static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm) +static struct sock *__l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src) { struct sock *sk, *sk1 = NULL; - read_lock(&l2cap_sk_list.lock); - for (sk = l2cap_sk_list.head; sk; sk = sk->next) { - if (sk->state != BT_LISTEN) + if (state && sk->state != state) continue; if (l2cap_pi(sk)->psm == psm) { @@ -272,9 +275,19 @@ static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm) sk1 = sk; } } + return sk ? sk : sk1; +} +/* Find socket with given address (psm, src). + * Returns locked socket */ +static inline struct sock *l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src) +{ + struct sock *s; + read_lock(&l2cap_sk_list.lock); + s = __l2cap_get_sock_by_psm(state, psm, src); + if (s) bh_lock_sock(s); read_unlock(&l2cap_sk_list.lock); - return sk ? sk : sk1; + return s; } static void l2cap_sock_destruct(struct sock *sk) @@ -283,7 +296,7 @@ static void l2cap_sock_destruct(struct sock *sk) skb_queue_purge(&sk->receive_queue); skb_queue_purge(&sk->write_queue); - + if (sk->protinfo) kfree(sk->protinfo); @@ -320,8 +333,6 @@ static void l2cap_sock_kill(struct sock *sk) sock_put(sk); } -/* Close socket. - */ static void __l2cap_sock_close(struct sock *sk, int reason) { BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket); @@ -333,6 +344,7 @@ static void __l2cap_sock_close(struct sock *sk, int reason) case BT_CONNECTED: case BT_CONFIG: + case BT_CONNECT2: if (sk->type == SOCK_SEQPACKET) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; l2cap_disconn_req req; @@ -349,7 +361,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason) break; case BT_CONNECT: - case BT_CONNECT2: case BT_DISCONN: l2cap_chan_del(sk, reason); break; @@ -380,7 +391,6 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) if (parent) { sk->type = parent->type; - pi->imtu = l2cap_pi(parent)->imtu; pi->omtu = l2cap_pi(parent)->omtu; pi->link_mode = l2cap_pi(parent)->link_mode; @@ -405,6 +415,8 @@ static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio) sk->destruct = l2cap_sock_destruct; sk->sndtimeo = L2CAP_CONN_TIMEOUT; + + sk->protocol = proto; sk->state = BT_OPEN; l2cap_sock_init_timer(sk); @@ -421,11 +433,12 @@ static int l2cap_sock_create(struct socket *sock, int protocol) BT_DBG("sock %p", sock); - if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_RAW) + sock->state = SS_UNCONNECTED; + + if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; - sock->state = SS_UNCONNECTED; - sock->ops = &l2cap_sock_ops; + sock->ops = &l2cap_sock_ops; sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL); if (!sk) @@ -453,20 +466,16 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ goto done; } - write_lock(&l2cap_sk_list.lock); - - if (la->l2_psm && __l2cap_get_sock_by_addr(la)) { + write_lock_bh(&l2cap_sk_list.lock); + if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) { err = -EADDRINUSE; - goto unlock; + } else { + /* Save source address */ + bacpy(&bluez_sk(sk)->src, &la->l2_bdaddr); + l2cap_pi(sk)->psm = la->l2_psm; + sk->state = BT_BOUND; } - - /* Save source address */ - bacpy(&bluez_sk(sk)->src, &la->l2_bdaddr); - l2cap_pi(sk)->psm = la->l2_psm; - sk->state = BT_BOUND; - -unlock: - write_unlock(&l2cap_sk_list.lock); + write_unlock_bh(&l2cap_sk_list.lock); done: release_sock(sk); @@ -629,7 +638,6 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l bacpy(&la->l2_bdaddr, &bluez_sk(sk)->src); la->l2_psm = l2cap_pi(sk)->psm; - return 0; } @@ -646,6 +654,10 @@ static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; + /* Check outgoing MTU */ + if (len > l2cap_pi(sk)->omtu) + return -EINVAL; + lock_sock(sk); if (sk->state == BT_CONNECTED) @@ -691,7 +703,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch default: err = -ENOPROTOOPT; break; - }; + } release_sock(sk); return err; @@ -743,20 +755,37 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch default: err = -ENOPROTOOPT; break; - }; + } release_sock(sk); return err; } +static int l2cap_sock_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + BT_DBG("sock %p, sk %p", sock, sk); + + if (!sk) return 0; + + l2cap_sock_clear_timer(sk); + + lock_sock(sk); + sk->shutdown = SHUTDOWN_MASK; + __l2cap_sock_close(sk, ECONNRESET); + release_sock(sk); + + return 0; +} + static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; BT_DBG("sock %p, sk %p", sock, sk); - if (!sk) - return 0; + if (!sk) return 0; sock_orphan(sk); l2cap_sock_close(sk); @@ -767,24 +796,20 @@ static int l2cap_sock_release(struct socket *sock) static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid) { struct sock *s; - for (s = l->head; s; s = l2cap_pi(s)->next_c) { if (l2cap_pi(s)->dcid == cid) break; } - return s; } static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid) { struct sock *s; - for (s = l->head; s; s = l2cap_pi(s)->next_c) { if (l2cap_pi(s)->scid == cid) break; } - return s; } @@ -850,10 +875,15 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so l2cap_pi(sk)->conn = conn; if (sk->type == SOCK_SEQPACKET) { - /* Alloc CID for normal socket */ + /* Alloc CID for connection-oriented socket */ l2cap_pi(sk)->scid = l2cap_alloc_cid(l); + } else if (sk->type == SOCK_DGRAM) { + /* Connectionless socket */ + l2cap_pi(sk)->scid = 0x0002; + l2cap_pi(sk)->dcid = 0x0002; + l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; } else { - /* Raw socket can send only signalling messages */ + /* Raw socket can send/recv signalling messages only */ l2cap_pi(sk)->scid = 0x0001; l2cap_pi(sk)->dcid = 0x0001; l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; @@ -865,14 +895,6 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so bluez_accept_enqueue(parent, sk); } -static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) -{ - struct l2cap_chan_list *l = &conn->chan_list; - write_lock(&l->lock); - __l2cap_chan_add(conn, sk, parent); - write_unlock(&l->lock); -} - /* Delete channel. * Must be called on the locked socket. */ static void l2cap_chan_del(struct sock *sk, int err) @@ -984,25 +1006,31 @@ static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff *skb, **frag; - int err, size, count, sent=0; + int err, hlen, count, sent=0; l2cap_hdr *lh; - /* Check outgoing MTU */ - if (len > l2cap_pi(sk)->omtu) - return -EINVAL; - BT_DBG("sk %p len %d", sk, len); /* First fragment (with L2CAP header) */ - count = MIN(conn->mtu - L2CAP_HDR_SIZE, len); - size = L2CAP_HDR_SIZE + count; - if (!(skb = bluez_skb_send_alloc(sk, size, msg->msg_flags & MSG_DONTWAIT, &err))) + if (sk->type == SOCK_DGRAM) + hlen = L2CAP_HDR_SIZE + 2; + else + hlen = L2CAP_HDR_SIZE; + + count = MIN(conn->mtu - hlen, len); + + skb = bluez_skb_send_alloc(sk, hlen + count, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) return err; /* Create L2CAP header */ lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->len = __cpu_to_le16(len); lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid); + lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + + if (sk->type == SOCK_DGRAM) + put_unaligned(l2cap_pi(sk)->psm, (__u16 *) skb_put(skb, 2)); if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { err = -EFAULT; @@ -1315,37 +1343,41 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid); /* Check if we have socket listening on psm */ - if (!(parent = l2cap_get_sock_listen(conn->src, psm))) { + parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src); + if (!parent) { result = L2CAP_CR_BAD_PSM; - goto resp; + goto sendresp; } - write_lock(&list->lock); - bh_lock_sock(parent); - result = L2CAP_CR_NO_MEM; - /* Check if we already have channel with that dcid */ - if (__l2cap_get_chan_by_dcid(list, scid)) - goto unlock; - /* Check for backlog size */ if (parent->ack_backlog > parent->max_ack_backlog) { BT_DBG("backlog full %d", parent->ack_backlog); - goto unlock; + goto response; } - if (!(sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC))) - goto unlock; + sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC); + if (!sk) + goto response; - l2cap_sock_init(sk, parent); + write_lock(&list->lock); + + /* Check if we already have channel with that dcid */ + if (__l2cap_get_chan_by_dcid(list, scid)) { + write_unlock(&list->lock); + sk->zapped = 1; + l2cap_sock_kill(sk); + goto response; + } + hci_conn_hold(conn->hcon); + + l2cap_sock_init(sk, parent); bacpy(&bluez_sk(sk)->src, conn->src); bacpy(&bluez_sk(sk)->dst, conn->dst); l2cap_pi(sk)->psm = psm; l2cap_pi(sk)->dcid = scid; - - hci_conn_hold(conn->hcon); __l2cap_chan_add(conn, sk, parent); dcid = l2cap_pi(sk)->scid; @@ -1360,20 +1392,22 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) { if (!hci_conn_encrypt(conn->hcon)) - goto unlock; + goto done; } else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) { if (!hci_conn_auth(conn->hcon)) - goto unlock; + goto done; } sk->state = BT_CONFIG; result = status = 0; -unlock: - bh_unlock_sock(parent); +done: write_unlock(&list->lock); -resp: +response: + bh_unlock_sock(parent); + +sendresp: rsp.scid = __cpu_to_le16(scid); rsp.dcid = __cpu_to_le16(dcid); rsp.result = __cpu_to_le16(result); @@ -1678,10 +1712,37 @@ done: return 0; } +static inline int l2cap_conless_channel(struct l2cap_conn *conn, __u16 psm, struct sk_buff *skb) +{ + struct sock *sk; + + sk = l2cap_get_sock_by_psm(0, psm, conn->src); + if (!sk) + goto drop; + + BT_DBG("sk %p, len %d", sk, skb->len); + + if (sk->state != BT_BOUND && sk->state != BT_CONNECTED) + goto drop; + + if (l2cap_pi(sk)->imtu < skb->len) + goto drop; + + if (!sock_queue_rcv_skb(sk, skb)) + goto done; + +drop: + kfree_skb(skb); + +done: + if (sk) bh_unlock_sock(sk); + return 0; +} + static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) { l2cap_hdr *lh = (l2cap_hdr *) skb->data; - __u16 cid, len; + __u16 cid, psm, len; skb_pull(skb, L2CAP_HDR_SIZE); cid = __le16_to_cpu(lh->cid); @@ -1689,10 +1750,21 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("len %d, cid 0x%4.4x", len, cid); - if (cid == 0x0001) + switch (cid) { + case 0x0001: l2cap_sig_channel(conn, skb); - else + break; + + case 0x0002: + psm = get_unaligned((__u16 *) skb->data); + skb_pull(skb, 2); + l2cap_conless_channel(conn, psm, skb); + break; + + default: l2cap_data_channel(conn, cid, skb); + break; + } } /* ------------ L2CAP interface with lower layer (HCI) ------------- */ @@ -1859,8 +1931,8 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16 BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); if (flags & ACL_START) { - int flen, tlen, size; - l2cap_hdr *lh; + l2cap_hdr *hdr; + int len; if (conn->rx_len) { BT_ERR("Unexpected start frame (len %d)", skb->len); @@ -1869,30 +1941,28 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16 conn->rx_len = 0; } - if (skb->len < L2CAP_HDR_SIZE) { + if (skb->len < 2) { BT_ERR("Frame is too small (len %d)", skb->len); goto drop; } - lh = (l2cap_hdr *)skb->data; - tlen = __le16_to_cpu(lh->len); - flen = skb->len - L2CAP_HDR_SIZE; + hdr = (l2cap_hdr *) skb->data; + len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE; - BT_DBG("Start: total len %d, frag len %d", tlen, flen); + BT_DBG("Start: total len %d, frag len %d", len, skb->len); - if (flen == tlen) { + if (len == skb->len) { /* Complete frame received */ l2cap_recv_frame(conn, skb); return 0; } /* Allocate skb for the complete frame (with header) */ - size = L2CAP_HDR_SIZE + tlen; - if (!(conn->rx_skb = bluez_skb_alloc(size, GFP_ATOMIC))) + if (!(conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC))) goto drop; memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len); - conn->rx_len = tlen - flen; + conn->rx_len = len - skb->len; } else { BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); @@ -1932,7 +2002,7 @@ static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list) struct sock *sk; char *ptr = buf; - write_lock(&list->lock); + read_lock_bh(&list->lock); for (sk = list->head; sk; sk = sk->next) { pi = l2cap_pi(sk); @@ -1942,7 +2012,7 @@ static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list) pi->link_mode); } - write_unlock(&list->lock); + read_unlock_bh(&list->lock); ptr += sprintf(ptr, "\n"); return ptr - buf; @@ -1973,38 +2043,38 @@ static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int } static struct proto_ops l2cap_sock_ops = { - .family = PF_BLUETOOTH, - .release = l2cap_sock_release, - .bind = l2cap_sock_bind, - .connect = l2cap_sock_connect, - .listen = l2cap_sock_listen, - .accept = l2cap_sock_accept, - .getname = l2cap_sock_getname, - .sendmsg = l2cap_sock_sendmsg, - .recvmsg = bluez_sock_recvmsg, - .poll = bluez_sock_poll, - .socketpair = sock_no_socketpair, - .ioctl = sock_no_ioctl, - .shutdown = sock_no_shutdown, - .setsockopt = l2cap_sock_setsockopt, - .getsockopt = l2cap_sock_getsockopt, - .mmap = sock_no_mmap + .family = PF_BLUETOOTH, + .release = l2cap_sock_release, + .bind = l2cap_sock_bind, + .connect = l2cap_sock_connect, + .listen = l2cap_sock_listen, + .accept = l2cap_sock_accept, + .getname = l2cap_sock_getname, + .sendmsg = l2cap_sock_sendmsg, + .recvmsg = bluez_sock_recvmsg, + .poll = bluez_sock_poll, + .mmap = sock_no_mmap, + .socketpair = sock_no_socketpair, + .ioctl = sock_no_ioctl, + .shutdown = l2cap_sock_shutdown, + .setsockopt = l2cap_sock_setsockopt, + .getsockopt = l2cap_sock_getsockopt }; static struct net_proto_family l2cap_sock_family_ops = { - .family = PF_BLUETOOTH, - .create = l2cap_sock_create + .family = PF_BLUETOOTH, + .create = l2cap_sock_create }; static struct hci_proto l2cap_hci_proto = { - .name = "L2CAP", - .id = HCI_PROTO_L2CAP, - .connect_ind = l2cap_connect_ind, - .connect_cfm = l2cap_connect_cfm, - .disconn_ind = l2cap_disconn_ind, - .recv_acldata = l2cap_recv_acldata, - .auth_cfm = l2cap_auth_cfm, - .encrypt_cfm = l2cap_encrypt_cfm + .name = "L2CAP", + .id = HCI_PROTO_L2CAP, + .connect_ind = l2cap_connect_ind, + .connect_cfm = l2cap_connect_cfm, + .disconn_ind = l2cap_disconn_ind, + .auth_cfm = l2cap_auth_cfm, + .encrypt_cfm = l2cap_encrypt_cfm, + .recv_acldata = l2cap_recv_acldata }; int __init l2cap_init(void) diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index 44b2bb2771e7..3fbcbd646d9f 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -105,7 +105,7 @@ int bterr(__u16 code) return EACCES; case 0x06: - return EINVAL; + return EBADE; case 0x07: return ENOMEM; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index eb0ef4a11766..129e77012728 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -27,7 +27,7 @@ * * $Id: sco.c,v 1.3 2002/04/17 17:37:16 maxk Exp $ */ -#define VERSION "0.2" +#define VERSION "0.3" #include #include @@ -67,16 +67,15 @@ static struct bluez_sock_list sco_sk_list = { .lock = RW_LOCK_UNLOCKED }; -static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); +static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); static void sco_chan_del(struct sock *sk, int err); -static inline struct sock * sco_chan_get(struct sco_conn *conn); static int sco_conn_del(struct hci_conn *conn, int err); static void sco_sock_close(struct sock *sk); static void sco_sock_kill(struct sock *sk); -/* ----- SCO timers ------ */ +/* ---- SCO timers ---- */ static void sco_sock_timeout(unsigned long arg) { struct sock *sk = (struct sock *) arg; @@ -115,7 +114,7 @@ static void sco_sock_init_timer(struct sock *sk) sk->timer.data = (unsigned long)sk; } -/* -------- SCO connections --------- */ +/* ---- SCO connections ---- */ static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status) { struct hci_dev *hdev = hcon->hdev; @@ -150,6 +149,15 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status) return conn; } +static inline struct sock *sco_chan_get(struct sco_conn *conn) +{ + struct sock *sk = NULL; + sco_conn_lock(conn); + sk = conn->sk; + sco_conn_unlock(conn); + return sk; +} + static int sco_conn_del(struct hci_conn *hcon, int err) { struct sco_conn *conn; @@ -176,6 +184,20 @@ static int sco_conn_del(struct hci_conn *hcon, int err) return 0; } +static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) +{ + int err = 0; + + sco_conn_lock(conn); + if (conn->sk) { + err = -EBUSY; + } else { + __sco_chan_add(conn, sk, parent); + } + sco_conn_unlock(conn); + return err; +} + int sco_connect(struct sock *sk) { bdaddr_t *src = &bluez_sk(sk)->src; @@ -462,23 +484,20 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le goto done; } - write_lock(&sco_sk_list.lock); - + write_lock_bh(&sco_sk_list.lock); + if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) { err = -EADDRINUSE; - goto unlock; + } else { + /* Save source address */ + bacpy(&bluez_sk(sk)->src, &sa->sco_bdaddr); + sk->state = BT_BOUND; } - - /* Save source address */ - bacpy(&bluez_sk(sk)->src, &sa->sco_bdaddr); - sk->state = BT_BOUND; - -unlock: - write_unlock(&sco_sk_list.lock); + + write_unlock_bh(&sco_sk_list.lock); done: release_sock(sk); - return err; } @@ -649,7 +668,7 @@ int sco_sock_setsockopt(struct socket *sock, int level, int optname, char *optva default: err = -ENOPROTOOPT; break; - }; + } release_sock(sk); return err; @@ -703,7 +722,7 @@ int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optva default: err = -ENOPROTOOPT; break; - }; + } release_sock(sk); return err; @@ -735,29 +754,6 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock * bluez_accept_enqueue(parent, sk); } -static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) -{ - int err = 0; - - sco_conn_lock(conn); - if (conn->sk) { - err = -EBUSY; - } else { - __sco_chan_add(conn, sk, parent); - } - sco_conn_unlock(conn); - return err; -} - -static inline struct sock * sco_chan_get(struct sco_conn *conn) -{ - struct sock *sk = NULL; - sco_conn_lock(conn); - sk = conn->sk; - sco_conn_unlock(conn); - return sk; -} - /* Delete channel. * Must be called on the locked socket. */ static void sco_chan_del(struct sock *sk, int err) @@ -895,7 +891,7 @@ static int sco_sock_dump(char *buf, struct bluez_sock_list *list) struct sock *sk; char *ptr = buf; - write_lock(&list->lock); + read_lock_bh(&list->lock); for (sk = list->head; sk; sk = sk->next) { pi = sco_pi(sk); @@ -904,7 +900,7 @@ static int sco_sock_dump(char *buf, struct bluez_sock_list *list) sk->state); } - write_unlock(&list->lock); + read_unlock_bh(&list->lock); ptr += sprintf(ptr, "\n"); @@ -936,36 +932,36 @@ static int sco_read_proc(char *buf, char **start, off_t offset, int count, int * } static struct proto_ops sco_sock_ops = { - .family = PF_BLUETOOTH, - .release = sco_sock_release, - .bind = sco_sock_bind, - .connect = sco_sock_connect, - .listen = sco_sock_listen, - .accept = sco_sock_accept, - .getname = sco_sock_getname, - .sendmsg = sco_sock_sendmsg, - .recvmsg = bluez_sock_recvmsg, - .poll = bluez_sock_poll, - .socketpair = sock_no_socketpair, - .ioctl = sock_no_ioctl, - .shutdown = sock_no_shutdown, - .setsockopt = sco_sock_setsockopt, - .getsockopt = sco_sock_getsockopt, - .mmap = sock_no_mmap + .family = PF_BLUETOOTH, + .release = sco_sock_release, + .bind = sco_sock_bind, + .connect = sco_sock_connect, + .listen = sco_sock_listen, + .accept = sco_sock_accept, + .getname = sco_sock_getname, + .sendmsg = sco_sock_sendmsg, + .recvmsg = bluez_sock_recvmsg, + .poll = bluez_sock_poll, + .ioctl = sock_no_ioctl, + .mmap = sock_no_mmap, + .socketpair = sock_no_socketpair, + .shutdown = sock_no_shutdown, + .setsockopt = sco_sock_setsockopt, + .getsockopt = sco_sock_getsockopt }; static struct net_proto_family sco_sock_family_ops = { - .family = PF_BLUETOOTH, - .create = sco_sock_create + .family = PF_BLUETOOTH, + .create = sco_sock_create }; static struct hci_proto sco_hci_proto = { - .name = "SCO", - .id = HCI_PROTO_SCO, - .connect_ind = sco_connect_ind, - .connect_cfm = sco_connect_cfm, - .disconn_ind = sco_disconn_ind, - .recv_scodata = sco_recv_scodata, + .name = "SCO", + .id = HCI_PROTO_SCO, + .connect_ind = sco_connect_ind, + .connect_cfm = sco_connect_cfm, + .disconn_ind = sco_disconn_ind, + .recv_scodata = sco_recv_scodata }; int __init sco_init(void) -- cgit v1.2.3 From 3cefba2a9ea8d8134a3007fa18c9adeed8cc3687 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 3 Oct 2002 21:44:57 -0700 Subject: [PATCH] s390 update (2/27): include. s390 include file changes for 2.5.39. --- include/asm-s390/hardirq.h | 105 ++++++++++++++++++++++------------------- include/asm-s390/irq.h | 61 ------------------------ include/asm-s390/kmap_types.h | 21 +++++++++ include/asm-s390/param.h | 10 ++-- include/asm-s390/pgalloc.h | 2 + include/asm-s390/pgtable.h | 2 + include/asm-s390/rwsem.h | 68 +++++++++++++++++++++++++- include/asm-s390/smp.h | 17 +++++-- include/asm-s390/softirq.h | 29 ++++-------- include/asm-s390/spinlock.h | 2 + include/asm-s390/system.h | 94 +++++++++++++++++++++++++++++------- include/asm-s390/tlbflush.h | 3 +- include/asm-s390x/bitops.h | 9 +++- include/asm-s390x/hardirq.h | 102 +++++++++++++++++++++------------------ include/asm-s390x/irq.h | 61 ------------------------ include/asm-s390x/kmap_types.h | 21 +++++++++ include/asm-s390x/param.h | 13 +++-- include/asm-s390x/pgalloc.h | 2 + include/asm-s390x/pgtable.h | 2 + include/asm-s390x/rwsem.h | 68 +++++++++++++++++++++++++- include/asm-s390x/smp.h | 17 +++++-- include/asm-s390x/softirq.h | 31 +++++------- include/asm-s390x/spinlock.h | 9 ++++ include/asm-s390x/system.h | 102 ++++++++++++++++++++++++++++++++------- include/asm-s390x/tlbflush.h | 3 +- 25 files changed, 537 insertions(+), 317 deletions(-) create mode 100644 include/asm-s390/kmap_types.h create mode 100644 include/asm-s390x/kmap_types.h (limited to 'include') diff --git a/include/asm-s390/hardirq.h b/include/asm-s390/hardirq.h index d7b47844d7c2..a8b241ec8e9e 100644 --- a/include/asm-s390/hardirq.h +++ b/include/asm-s390/hardirq.h @@ -14,15 +14,13 @@ #include #include -#include #include #include +#include /* entry.S is sensitive to the offsets of these fields */ typedef struct { unsigned int __softirq_pending; - unsigned int __local_irq_count; - unsigned int __local_bh_count; unsigned int __syscall_count; struct task_struct * __ksoftirqd_task; /* waitqueue is too large */ } ____cacheline_aligned irq_cpustat_t; @@ -30,64 +28,75 @@ typedef struct { #include /* Standard mappings for irq_cpustat_t above */ /* - * Are we in an interrupt context? Either doing bottom half - * or hardware interrupt processing? + * 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: 0x00010000 */ -#define in_interrupt() ({ int __cpu = smp_processor_id(); \ - (local_irq_count(__cpu) + local_bh_count(__cpu) != 0); }) -#define in_irq() (local_irq_count(smp_processor_id()) != 0) - -#ifndef CONFIG_SMP +#define PREEMPT_BITS 8 +#define SOFTIRQ_BITS 8 +#define HARDIRQ_BITS 1 -#define hardirq_trylock(cpu) (local_irq_count(cpu) == 0) -#define hardirq_endlock(cpu) do { } while (0) - -#define hardirq_enter(cpu) (local_irq_count(cpu)++) -#define hardirq_exit(cpu) (local_irq_count(cpu)--) +#define PREEMPT_SHIFT 0 +#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) +#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define synchronize_irq() do { } while (0) +#define __MASK(x) ((1UL << (x))-1) -#else /* CONFIG_SMP */ +#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) +#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) +#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#include -#include +#define hardirq_count() (preempt_count() & HARDIRQ_MASK) +#define softirq_count() (preempt_count() & SOFTIRQ_MASK) +#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) -extern atomic_t global_irq_holder; -extern atomic_t global_irq_lock; -extern atomic_t global_irq_count; +#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()) -static inline void release_irqlock(int cpu) -{ - /* if we didn't own the irq lock, just ignore.. */ - if (atomic_read(&global_irq_holder) == cpu) { - atomic_set(&global_irq_holder,NO_PROC_ID); - atomic_set(&global_irq_lock,0); - } -} -static inline void hardirq_enter(int cpu) -{ - ++local_irq_count(cpu); - atomic_inc(&global_irq_count); -} +#define hardirq_trylock() (!in_interrupt()) +#define hardirq_endlock() do { } while (0) -static inline void hardirq_exit(int cpu) -{ - atomic_dec(&global_irq_count); - --local_irq_count(cpu); -} +#define irq_enter() (preempt_count() += HARDIRQ_OFFSET) -static inline int hardirq_trylock(int cpu) -{ - return !atomic_read(&global_irq_count) && - !atomic_read(&global_irq_lock); -} +extern void do_call_softirq(void); -#define hardirq_endlock(cpu) do { } while (0) +#define in_atomic() (preempt_count() != 0) +#define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -extern void synchronize_irq(void); +#define irq_exit() \ +do { \ + preempt_count() -= HARDIRQ_OFFSET; \ + if (!in_interrupt() && softirq_pending(smp_processor_id())) \ + /* Use the async. stack for softirq */ \ + do_call_softirq(); \ +} while (0) + +#ifndef CONFIG_SMP +# define synchronize_irq(irq) barrier() +#else + extern void synchronize_irq(unsigned int irq); +#endif /* CONFIG_SMP */ -#endif /* CONFIG_SMP */ +extern void show_stack(unsigned long * esp); #endif /* __ASM_HARDIRQ_H */ diff --git a/include/asm-s390/irq.h b/include/asm-s390/irq.h index 8ecfe3324a44..5ccf5013a518 100644 --- a/include/asm-s390/irq.h +++ b/include/asm-s390/irq.h @@ -897,67 +897,6 @@ extern __inline__ int chsc( chsc_area_t * chsc_area) return cc; } -/* - * Various low-level irq details needed by irq.c, process.c, - * time.c, io_apic.c and smp.c - * - * Interrupt entry/exit code at both C and assembly level - */ - -#ifdef CONFIG_SMP - -#include - -static inline void irq_enter(int cpu, unsigned int irq) -{ - hardirq_enter(cpu); - while (atomic_read(&global_irq_lock) != 0) { - eieio(); - } -} - -static inline void irq_exit(int cpu, unsigned int irq) -{ - hardirq_exit(cpu); - release_irqlock(cpu); -} - - -#else - -#define irq_enter(cpu, irq) (++local_irq_count(cpu)) -#define irq_exit(cpu, irq) (--local_irq_count(cpu)) - -#endif - -#define __STR(x) #x -#define STR(x) __STR(x) - -/* - * x86 profiling function, SMP safe. We might want to do this in - * assembly totally? - * is this ever used anyway? - */ -extern char _stext; -static inline void s390_do_profile (unsigned long addr) -{ - if (prof_buffer && current->pid) { -#ifndef CONFIG_ARCH_S390X - addr &= 0x7fffffff; -#endif - addr -= (unsigned long) &_stext; - addr >>= 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 (addr > prof_len-1) - addr = prof_len-1; - atomic_inc((atomic_t *)&prof_buffer[addr]); - } -} - #include #define get_irq_lock(irq) &ioinfo[irq]->irq_lock diff --git a/include/asm-s390/kmap_types.h b/include/asm-s390/kmap_types.h new file mode 100644 index 000000000000..27f3d6c49ad5 --- /dev/null +++ b/include/asm-s390/kmap_types.h @@ -0,0 +1,21 @@ +#ifdef __KERNEL__ +#ifndef _ASM_KMAP_TYPES_H +#define _ASM_KMAP_TYPES_H + +enum km_type { + KM_BOUNCE_READ, + KM_SKB_SUNRPC_DATA, + KM_SKB_DATA_SOFTIRQ, + KM_USER0, + KM_USER1, + KM_BIO_SRC_IRQ, + KM_BIO_DST_IRQ, + KM_PTE0, + KM_PTE1, + KM_IRQ0, + KM_IRQ1, + KM_TYPE_NR +}; + +#endif +#endif /* __KERNEL__ */ diff --git a/include/asm-s390/param.h b/include/asm-s390/param.h index 4c47f9f048f2..753b8bdeecba 100644 --- a/include/asm-s390/param.h +++ b/include/asm-s390/param.h @@ -9,6 +9,12 @@ #ifndef _ASMS390_PARAM_H #define _ASMS390_PARAM_H +#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 @@ -25,8 +31,4 @@ #define MAXHOSTNAMELEN 64 /* max length of hostname */ -#ifdef __KERNEL__ -# define CLOCKS_PER_SEC HZ /* frequency at which times() counts */ -#endif - #endif diff --git a/include/asm-s390/pgalloc.h b/include/asm-s390/pgalloc.h index 9ef3dd733514..5a216e91891b 100644 --- a/include/asm-s390/pgalloc.h +++ b/include/asm-s390/pgalloc.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #define check_pgt_cache() do {} while (0) diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h index b48ab817e095..b11c0deb09c4 100644 --- a/include/asm-s390/pgtable.h +++ b/include/asm-s390/pgtable.h @@ -176,6 +176,8 @@ extern char empty_zero_page[PAGE_SIZE]; #define _SEGMENT_TABLE (_USER_SEG_TABLE_LEN|0x80000000|0x100) #define _KERNSEG_TABLE (_KERNEL_SEG_TABLE_LEN) +#define USER_STD_MASK 0x00000080UL + /* * No mapping available */ diff --git a/include/asm-s390/rwsem.h b/include/asm-s390/rwsem.h index 1072694713be..4345d83de401 100644 --- a/include/asm-s390/rwsem.h +++ b/include/asm-s390/rwsem.h @@ -48,9 +48,11 @@ struct rwsem_waiter; -extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *); +extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *); extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *); +extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *); +extern struct rw_semaphore *rwsem_downgrade_write(struct rw_semaphore *); /* * the semaphore definition @@ -104,6 +106,27 @@ static inline void __down_read(struct rw_semaphore *sem) rwsem_down_read_failed(sem); } +/* + * trylock for reading -- returns 1 if successful, 0 if contention + */ +static inline int __down_read_trylock(struct rw_semaphore *sem) +{ + signed long old, new; + + __asm__ __volatile__( + " l %0,0(%2)\n" + "0: ltr %1,%0\n" + " jm 1f\n" + " ahi %1,%3\n" + " cs %0,%1,0(%2)\n" + " jl 0b\n" + "1:" + : "=&d" (old), "=&d" (new) + : "a" (&sem->count), "i" (RWSEM_ACTIVE_READ_BIAS) + : "cc", "memory" ); + return old >= 0 ? 1 : 0; +} + /* * lock for writing */ @@ -125,6 +148,26 @@ static inline void __down_write(struct rw_semaphore *sem) rwsem_down_write_failed(sem); } +/* + * trylock for writing -- returns 1 if successful, 0 if contention + */ +static inline int __down_write_trylock(struct rw_semaphore *sem) +{ + signed long old; + + __asm__ __volatile__( + " l %0,0(%1)\n" + "0: ltr %0,%0\n" + " jnz 1f\n" + " cs %0,%2,0(%1)\n" + " jl 0b\n" + "1:" + : "=&d" (old) + : "a" (&sem->count), "d" (RWSEM_ACTIVE_WRITE_BIAS) + : "cc", "memory" ); + return (old == RWSEM_UNLOCKED_VALUE) ? 1 : 0; +} + /* * unlock after reading */ @@ -168,6 +211,27 @@ static inline void __up_write(struct rw_semaphore *sem) rwsem_wake(sem); } +/* + * downgrade write lock to read lock + */ +static inline void __downgrade_write(struct rw_semaphore *sem) +{ + signed long old, new, tmp; + + tmp = -RWSEM_WAITING_BIAS; + __asm__ __volatile__( + " l %0,0(%2)\n" + "0: lr %1,%0\n" + " a %1,%3\n" + " cs %0,%1,0(%2)\n" + " jl 0b" + : "=&d" (old), "=&d" (new) + : "a" (&sem->count), "m" (tmp) + : "cc", "memory" ); + if (new > 1) // FIXME: is this correct ?!? + rwsem_downgrade_wake(sem); +} + /* * implement atomic add functionality */ diff --git a/include/asm-s390/smp.h b/include/asm-s390/smp.h index 8caa0b8bdd87..7bd666e535ae 100644 --- a/include/asm-s390/smp.h +++ b/include/asm-s390/smp.h @@ -11,7 +11,7 @@ #include #include -#include +#include #if defined(__KERNEL__) && defined(CONFIG_SMP) && !defined(__ASSEMBLY__) @@ -29,6 +29,7 @@ typedef struct } sigp_info; extern volatile unsigned long cpu_online_map; +extern unsigned long cpu_possible_map; #define NO_PROC_ID 0xFF /* No processor magic marker */ @@ -46,14 +47,20 @@ extern volatile unsigned long cpu_online_map; #define smp_processor_id() (current_thread_info()->cpu) -extern __inline__ int cpu_logical_map(int cpu) +#define cpu_online(cpu) (cpu_online_map & (1<<(cpu))) +#define cpu_possible(cpu) (cpu_possible_map & (1<<(cpu))) + +extern inline unsigned int num_online_cpus(void) { - return cpu; + return hweight32(cpu_online_map); } -extern __inline__ int cpu_number_map(int cpu) +extern inline int any_online_cpu(unsigned int mask) { - return cpu; + if (mask & cpu_online_map) + return __ffs(mask & cpu_online_map); + + return -1; } extern __inline__ __u16 hard_smp_processor_id(void) diff --git a/include/asm-s390/softirq.h b/include/asm-s390/softirq.h index b82aac30db21..25d7b42c426f 100644 --- a/include/asm-s390/softirq.h +++ b/include/asm-s390/softirq.h @@ -9,34 +9,25 @@ #ifndef __ASM_SOFTIRQ_H #define __ASM_SOFTIRQ_H -#ifndef __LINUX_SMP_H #include -#endif #include #include #include -#define __cpu_bh_enable(cpu) \ - do { barrier(); local_bh_count(cpu)--; } while (0) -#define cpu_bh_disable(cpu) \ - do { local_bh_count(cpu)++; barrier(); } while (0) - -#define local_bh_disable() cpu_bh_disable(smp_processor_id()) -#define __local_bh_enable() __cpu_bh_enable(smp_processor_id()) - -#define in_softirq() (local_bh_count(smp_processor_id()) != 0) +#define local_bh_disable() \ + do { preempt_count() += SOFTIRQ_OFFSET; barrier(); } while (0) +#define __local_bh_enable() \ + do { barrier(); preempt_count() -= SOFTIRQ_OFFSET; } while (0) extern void do_call_softirq(void); -#define local_bh_enable() \ -do { \ - unsigned int *ptr = &local_bh_count(smp_processor_id()); \ - barrier(); \ - if (!--*ptr) \ - if (softirq_pending(smp_processor_id())) \ - /* Use the async. stack for softirq */ \ - do_call_softirq(); \ +#define local_bh_enable() \ +do { \ + __local_bh_enable(); \ + if (!in_interrupt() && softirq_pending(smp_processor_id())) \ + /* Use the async. stack for softirq */ \ + do_call_softirq(); \ } while (0) #endif /* __ASM_SOFTIRQ_H */ diff --git a/include/asm-s390/spinlock.h b/include/asm-s390/spinlock.h index 46e77f6fb9f1..176feb128687 100644 --- a/include/asm-s390/spinlock.h +++ b/include/asm-s390/spinlock.h @@ -76,6 +76,8 @@ typedef struct { #define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) +#define rwlock_is_locked(x) ((x)->lock != 0) + #define _raw_read_lock(rw) \ asm volatile(" l 2,0(%1)\n" \ " j 1f\n" \ diff --git a/include/asm-s390/system.h b/include/asm-s390/system.h index 15e1af97a358..2257425edae2 100644 --- a/include/asm-s390/system.h +++ b/include/asm-s390/system.h @@ -101,6 +101,77 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) return x; } +/* + * Atomic compare and exchange. Compare OLD with MEM, if identical, + * store NEW in MEM. Return the initial value in MEM. Success is + * indicated by comparing RETURN with OLD. + */ + +#define __HAVE_ARCH_CMPXCHG 1 + +#define cmpxchg(ptr,o,n)\ + ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ + (unsigned long)(n),sizeof(*(ptr)))) + +static inline unsigned long +__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) +{ + unsigned long addr, prev, tmp; + int shift; + + switch (size) { + case 1: + addr = (unsigned long) ptr; + shift = (3 ^ (addr & 3)) << 3; + addr ^= addr & 3; + asm volatile( + " l %0,0(%4)\n" + "0: nr %0,%5\n" + " lr %1,%0\n" + " or %0,%2\n" + " or %1,%3\n" + " cs %0,%1,0(%4)\n" + " jnl 1f\n" + " xr %1,%0\n" + " nr %1,%5\n" + " jnz 0b\n" + "1:" + : "=&d" (prev), "=&d" (tmp) + : "d" (old << shift), "d" (new << shift), "a" (ptr), + "d" (~(255 << shift)) + : "memory", "cc" ); + return prev >> shift; + case 2: + addr = (unsigned long) ptr; + shift = (2 ^ (addr & 2)) << 3; + addr ^= addr & 2; + asm volatile( + " l %0,0(%4)\n" + "0: nr %0,%5\n" + " lr %1,%0\n" + " or %0,%2\n" + " or %1,%3\n" + " cs %0,%1,0(%4)\n" + " jnl 1f\n" + " xr %1,%0\n" + " nr %1,%5\n" + " jnz 0b\n" + "1:" + : "=&d" (prev), "=&d" (tmp) + : "d" (old << shift), "d" (new << shift), "a" (ptr), + "d" (~(65535 << shift)) + : "memory", "cc" ); + return prev >> shift; + case 4: + asm volatile ( + " cs %0,%2,0(%3)\n" + : "=&d" (prev) : "0" (old), "d" (new), "a" (ptr) + : "memory", "cc" ); + return prev; + } + return old; +} + /* * Force strict CPU ordering. * And yes, this is required on UP too when we're talking @@ -146,6 +217,13 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) #define local_irq_restore(x) \ __asm__ __volatile__("ssm 0(%0)" : : "a" (&x) : "memory") +#define irqs_disabled() \ +({ \ + unsigned long flags; \ + local_save_flags(flags); \ + !((flags >> 24) & 3); \ +}) + #define __load_psw(psw) \ __asm__ __volatile__("lpsw 0(%0)" : : "a" (&psw) : "cc" ); @@ -210,16 +288,6 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) #ifdef CONFIG_SMP -extern void __global_cli(void); -extern void __global_sti(void); - -extern unsigned long __global_save_flags(void); -extern void __global_restore_flags(unsigned long); -#define cli() __global_cli() -#define sti() __global_sti() -#define save_flags(x) ((x)=__global_save_flags()) -#define restore_flags(x) __global_restore_flags(x) - extern void smp_ctl_set_bit(int cr, int bit); extern void smp_ctl_clear_bit(int cr, int bit); #define ctl_set_bit(cr, bit) smp_ctl_set_bit(cr, bit) @@ -227,15 +295,9 @@ extern void smp_ctl_clear_bit(int cr, int bit); #else -#define cli() local_irq_disable() -#define sti() local_irq_enable() -#define save_flags(x) local_save_flags(x) -#define restore_flags(x) local_irq_restore(x) - #define ctl_set_bit(cr, bit) __ctl_set_bit(cr, bit) #define ctl_clear_bit(cr, bit) __ctl_clear_bit(cr, bit) - #endif #ifdef __KERNEL__ diff --git a/include/asm-s390/tlbflush.h b/include/asm-s390/tlbflush.h index 372c70f353f3..f079ea53a2a2 100644 --- a/include/asm-s390/tlbflush.h +++ b/include/asm-s390/tlbflush.h @@ -91,8 +91,7 @@ static inline void global_flush_tlb(void) static inline void __flush_tlb_mm(struct mm_struct * mm) { - if ((smp_num_cpus > 1) && - ((atomic_read(&mm->mm_count) != 1) || + if (((atomic_read(&mm->mm_count) != 1) || (mm->cpu_vm_mask != (1UL << smp_processor_id())))) { mm->cpu_vm_mask = (1UL << smp_processor_id()); global_flush_tlb(); diff --git a/include/asm-s390x/bitops.h b/include/asm-s390x/bitops.h index eaff499377dd..ac889a324bbb 100644 --- a/include/asm-s390x/bitops.h +++ b/include/asm-s390x/bitops.h @@ -811,7 +811,14 @@ extern __inline__ int fls(int x) * hweightN: returns the hamming weight (i.e. the number * of bits set) of a N-bit word */ - +#define hweight64(x) \ +({ \ + unsigned long __x = (x); \ + unsigned int __w; \ + __w = generic_hweight32((unsigned int) __x); \ + __w += generic_hweight32((unsigned int) (__x>>32)); \ + __w; \ +}) #define hweight32(x) generic_hweight32(x) #define hweight16(x) generic_hweight16(x) #define hweight8(x) generic_hweight8(x) diff --git a/include/asm-s390x/hardirq.h b/include/asm-s390x/hardirq.h index 607f34ee659a..8933c4c096e9 100644 --- a/include/asm-s390x/hardirq.h +++ b/include/asm-s390x/hardirq.h @@ -21,8 +21,6 @@ /* entry.S is sensitive to the offsets of these fields */ typedef struct { unsigned int __softirq_pending; - unsigned int __local_irq_count; - unsigned int __local_bh_count; unsigned int __syscall_count; struct task_struct * __ksoftirqd_task; /* waitqueue is too large */ } ____cacheline_aligned irq_cpustat_t; @@ -30,64 +28,76 @@ typedef struct { #include /* Standard mappings for irq_cpustat_t above */ /* - * Are we in an interrupt context? Either doing bottom half - * or hardware interrupt processing? + * 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: 0x00010000 */ -#define in_interrupt() ({ int __cpu = smp_processor_id(); \ - (local_irq_count(__cpu) + local_bh_count(__cpu) != 0); }) -#define in_irq() (local_irq_count(smp_processor_id()) != 0) +#define PREEMPT_BITS 8 +#define SOFTIRQ_BITS 8 +// FIXME: we have 2^16 i/o and 2^16 external interrupts... +#define HARDIRQ_BITS 1 -#ifndef CONFIG_SMP - -#define hardirq_trylock(cpu) (local_irq_count(cpu) == 0) -#define hardirq_endlock(cpu) do { } while (0) - -#define hardirq_enter(cpu) (local_irq_count(cpu)++) -#define hardirq_exit(cpu) (local_irq_count(cpu)--) +#define PREEMPT_SHIFT 0 +#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) +#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define synchronize_irq() do { } while (0) +#define __MASK(x) ((1UL << (x))-1) -#else +#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) +#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) +#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#include -#include +#define hardirq_count() (preempt_count() & HARDIRQ_MASK) +#define softirq_count() (preempt_count() & SOFTIRQ_MASK) +#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) -extern atomic_t global_irq_holder; -extern atomic_t global_irq_lock; -extern atomic_t global_irq_count; +#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()) -static inline void release_irqlock(int cpu) -{ - /* if we didn't own the irq lock, just ignore.. */ - if (atomic_read(&global_irq_holder) == cpu) { - atomic_set(&global_irq_holder,NO_PROC_ID); - atomic_set(&global_irq_lock,0); - } -} -static inline void hardirq_enter(int cpu) -{ - ++local_irq_count(cpu); - atomic_inc(&global_irq_count); -} +#define hardirq_trylock() (!in_interrupt()) +#define hardirq_endlock() do { } while (0) -static inline void hardirq_exit(int cpu) -{ - atomic_dec(&global_irq_count); - --local_irq_count(cpu); -} +#define irq_enter() (preempt_count() += HARDIRQ_OFFSET) -static inline int hardirq_trylock(int cpu) -{ - return !atomic_read(&global_irq_count) && - !atomic_read(&global_irq_lock); -} +extern void do_call_softirq(void); -#define hardirq_endlock(cpu) do { } while (0) +#define in_atomic() (preempt_count() != 0) +#define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -extern void synchronize_irq(void); +#define irq_exit() \ +do { \ + preempt_count() -= HARDIRQ_OFFSET; \ + if (!in_interrupt() && softirq_pending(smp_processor_id())) \ + /* Use the async. stack for softirq */ \ + do_call_softirq(); \ +} while (0) +#ifndef CONFIG_SMP +# define synchronize_irq(irq) barrier() +#else + extern void synchronize_irq(unsigned int irq); #endif /* CONFIG_SMP */ +extern void show_stack(unsigned long * esp); + #endif /* __ASM_HARDIRQ_H */ diff --git a/include/asm-s390x/irq.h b/include/asm-s390x/irq.h index 8ecfe3324a44..5ccf5013a518 100644 --- a/include/asm-s390x/irq.h +++ b/include/asm-s390x/irq.h @@ -897,67 +897,6 @@ extern __inline__ int chsc( chsc_area_t * chsc_area) return cc; } -/* - * Various low-level irq details needed by irq.c, process.c, - * time.c, io_apic.c and smp.c - * - * Interrupt entry/exit code at both C and assembly level - */ - -#ifdef CONFIG_SMP - -#include - -static inline void irq_enter(int cpu, unsigned int irq) -{ - hardirq_enter(cpu); - while (atomic_read(&global_irq_lock) != 0) { - eieio(); - } -} - -static inline void irq_exit(int cpu, unsigned int irq) -{ - hardirq_exit(cpu); - release_irqlock(cpu); -} - - -#else - -#define irq_enter(cpu, irq) (++local_irq_count(cpu)) -#define irq_exit(cpu, irq) (--local_irq_count(cpu)) - -#endif - -#define __STR(x) #x -#define STR(x) __STR(x) - -/* - * x86 profiling function, SMP safe. We might want to do this in - * assembly totally? - * is this ever used anyway? - */ -extern char _stext; -static inline void s390_do_profile (unsigned long addr) -{ - if (prof_buffer && current->pid) { -#ifndef CONFIG_ARCH_S390X - addr &= 0x7fffffff; -#endif - addr -= (unsigned long) &_stext; - addr >>= 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 (addr > prof_len-1) - addr = prof_len-1; - atomic_inc((atomic_t *)&prof_buffer[addr]); - } -} - #include #define get_irq_lock(irq) &ioinfo[irq]->irq_lock diff --git a/include/asm-s390x/kmap_types.h b/include/asm-s390x/kmap_types.h new file mode 100644 index 000000000000..27f3d6c49ad5 --- /dev/null +++ b/include/asm-s390x/kmap_types.h @@ -0,0 +1,21 @@ +#ifdef __KERNEL__ +#ifndef _ASM_KMAP_TYPES_H +#define _ASM_KMAP_TYPES_H + +enum km_type { + KM_BOUNCE_READ, + KM_SKB_SUNRPC_DATA, + KM_SKB_DATA_SOFTIRQ, + KM_USER0, + KM_USER1, + KM_BIO_SRC_IRQ, + KM_BIO_DST_IRQ, + KM_PTE0, + KM_PTE1, + KM_IRQ0, + KM_IRQ1, + KM_TYPE_NR +}; + +#endif +#endif /* __KERNEL__ */ diff --git a/include/asm-s390x/param.h b/include/asm-s390x/param.h index f2e0cc0a4dcc..753b8bdeecba 100644 --- a/include/asm-s390x/param.h +++ b/include/asm-s390x/param.h @@ -9,11 +9,14 @@ #ifndef _ASMS390_PARAM_H #define _ASMS390_PARAM_H -#ifndef HZ -#define HZ 100 #ifdef __KERNEL__ -#define hz_to_std(a) (a) +# 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 @@ -28,8 +31,4 @@ #define MAXHOSTNAMELEN 64 /* max length of hostname */ -#ifdef __KERNEL__ -# define CLOCKS_PER_SEC HZ /* frequency at which times() counts */ -#endif - #endif diff --git a/include/asm-s390x/pgalloc.h b/include/asm-s390x/pgalloc.h index 282ec93f29d4..838eb9ff927e 100644 --- a/include/asm-s390x/pgalloc.h +++ b/include/asm-s390x/pgalloc.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #define check_pgt_cache() do { } while (0) diff --git a/include/asm-s390x/pgtable.h b/include/asm-s390x/pgtable.h index 6c590334ab3a..f84930684b68 100644 --- a/include/asm-s390x/pgtable.h +++ b/include/asm-s390x/pgtable.h @@ -168,6 +168,8 @@ extern char empty_zero_page[PAGE_SIZE]; #define _REGION_TABLE (_REGION_THIRD|_REGION_THIRD_LEN|0x40|0x100) #define _KERN_REGION_TABLE (_REGION_THIRD|_REGION_THIRD_LEN) +#define USER_STD_MASK 0x0000000000000080UL + /* Bits in the storage key */ #define _PAGE_CHANGED 0x02 /* HW changed bit */ #define _PAGE_REFERENCED 0x04 /* HW referenced bit */ diff --git a/include/asm-s390x/rwsem.h b/include/asm-s390x/rwsem.h index 5ee597eadc88..ec635241c20e 100644 --- a/include/asm-s390x/rwsem.h +++ b/include/asm-s390x/rwsem.h @@ -48,9 +48,11 @@ struct rwsem_waiter; -extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); -extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *); +extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *); extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *); +extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *); +extern struct rw_semaphore *rwsem_downgrade_write(struct rw_semaphore *); /* * the semaphore definition @@ -104,6 +106,27 @@ static inline void __down_read(struct rw_semaphore *sem) rwsem_down_read_failed(sem); } +/* + * trylock for reading -- returns 1 if successful, 0 if contention + */ +static inline int __down_read_trylock(struct rw_semaphore *sem) +{ + signed long old, new; + + __asm__ __volatile__( + " lg %0,0(%2)\n" + "0: ltgr %1,%0\n" + " jm 1f\n" + " aghi %1,%3\n" + " csg %0,%1,0(%2)\n" + " jl 0b\n" + "1:" + : "=&d" (old), "=&d" (new) + : "a" (&sem->count), "i" (RWSEM_ACTIVE_READ_BIAS) + : "cc", "memory" ); + return old >= 0 ? 1 : 0; +} + /* * lock for writing */ @@ -125,6 +148,26 @@ static inline void __down_write(struct rw_semaphore *sem) rwsem_down_write_failed(sem); } +/* + * trylock for writing -- returns 1 if successful, 0 if contention + */ +static inline int __down_write_trylock(struct rw_semaphore *sem) +{ + signed long old; + + __asm__ __volatile__( + " lg %0,0(%1)\n" + "0: ltgr %0,%0\n" + " jnz 1f\n" + " csg %0,%2,0(%1)\n" + " jl 0b\n" + "1:" + : "=&d" (old) + : "a" (&sem->count), "d" (RWSEM_ACTIVE_WRITE_BIAS) + : "cc", "memory" ); + return (old == RWSEM_UNLOCKED_VALUE) ? 1 : 0; +} + /* * unlock after reading */ @@ -168,6 +211,27 @@ static inline void __up_write(struct rw_semaphore *sem) rwsem_wake(sem); } +/* + * downgrade write lock to read lock + */ +static inline void __downgrade_write(struct rw_semaphore *sem) +{ + signed long old, new, tmp; + + tmp = -RWSEM_WAITING_BIAS; + __asm__ __volatile__( + " lg %0,0(%2)\n" + "0: lgr %1,%0\n" + " ag %1,%3\n" + " csg %0,%1,0(%2)\n" + " jl 0b" + : "=&d" (old), "=&d" (new) + : "a" (&sem->count), "m" (tmp) + : "cc", "memory" ); + if (new > 1) // FIXME: is this correct ?!? + rwsem_downgrade_wake(sem); +} + /* * implement atomic add functionality */ diff --git a/include/asm-s390x/smp.h b/include/asm-s390x/smp.h index 8caa0b8bdd87..28e1fd23b426 100644 --- a/include/asm-s390x/smp.h +++ b/include/asm-s390x/smp.h @@ -11,7 +11,7 @@ #include #include -#include +#include #if defined(__KERNEL__) && defined(CONFIG_SMP) && !defined(__ASSEMBLY__) @@ -29,6 +29,7 @@ typedef struct } sigp_info; extern volatile unsigned long cpu_online_map; +extern volatile unsigned long cpu_possible_map; #define NO_PROC_ID 0xFF /* No processor magic marker */ @@ -46,14 +47,20 @@ extern volatile unsigned long cpu_online_map; #define smp_processor_id() (current_thread_info()->cpu) -extern __inline__ int cpu_logical_map(int cpu) +#define cpu_online(cpu) (cpu_online_map & (1<<(cpu))) +#define cpu_possible(cpu) (cpu_possible_map & (1<<(cpu))) + +extern inline unsigned int num_online_cpus(void) { - return cpu; + return hweight64(cpu_online_map); } -extern __inline__ int cpu_number_map(int cpu) +extern inline int any_online_cpu(unsigned int mask) { - return cpu; + if (mask & cpu_online_map) + return __ffs(mask & cpu_online_map); + + return -1; } extern __inline__ __u16 hard_smp_processor_id(void) diff --git a/include/asm-s390x/softirq.h b/include/asm-s390x/softirq.h index b82aac30db21..91f9853561dd 100644 --- a/include/asm-s390x/softirq.h +++ b/include/asm-s390x/softirq.h @@ -9,34 +9,27 @@ #ifndef __ASM_SOFTIRQ_H #define __ASM_SOFTIRQ_H -#ifndef __LINUX_SMP_H #include -#endif +#include #include #include #include -#define __cpu_bh_enable(cpu) \ - do { barrier(); local_bh_count(cpu)--; } while (0) -#define cpu_bh_disable(cpu) \ - do { local_bh_count(cpu)++; barrier(); } while (0) - -#define local_bh_disable() cpu_bh_disable(smp_processor_id()) -#define __local_bh_enable() __cpu_bh_enable(smp_processor_id()) - -#define in_softirq() (local_bh_count(smp_processor_id()) != 0) +#define local_bh_disable() \ + do { preempt_count() += SOFTIRQ_OFFSET; barrier(); } while (0) +#define __local_bh_enable() \ + do { barrier(); preempt_count() -= SOFTIRQ_OFFSET; } while (0) extern void do_call_softirq(void); -#define local_bh_enable() \ -do { \ - unsigned int *ptr = &local_bh_count(smp_processor_id()); \ - barrier(); \ - if (!--*ptr) \ - if (softirq_pending(smp_processor_id())) \ - /* Use the async. stack for softirq */ \ - do_call_softirq(); \ +#define local_bh_enable() \ +do { \ + __local_bh_enable(); \ + if (!in_interrupt() && softirq_pending(smp_processor_id())) \ + /* Use the async. stack for softirq */ \ + do_call_softirq(); \ + preempt_check_resched(); \ } while (0) #endif /* __ASM_SOFTIRQ_H */ diff --git a/include/asm-s390x/spinlock.h b/include/asm-s390x/spinlock.h index d18dba14f2eb..f8e8bbdefe61 100644 --- a/include/asm-s390x/spinlock.h +++ b/include/asm-s390x/spinlock.h @@ -11,6 +11,13 @@ #ifndef __ASM_SPINLOCK_H #define __ASM_SPINLOCK_H +/* + * Grmph, take care of %&#! user space programs that include + * asm/spinlock.h. The diagnose is only available in kernel + * context. + */ +#include + /* * Simple spin lock operations. There are two variants, one clears IRQ's * on the local processor, one does not. @@ -76,6 +83,8 @@ typedef struct { #define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) +#define rwlock_is_locked(x) ((x)->lock != 0) + #define _raw_read_lock(rw) \ asm volatile(" lg 2,0(%1)\n" \ " j 1f\n" \ diff --git a/include/asm-s390x/system.h b/include/asm-s390x/system.h index 81810a4819b9..44f4acbb36fa 100644 --- a/include/asm-s390x/system.h +++ b/include/asm-s390x/system.h @@ -18,7 +18,7 @@ #endif #include -#define switch_to(prev,next),last do { \ +#define switch_to(prev,next,last) do { \ if (prev == next) \ break; \ save_fp_regs(&prev->thread.fp_regs); \ @@ -114,6 +114,83 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) return x; } +/* + * Atomic compare and exchange. Compare OLD with MEM, if identical, + * store NEW in MEM. Return the initial value in MEM. Success is + * indicated by comparing RETURN with OLD. + */ + +#define __HAVE_ARCH_CMPXCHG 1 + +#define cmpxchg(ptr,o,n)\ + ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ + (unsigned long)(n),sizeof(*(ptr)))) + +static inline unsigned long +__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) +{ + unsigned long addr, prev, tmp; + int shift; + + switch (size) { + case 1: + addr = (unsigned long) ptr; + shift = (3 ^ (addr & 3)) << 3; + addr ^= addr & 3; + asm volatile( + " l %0,0(%4)\n" + "0: nr %0,%5\n" + " lr %1,%0\n" + " or %0,%2\n" + " or %1,%3\n" + " cs %0,%1,0(%4)\n" + " jnl 1f\n" + " xr %1,%0\n" + " nr %1,%5\n" + " jnz 0b\n" + "1:" + : "=&d" (prev), "=&d" (tmp) + : "d" (old << shift), "d" (new << shift), "a" (ptr), + "d" (~(255 << shift)) + : "memory", "cc" ); + return prev >> shift; + case 2: + addr = (unsigned long) ptr; + shift = (2 ^ (addr & 2)) << 3; + addr ^= addr & 2; + asm volatile( + " l %0,0(%4)\n" + "0: nr %0,%5\n" + " lr %1,%0\n" + " or %0,%2\n" + " or %1,%3\n" + " cs %0,%1,0(%4)\n" + " jnl 1f\n" + " xr %1,%0\n" + " nr %1,%5\n" + " jnz 0b\n" + "1:" + : "=&d" (prev), "=&d" (tmp) + : "d" (old << shift), "d" (new << shift), "a" (ptr), + "d" (~(65535 << shift)) + : "memory", "cc" ); + return prev >> shift; + case 4: + asm volatile ( + " cs %0,%2,0(%3)\n" + : "=&d" (prev) : "0" (old), "d" (new), "a" (ptr) + : "memory", "cc" ); + return prev; + case 8: + asm volatile ( + " csg %0,%2,0(%3)\n" + : "=&d" (prev) : "0" (old), "d" (new), "a" (ptr) + : "memory", "cc" ); + return prev; + } + return old; +} + /* * Force strict CPU ordering. * And yes, this is required on UP too when we're talking @@ -158,6 +235,13 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) #define local_irq_restore(x) \ __asm__ __volatile__("ssm 0(%0)" : : "a" (&x) : "memory") +#define irqs_disabled() \ +({ \ + unsigned long flags; \ + local_save_flags(flags); \ + !((flags >> 56) & 3); \ +}) + #define __load_psw(psw) \ __asm__ __volatile__("lpswe 0(%0)" : : "a" (&psw) : "cc" ); @@ -220,16 +304,6 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) #ifdef CONFIG_SMP -extern void __global_cli(void); -extern void __global_sti(void); - -extern unsigned long __global_save_flags(void); -extern void __global_restore_flags(unsigned long); -#define cli() __global_cli() -#define sti() __global_sti() -#define save_flags(x) ((x)=__global_save_flags()) -#define restore_flags(x) __global_restore_flags(x) - extern void smp_ctl_set_bit(int cr, int bit); extern void smp_ctl_clear_bit(int cr, int bit); #define ctl_set_bit(cr, bit) smp_ctl_set_bit(cr, bit) @@ -237,15 +311,9 @@ extern void smp_ctl_clear_bit(int cr, int bit); #else -#define cli() local_irq_disable() -#define sti() local_irq_enable() -#define save_flags(x) local_save_flags(x) -#define restore_flags(x) local_irq_restore(x) - #define ctl_set_bit(cr, bit) __ctl_set_bit(cr, bit) #define ctl_clear_bit(cr, bit) __ctl_clear_bit(cr, bit) - #endif #ifdef __KERNEL__ diff --git a/include/asm-s390x/tlbflush.h b/include/asm-s390x/tlbflush.h index d064a59b0678..77b6f6dd7f21 100644 --- a/include/asm-s390x/tlbflush.h +++ b/include/asm-s390x/tlbflush.h @@ -88,8 +88,7 @@ static inline void global_flush_tlb(void) static inline void __flush_tlb_mm(struct mm_struct * mm) { - if ((smp_num_cpus > 1) && - ((atomic_read(&mm->mm_count) != 1) || + if (((atomic_read(&mm->mm_count) != 1) || (mm->cpu_vm_mask != (1UL << smp_processor_id())))) { mm->cpu_vm_mask = (1UL << smp_processor_id()); global_flush_tlb(); -- cgit v1.2.3 From ec0003c22af6766c8e2da5f34f0c061679937482 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 3 Oct 2002 21:45:24 -0700 Subject: [PATCH] s390 update (4/27): syscalls. New system calls: security, async. i/o and sys_exit_group. Add 31 bit emulation function for sys_futex. --- arch/s390/kernel/entry.S | 10 +++++++++- arch/s390x/kernel/entry.S | 14 +++++++++++--- arch/s390x/kernel/linux32.c | 31 +++++++++++++++++++++++++++++++ arch/s390x/kernel/wrapper32.S | 6 +++++- include/asm-s390/unistd.h | 10 ++++++++++ include/asm-s390x/unistd.h | 10 ++++++++++ 6 files changed, 76 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 372bfc1a8dc1..a5553e7bdde2 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -580,7 +580,15 @@ sys_call_table: .long sys_futex .long sys_sched_setaffinity .long sys_sched_getaffinity /* 240 */ - .rept 255-240 + .long sys_security + .long sys_ni_syscall /* reserved for TUX */ + .long sys_io_setup + .long sys_io_destroy + .long sys_io_getevents /* 245 */ + .long sys_io_submit + .long sys_io_cancel + .long sys_exit_group + .rept 255-248 .long sys_ni_syscall .endr diff --git a/arch/s390x/kernel/entry.S b/arch/s390x/kernel/entry.S index e74b88059bbd..387aebfc0c2e 100644 --- a/arch/s390x/kernel/entry.S +++ b/arch/s390x/kernel/entry.S @@ -605,13 +605,21 @@ sys_call_table: .long SYSCALL(sys_flistxattr,sys32_flistxattr_wrapper) .long SYSCALL(sys_removexattr,sys32_removexattr_wrapper) .long SYSCALL(sys_lremovexattr,sys32_lremovexattr_wrapper) - .long SYSCALL(sys_fremovexattr,sys32_fremovexattr_wrapper) + .long SYSCALL(sys_fremovexattr,sys32_fremovexattr_wrapper) /* 235 */ .long SYSCALL(sys_gettid,sys_gettid) .long SYSCALL(sys_tkill,sys_tkill) .long SYSCALL(sys_futex,sys32_futex_wrapper) .long SYSCALL(sys_sched_setaffinity,sys32_sched_setaffinity_wrapper) - .long SYSCALL(sys_sched_getaffinity,sys32_sched_getaffinity_wrapper) - .rept 255-240 + .long SYSCALL(sys_sched_getaffinity,sys32_sched_getaffinity_wrapper) /* 240 */ + .long SYSCALL(sys_security,sys_ni_syscall) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* reserved for TUX */ + .long SYSCALL(sys_io_setup,sys_ni_syscall) + .long SYSCALL(sys_io_destroy,sys_ni_syscall) + .long SYSCALL(sys_io_getevents,sys_ni_syscall) /* 245 */ + .long SYSCALL(sys_io_submit,sys_ni_syscall) + .long SYSCALL(sys_io_cancel,sys_ni_syscall) + .long SYSCALL(sys_exit_group,sys32_exit_group_wrapper) + .rept 255-248 .long SYSCALL(sys_ni_syscall,sys_ni_syscall) .endr diff --git a/arch/s390x/kernel/linux32.c b/arch/s390x/kernel/linux32.c index 7d14f9279b1c..49b54ce15b8c 100644 --- a/arch/s390x/kernel/linux32.c +++ b/arch/s390x/kernel/linux32.c @@ -4527,3 +4527,34 @@ asmlinkage int sys32_sched_getaffinity(__kernel_pid_t32 pid, unsigned int len, return ret; } + +asmlinkage int +sys_futex(void *uaddr, int op, int val, struct timespec *utime); + +asmlinkage int +sys32_futex(void *uaddr, int op, int val, + struct timespec32 *timeout32) +{ + long ret; + struct timespec tmp, *timeout; + + ret = -ENOMEM; + timeout = kmalloc(sizeof(*timeout), GFP_USER); + if (!timeout) + goto out; + + ret = -EINVAL; + if (get_user (tmp.tv_sec, &timeout32->tv_sec) || + get_user (tmp.tv_nsec, &timeout32->tv_nsec) || + put_user (tmp.tv_sec, &timeout->tv_sec) || + put_user (tmp.tv_nsec, &timeout->tv_nsec)) + goto out_free; + + ret = sys_futex(uaddr, op, val, timeout); + +out_free: + kfree(timeout); +out: + return ret; +} + diff --git a/arch/s390x/kernel/wrapper32.S b/arch/s390x/kernel/wrapper32.S index 083d8383e490..4f23e2e05b41 100644 --- a/arch/s390x/kernel/wrapper32.S +++ b/arch/s390x/kernel/wrapper32.S @@ -1114,7 +1114,7 @@ sys32_futex_wrapper: lgfr %r3,%r3 # int lgfr %r4,%r4 # int llgtr %r5,%r5 # struct timespec * - jg sys_futex # branch to system call + jg sys32_futex # branch to system call .globl sys32_setxattr_wrapper sys32_setxattr_wrapper: @@ -1220,3 +1220,7 @@ sys32_sched_getaffinity_wrapper: llgtr %r4,%r4 # unsigned long * jg sys32_sched_getaffinity + .globl sys32_exit_group_wrapper +sys32_exit_group_wrapper: + lgfr %r2,%r2 # int + jg sys_exit_group # branch to system call diff --git a/include/asm-s390/unistd.h b/include/asm-s390/unistd.h index ff9cf61ee3e6..e295910bd72d 100644 --- a/include/asm-s390/unistd.h +++ b/include/asm-s390/unistd.h @@ -231,6 +231,16 @@ #define __NR_futex 238 #define __NR_sched_setaffinity 239 #define __NR_sched_getaffinity 240 +#define __NR_security 241 /* syscall for security modules */ +/* + * Number 242 is reserved for tux + */ +#define __NR_io_setup 243 +#define __NR_io_destroy 244 +#define __NR_io_getevents 245 +#define __NR_io_submit 246 +#define __NR_io_cancel 247 +#define __NR_exit_group 248 /* user-visible error numbers are in the range -1 - -122: see */ diff --git a/include/asm-s390x/unistd.h b/include/asm-s390x/unistd.h index cec7c863e072..dc8edd478cd5 100644 --- a/include/asm-s390x/unistd.h +++ b/include/asm-s390x/unistd.h @@ -198,6 +198,16 @@ #define __NR_futex 238 #define __NR_sched_setaffinity 239 #define __NR_sched_getaffinity 240 +#define __NR_security 241 /* syscall for security modules */ +/* + * Number 242 is reserved for tux + */ +#define __NR_io_setup 243 +#define __NR_io_destroy 244 +#define __NR_io_getevents 245 +#define __NR_io_submit 246 +#define __NR_io_cancel 247 +#define __NR_exit_group 248 /* user-visible error numbers are in the range -1 - -122: see */ -- cgit v1.2.3 From fd791128011844e53fafaa14e88b9324a6a0ca0b Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 3 Oct 2002 21:46:11 -0700 Subject: [PATCH] s390 update (7/27): dasd driver. --- drivers/s390/block/dasd.c | 107 +++++++++++-------- drivers/s390/block/dasd_devmap.c | 9 ++ drivers/s390/block/dasd_diag.c | 41 ++----- drivers/s390/block/dasd_eckd.c | 36 +++---- drivers/s390/block/dasd_fba.c | 7 +- drivers/s390/block/dasd_genhd.c | 225 +++++++++++++++++++++++---------------- drivers/s390/block/dasd_int.h | 22 ++-- drivers/s390/block/dasd_ioctl.c | 124 ++++++++++++--------- drivers/s390/block/dasd_proc.c | 15 ++- include/asm-s390/dasd.h | 4 +- include/asm-s390x/dasd.h | 4 +- 11 files changed, 325 insertions(+), 269 deletions(-) (limited to 'include') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index e9278eec247e..33df594fb53c 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -203,20 +203,24 @@ dasd_device_t * dasd_alloc_device(dasd_devmap_t *devmap) { dasd_device_t *device; + struct gendisk *gdp; int rc; - /* Make sure the gendisk structure for this device exists. */ - while (dasd_gendisk_from_devindex(devmap->devindex) == NULL) { - rc = dasd_gendisk_new_major(); - if (rc) - return ERR_PTR(rc); - } - device = kmalloc(sizeof (dasd_device_t), GFP_ATOMIC); if (device == NULL) return ERR_PTR(-ENOMEM); memset(device, 0, sizeof (dasd_device_t)); + /* Get devinfo from the common io layer. */ + rc = get_dev_info_by_devno(devmap->devno, &device->devinfo); + if (rc) { + kfree(device); + return ERR_PTR(rc); + } + DBF_EVENT(DBF_NOTICE, "got devinfo CU-type %04x and dev-type %04x", + device->devinfo.sid_data.cu_type, + device->devinfo.sid_data.dev_type); + /* Get two pages for normal block device operations. */ device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1); if (device->ccw_mem == NULL) { @@ -231,17 +235,15 @@ dasd_alloc_device(dasd_devmap_t *devmap) return ERR_PTR(-ENOMEM); } - /* Get devinfo from the common io layer. */ - rc = get_dev_info_by_devno(devmap->devno, &device->devinfo); - if (rc) { - free_page((unsigned long) device->erp_mem); + /* Allocate gendisk structure for device. */ + gdp = dasd_gendisk_alloc(device->name, devmap->devindex); + if (IS_ERR(gdp)) { + free_page((unsigned long) device->erp_mem); free_pages((unsigned long) device->ccw_mem, 1); kfree(device); - return ERR_PTR(rc); + return (dasd_device_t *) gdp; } - DBF_EVENT(DBF_NOTICE, "got devinfo CU-type %04x and dev-type %04x", - device->devinfo.sid_data.cu_type, - device->devinfo.sid_data.dev_type); + device->gdp = gdp; dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2); dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE); @@ -268,6 +270,7 @@ dasd_free_device(dasd_device_t *device) kfree(device->private); free_page((unsigned long) device->erp_mem); free_pages((unsigned long) device->ccw_mem, 1); + dasd_gendisk_free(device->gdp); kfree(device); } @@ -278,21 +281,22 @@ static inline int dasd_state_new_to_known(dasd_device_t *device) { char buffer[5]; - struct gendisk *gdp; dasd_devmap_t *devmap; umode_t devfs_perm; devfs_handle_t dir; - int minor, rc; + int major, minor, rc; devmap = dasd_devmap_from_devno(device->devinfo.devno); if (devmap == NULL) return -ENODEV; - gdp = dasd_gendisk_from_devindex(devmap->devindex); - if (gdp == NULL) + major = dasd_gendisk_index_major(devmap->devindex); + if (major < 0) return -ENODEV; - /* Set kdev and the device name. */ - device->kdev = mk_kdev(gdp->major, gdp->first_minor); - strcpy(device->name, gdp->disk_name); + minor = devmap->devindex % DASD_PER_MAJOR; + + /* Set bdev and the device name. */ + device->bdev = bdget(MKDEV(major, minor << DASD_PARTN_BITS)); + strcpy(device->name, device->gdp->disk_name); /* Find a discipline for the device. */ rc = dasd_find_disc(device); @@ -302,14 +306,13 @@ dasd_state_new_to_known(dasd_device_t *device) /* Add a proc directory and the dasd device entry to devfs. */ sprintf(buffer, "%04x", device->devinfo.devno); dir = devfs_mk_dir(dasd_devfs_handle, buffer, device); - gdp->de = dir; + device->gdp->de = dir; if (devmap->features & DASD_FEATURE_READONLY) devfs_perm = S_IFBLK | S_IRUSR; else devfs_perm = S_IFBLK | S_IRUSR | S_IWUSR; device->devfs_entry = devfs_register(dir, "device", DEVFS_FL_DEFAULT, - major(device->kdev), - minor(device->kdev), + major, minor << DASD_PARTN_BITS, devfs_perm, &dasd_device_operations, NULL); device->state = DASD_STATE_KNOWN; @@ -322,17 +325,25 @@ dasd_state_new_to_known(dasd_device_t *device) static inline void dasd_state_known_to_new(dasd_device_t * device) { - dasd_devmap_t *devmap = dasd_devmap_from_devno(device->devinfo.devno); - struct gendisk *gdp = dasd_gendisk_from_devindex(devmap->devindex); - if (gdp == NULL) - return; + dasd_devmap_t *devmap; + struct block_device *bdev; + int minor; + + devmap = dasd_devmap_from_devno(device->devinfo.devno); + minor = devmap->devindex % DASD_PER_MAJOR; + /* Remove device entry and devfs directory. */ devfs_unregister(device->devfs_entry); - devfs_unregister(gdp->de); + devfs_unregister(device->gdp->de); /* Forget the discipline information. */ device->discipline = NULL; device->state = DASD_STATE_NEW; + + /* Forget the block device */ + bdev = device->bdev; + device->bdev = NULL; + bdput(bdev); } /* @@ -418,6 +429,17 @@ dasd_state_accept_to_basic(dasd_device_t * device) device->state = DASD_STATE_BASIC; } +/* + * get the kdev_t of a device + * FIXME: remove this when no longer needed + */ +static inline kdev_t +dasd_partition_to_kdev_t(dasd_device_t *device, unsigned int partition) +{ + return to_kdev_t(device->bdev->bd_dev+partition); +} + + /* * Setup block device. */ @@ -425,15 +447,12 @@ static inline int dasd_state_accept_to_ready(dasd_device_t * device) { dasd_devmap_t *devmap; - int major, minor; int rc, i; devmap = dasd_devmap_from_devno(device->devinfo.devno); if (devmap->features & DASD_FEATURE_READONLY) { - major = major(device->kdev); - minor = minor(device->kdev); for (i = 0; i < (1 << DASD_PARTN_BITS); i++) - set_device_ro(mk_kdev(major, minor+i), 1); + set_device_ro(dasd_partition_to_kdev_t(device, i), 1); DEV_MESSAGE (KERN_WARNING, device, "%s", "setting read-only mode "); } @@ -1539,11 +1558,9 @@ restart: goto restart; } - /* Dechain request from device request queue ... */ + /* Rechain request on device device request queue */ cqr->endclk = get_clock(); - list_del(&cqr->list); - /* ... and add it to list of final requests. */ - list_add_tail(&cqr->list, final_queue); + list_move_tail(&cqr->list, final_queue); } } @@ -1572,6 +1589,10 @@ __dasd_process_blk_queue(dasd_device_t * device) dasd_ccw_req_t *cqr; int nr_queued; + /* No bdev, no queue. */ + bdev = device->bdev; + if (!bdev) + return; queue = device->request_queue; /* No queue ? Then there is nothing to do. */ if (queue == NULL) @@ -1594,9 +1615,6 @@ __dasd_process_blk_queue(dasd_device_t * device) if (cqr->status == DASD_CQR_QUEUED) nr_queued++; } - bdev = bdget(kdev_t_to_nr(device->kdev)); - if (!bdev) - return; while (!blk_queue_plugged(queue) && !blk_queue_empty(queue) && nr_queued < DASD_CHANQ_MAX_SIZE) { @@ -1628,7 +1646,6 @@ __dasd_process_blk_queue(dasd_device_t * device) dasd_profile_start(device, cqr, req); nr_queued++; } - bdput(bdev); } /* @@ -1707,11 +1724,9 @@ dasd_flush_ccw_queue(dasd_device_t * device, int all) __dasd_process_erp(device, cqr); continue; } - /* Dechain request from device request queue ... */ + /* Rechain request on device request queue */ cqr->endclk = get_clock(); - list_del(&cqr->list); - /* ... and add it to list of flushed requests. */ - list_add_tail(&cqr->list, &flush_queue); + list_move_tail(&cqr->list, &flush_queue); } spin_unlock_irq(get_irq_lock(device->devinfo.irq)); /* Now call the callback function of flushed requests */ diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index b4d362aba2c5..129fe6615d70 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -448,6 +448,15 @@ dasd_devmap_from_kdev(kdev_t kdev) return dasd_devmap_from_devindex(devindex); } +/* + * Find the devmap for a device corresponding to a block_device. + */ +dasd_devmap_t * +dasd_devmap_from_bdev(struct block_device *bdev) +{ + return dasd_devmap_from_kdev(to_kdev_t(bdev->bd_dev)); +} + /* * Find the device structure for device number devno. If it does not * exists yet, allocate it. Increase the reference counter in the device diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index ffe9c012e866..cebb7b321679 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -21,6 +21,7 @@ #include #include #include /* HDIO_GETGEO */ +#include #include #include @@ -53,31 +54,6 @@ typedef struct dasd_diag_req_t { diag_bio_t bio[0]; } dasd_diag_req_t; - -static __inline__ int -dia210(void *devchar) -{ - int rc; - - __asm__ __volatile__(" diag %1,0,0x210\n" - "0: ipm %0\n" - " srl %0,28\n" - "1:\n" - ".section .fixup,\"ax\"\n" - "2: lhi %0,3\n" - " bras 1,3f\n" - " .long 1b\n" - "3: l 1,0(1)\n" - " br 1\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 0b,2b\n" ".previous\n":"=d"(rc) - :"d"((void *) __pa(devchar)) - :"1"); - return rc; -} - static __inline__ int dia250(void *iob, int cmd) { @@ -155,7 +131,7 @@ dasd_start_diag(dasd_ccw_req_t * cqr) private->iob.key = 0; private->iob.flags = 2; /* do asynchronous io */ private->iob.block_count = dreq->block_count; - private->iob.interrupt_params = (u32) cqr; + private->iob.interrupt_params = (u32)(addr_t) cqr; private->iob.bio_list = __pa(dreq->bio); cqr->startclk = get_clock(); @@ -196,21 +172,21 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code) ip = S390_lowcore.ext_params; cpu = smp_processor_id(); - irq_enter(cpu, -1); + irq_enter(); if (!ip) { /* no intparm: unsolicited interrupt */ MESSAGE(KERN_DEBUG, "%s", "caught unsolicited interrupt"); - irq_exit(cpu, -1); + irq_exit(); return; } - cqr = (dasd_ccw_req_t *) ip; + cqr = (dasd_ccw_req_t *)(addr_t) ip; device = (dasd_device_t *) cqr->device; if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { DEV_MESSAGE(KERN_WARNING, device, " magic number of dasd_ccw_req_t 0x%08X doesn't" " match discipline 0x%08X", cqr->magic, *(int *) (&device->discipline->name)); - irq_exit(cpu, -1); + irq_exit(); return; } @@ -244,8 +220,7 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code) dasd_schedule_bh(device); spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags); - irq_exit(cpu, -1); - + irq_exit(); } static int @@ -273,7 +248,7 @@ dasd_diag_check_device(dasd_device_t *device) rdc_data->dev_nr = device->devinfo.devno; rdc_data->rdc_len = sizeof (dasd_diag_characteristics_t); - rc = dia210(rdc_data); + rc = diag210((diag210_t *) rdc_data); if (rc) return -ENOTSUPP; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index d8d0e782f85c..36de4e2434ab 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -29,6 +29,7 @@ #include #include #include /* HDIO_GETGEO */ +#include #include #include @@ -69,7 +70,6 @@ typedef struct dasd_eckd_private_t { attrib_data_t attrib; /* e.g. cache operations */ } dasd_eckd_private_t; -#ifdef CONFIG_DASD_DYNAMIC static devreg_t dasd_eckd_known_devices[] = { { @@ -94,7 +94,6 @@ devreg_t dasd_eckd_known_devices[] = { oper_func:dasd_oper_handler } }; -#endif static const int sizes_trk0[] = { 28, 148, 84 }; #define LABEL_SIZE 140 @@ -1092,7 +1091,8 @@ dasd_eckd_fill_info(dasd_device_t * device, dasd_information2_t * info) * Buils a channel programm to releases a prior reserved * (see dasd_eckd_reserve) device. */ -static int dasd_eckd_release(void *inp, int no, long args) +static int +dasd_eckd_release(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; @@ -1101,7 +1101,7 @@ static int dasd_eckd_release(void *inp, int no, long args) if (!capable(CAP_SYS_ADMIN)) return -EACCES; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -1134,7 +1134,8 @@ static int dasd_eckd_release(void *inp, int no, long args) * 'timeout the request'. This leads to an terminate IO if * the interrupt is outstanding for a certain time. */ -static int dasd_eckd_reserve(void *inp, int no, long args) +static int +dasd_eckd_reserve(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; @@ -1143,7 +1144,7 @@ static int dasd_eckd_reserve(void *inp, int no, long args) if (!capable(CAP_SYS_ADMIN)) return -EACCES; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -1168,7 +1169,7 @@ static int dasd_eckd_reserve(void *inp, int no, long args) if (rc == -EIO) { /* Request got an eror or has been timed out. */ - dasd_eckd_release(inp, no, args); + dasd_eckd_release(bdev, no, args); } dasd_kfree_request(cqr, cqr->device); dasd_put_device(devmap); @@ -1180,7 +1181,8 @@ static int dasd_eckd_reserve(void *inp, int no, long args) * Buils a channel programm to break a device's reservation. * (unconditional reserve) */ -static int dasd_eckd_steal_lock(void *inp, int no, long args) +static int +dasd_eckd_steal_lock(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; @@ -1189,7 +1191,7 @@ static int dasd_eckd_steal_lock(void *inp, int no, long args) if (!capable(CAP_SYS_ADMIN)) return -EACCES; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -1213,7 +1215,7 @@ static int dasd_eckd_steal_lock(void *inp, int no, long args) if (rc == -EIO) { /* Request got an eror or has been timed out. */ - dasd_eckd_release(inp, no, args); + dasd_eckd_release(bdev, no, args); } dasd_kfree_request(cqr, cqr->device); dasd_put_device(devmap); @@ -1223,7 +1225,8 @@ static int dasd_eckd_steal_lock(void *inp, int no, long args) /* * Read performance statistics */ -static int dasd_eckd_performance(void *inp, int no, long args) +static int +dasd_eckd_performance(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; @@ -1233,7 +1236,7 @@ static int dasd_eckd_performance(void *inp, int no, long args) ccw1_t *ccw; int rc; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -1292,7 +1295,8 @@ static int dasd_eckd_performance(void *inp, int no, long args) * Set attributes (cache operations) * Stores the attributes for cache operation to be used in Define Extend (DE). */ -static int dasd_eckd_set_attrib(void *inp, int no, long args) +static int +dasd_eckd_set_attrib(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; @@ -1304,7 +1308,7 @@ static int dasd_eckd_set_attrib(void *inp, int no, long args) if (!args) return -EINVAL; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -1452,10 +1456,8 @@ dasd_eckd_init(void) ASCEBC(dasd_eckd_discipline.ebcname, 4); dasd_discipline_add(&dasd_eckd_discipline); -#ifdef CONFIG_DASD_DYNAMIC for (i = 0; i < sizeof(dasd_eckd_known_devices)/sizeof(devreg_t); i++) s390_device_register(&dasd_eckd_known_devices[i]); -#endif return 0; } @@ -1464,10 +1466,8 @@ dasd_eckd_cleanup(void) { int i; -#ifdef CONFIG_DASD_DYNAMIC for (i = 0; i < sizeof(dasd_eckd_known_devices)/sizeof(devreg_t); i++) s390_device_unregister(&dasd_eckd_known_devices[i]); -#endif /* CONFIG_DASD_DYNAMIC */ dasd_discipline_del(&dasd_eckd_discipline); diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 733e7c45822b..da091ce0b6c9 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -16,6 +16,7 @@ #include #include /* HDIO_GETGEO */ +#include #include #include @@ -43,7 +44,6 @@ typedef struct dasd_fba_private_t { dasd_fba_characteristics_t rdc_data; } dasd_fba_private_t; -#ifdef CONFIG_DASD_DYNAMIC static devreg_t dasd_fba_known_devices[] = { { @@ -59,7 +59,6 @@ devreg_t dasd_fba_known_devices[] = { oper_func:dasd_oper_handler } }; -#endif static inline void define_extent(ccw1_t * ccw, DE_fba_data_t *data, int rw, @@ -417,10 +416,8 @@ dasd_fba_init(void) ASCEBC(dasd_fba_discipline.ebcname, 4); dasd_discipline_add(&dasd_fba_discipline); -#ifdef CONFIG_DASD_DYNAMIC for (i = 0; i < sizeof(dasd_fba_known_devices) / sizeof(devreg_t); i++) s390_device_register(&dasd_fba_known_devices[i]); -#endif return 0; } @@ -429,10 +426,8 @@ dasd_fba_cleanup(void) { int i; -#ifdef CONFIG_DASD_DYNAMIC for (i = 0; i < sizeof(dasd_fba_known_devices) / sizeof(devreg_t); i++) s390_device_unregister(&dasd_fba_known_devices[i]); -#endif dasd_discipline_del(&dasd_fba_discipline); } diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c index 30c8a6bd1024..9f326e1309c6 100644 --- a/drivers/s390/block/dasd_genhd.c +++ b/drivers/s390/block/dasd_genhd.c @@ -33,7 +33,6 @@ static struct list_head dasd_major_info = LIST_HEAD_INIT(dasd_major_info); struct major_info { struct list_head list; int major; - struct gendisk disks[DASD_PER_MAJOR]; }; /* @@ -65,12 +64,8 @@ static int dasd_register_major(int major) { struct major_info *mi; - int new_major, rc; - struct list_head *l; - int index; - int i; + int new_major; - rc = 0; /* Allocate major info structure. */ mi = kmalloc(sizeof(struct major_info), GFP_KERNEL); @@ -79,66 +74,39 @@ dasd_register_major(int major) MESSAGE(KERN_WARNING, "%s", "Cannot get memory to allocate another " "major number"); - rc = -ENOMEM; - goto out_error; + return -ENOMEM; } /* Register block device. */ new_major = register_blkdev(major, "dasd", &dasd_device_operations); if (new_major < 0) { MESSAGE(KERN_WARNING, - "Cannot register to major no %d, rc = %d", major, rc); - rc = new_major; - goto out_error; + "Cannot register to major no %d, rc = %d", + major, new_major); + kfree(mi); + return new_major; } if (major != 0) new_major = major; - + /* Initialize major info structure. */ - memset(mi, 0, sizeof(struct major_info)); mi->major = new_major; - for (i = 0; i < DASD_PER_MAJOR; i++) { - struct gendisk *disk = mi->disks + i; - disk->major = new_major; - disk->first_minor = i << DASD_PARTN_BITS; - disk->minor_shift = DASD_PARTN_BITS; - disk->fops = &dasd_device_operations; - disk->flags = GENHD_FL_DEVFS; - } /* Setup block device pointers for the new major. */ blk_dev[new_major].queue = dasd_get_queue; + /* Insert the new major info structure into dasd_major_info list. */ spin_lock(&dasd_major_lock); - index = 0; - list_for_each(l, &dasd_major_info) - index += DASD_PER_MAJOR; - for (i = 0; i < DASD_PER_MAJOR; i++, index++) { - name = mi->disks[i].disk_name; - sprintf(name, "dasd"); - name += 4; - if (index > 701) - *name++ = 'a' + (((index - 702) / 676) % 26); - if (index > 25) - *name++ = 'a' + (((index - 26) / 26) % 26); - sprintf(name, "%c", 'a' + (index % 26)); - } list_add_tail(&mi->list, &dasd_major_info); spin_unlock(&dasd_major_lock); return 0; - - /* Something failed. Do the cleanup and return rc. */ -out_error: - /* We rely on kfree to do the != NULL check. */ - kfree(mi); - return rc; } static void dasd_unregister_major(struct major_info * mi) { - int major, rc; + int rc; if (mi == NULL) return; @@ -149,99 +117,177 @@ dasd_unregister_major(struct major_info * mi) spin_unlock(&dasd_major_lock); /* Clear block device pointers. */ - major = mi->major; - blk_dev[major].queue = NULL; + blk_dev[mi->major].queue = NULL; - rc = unregister_blkdev(major, "dasd"); + rc = unregister_blkdev(mi->major, "dasd"); if (rc < 0) MESSAGE(KERN_WARNING, "Cannot unregister from major no %d, rc = %d", - major, rc); + mi->major, rc); /* Free memory. */ kfree(mi); } /* - * Dynamically allocate a new major for dasd devices. + * This one is needed for naming 18000+ possible dasd devices. + * dasda - dasdz : 26 devices + * dasdaa - dasdzz : 676 devices, added up = 702 + * dasdaaa - dasdzzz : 17576 devices, added up = 18278 */ int -dasd_gendisk_new_major(void) +dasd_device_name(char *str, int index, int partition) { - int rc; - - rc = dasd_register_major(0); - if (rc) - DBF_EXC(DBF_ALERT, "%s", "out of major numbers!"); - return rc; + int len; + + if (partition > DASD_PARTN_MASK) + return -EINVAL; + + len = sprintf(str, "dasd"); + if (index > 25) { + if (index > 701) + len += sprintf(str + len, "%c", + 'a' + (((index - 702) / 676) % 26)); + len += sprintf(str + len, "%c", + 'a' + (((index - 26) / 26) % 26)); + } + len += sprintf(str + len, "%c", 'a' + (index % 26)); + + if (partition) + len += sprintf(str + len, "%d", partition); + return 0; } /* - * Return pointer to gendisk structure by kdev. + * Allocate gendisk structure for devindex. */ -static struct gendisk *dasd_gendisk_by_dev(kdev_t dev) +struct gendisk * +dasd_gendisk_alloc(char *device_name, int devindex) { struct list_head *l; struct major_info *mi; struct gendisk *gdp; - int major = major(dev); - - spin_lock(&dasd_major_lock); - gdp = NULL; - list_for_each(l, &dasd_major_info) { - mi = list_entry(l, struct major_info, list); - if (mi->major == major) { - gdp = &mi->disks[minor(dev) >> DASD_PARTN_BITS]; + struct hd_struct *gd_part; + int index, len, rc; + + /* Make sure the major for this device exists. */ + mi = NULL; + while (1) { + spin_lock(&dasd_major_lock); + index = devindex; + list_for_each(l, &dasd_major_info) { + mi = list_entry(l, struct major_info, list); + if (index < DASD_PER_MAJOR) + break; + index -= DASD_PER_MAJOR; + } + spin_unlock(&dasd_major_lock); + if (index < DASD_PER_MAJOR) break; + rc = dasd_register_major(0); + if (rc) { + DBF_EXC(DBF_ALERT, "%s", "out of major numbers!"); + return ERR_PTR(rc); } } - spin_unlock(&dasd_major_lock); + + /* Allocate genhd structure and gendisk arrays. */ + gdp = kmalloc(sizeof(struct gendisk), GFP_KERNEL); + gd_part = kmalloc(sizeof (struct hd_struct) << DASD_PARTN_BITS, + GFP_ATOMIC); + + /* Check if one of the allocations failed. */ + if (gdp == NULL || gd_part == NULL) { + /* We rely on kfree to do the != NULL check. */ + kfree(gd_part); + kfree(gdp); + return ERR_PTR(-ENOMEM); + } + + /* Initialize gendisk structure. */ + memset(gdp, 0, sizeof(struct gendisk)); + memcpy(gdp->disk_name, device_name, 16); + gdp->major = mi->major; + gdp->first_minor = index << DASD_PARTN_BITS; + gdp->minor_shift = DASD_PARTN_BITS; + gdp->part = gd_part; + gdp->fops = &dasd_device_operations; + + /* + * Set device name. + * dasda - dasdz : 26 devices + * dasdaa - dasdzz : 676 devices, added up = 702 + * dasdaaa - dasdzzz : 17576 devices, added up = 18278 + */ + len = sprintf(device_name, "dasd"); + if (devindex > 25) { + if (devindex > 701) + len += sprintf(device_name + len, "%c", + 'a' + (((devindex - 702) / 676) % 26)); + len += sprintf(device_name + len, "%c", + 'a' + (((devindex - 26) / 26) % 26)); + } + len += sprintf(device_name + len, "%c", 'a' + (devindex % 26)); + + /* Initialize the gendisk arrays. */ + memset(gd_part, 0, sizeof (struct hd_struct) << DASD_PARTN_BITS); + return gdp; } /* - * Return pointer to gendisk structure by devindex. + * Free gendisk structure for devindex. */ -struct gendisk * -dasd_gendisk_from_devindex(int devindex) +void +dasd_gendisk_free(struct gendisk *gdp) +{ + /* Free memory. */ + kfree(gdp->part); + kfree(gdp); +} + +/* + * Return devindex of first device using a specific major number. + */ +int dasd_gendisk_major_index(int major) { struct list_head *l; struct major_info *mi; - struct gendisk *gdp; + int devindex, rc; spin_lock(&dasd_major_lock); - gdp = NULL; + rc = -EINVAL; + devindex = 0; list_for_each(l, &dasd_major_info) { mi = list_entry(l, struct major_info, list); - if (devindex < DASD_PER_MAJOR) { - gdp = &mi->disks[devindex]; + if (mi->major == major) { + rc = devindex; break; } - devindex -= DASD_PER_MAJOR; + devindex += DASD_PER_MAJOR; } spin_unlock(&dasd_major_lock); - return gdp; + return rc; } /* - * Return devindex of first device using a specifiy major number. + * Return major number for device with device index devindex. */ -int dasd_gendisk_major_index(int major) +int dasd_gendisk_index_major(int devindex) { struct list_head *l; struct major_info *mi; - int devindex, rc; + int rc; spin_lock(&dasd_major_lock); - rc = -EINVAL; - devindex = 0; + rc = -ENODEV; list_for_each(l, &dasd_major_info) { mi = list_entry(l, struct major_info, list); - if (mi->major == major) { - rc = devindex; + if (devindex < DASD_PER_MAJOR) { + rc = mi->major; break; } - devindex += DASD_PER_MAJOR; + devindex -= DASD_PER_MAJOR; } spin_unlock(&dasd_major_lock); return rc; @@ -253,11 +299,9 @@ int dasd_gendisk_major_index(int major) void dasd_setup_partitions(dasd_device_t * device) { - struct gendisk *disk = dasd_gendisk_by_dev(device->kdev); - if (disk == NULL) - return; - set_capacity(disk, device->blocks << device->s2b_shift); - add_disk(disk); + /* Make the disk known. */ + set_capacity(device->gdp, device->blocks << device->s2b_shift); + add_disk(device->gdp); } /* @@ -267,13 +311,7 @@ dasd_setup_partitions(dasd_device_t * device) void dasd_destroy_partitions(dasd_device_t * device) { - struct gendisk *disk = dasd_gendisk_by_dev(device->kdev); - int minor, i; - - if (disk == NULL) - return; - - del_gendisk(disk); + del_gendisk(device->gdp); } int @@ -294,6 +332,7 @@ void dasd_gendisk_exit(void) { struct list_head *l, *n; + spin_lock(&dasd_major_lock); list_for_each_safe(l, n, &dasd_major_info) dasd_unregister_major(list_entry(l, struct major_info, list)); diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 3363a319546f..836b8d5f3da0 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -64,12 +64,12 @@ #include #include -#define CONFIG_DASD_DYNAMIC - /* * SECTION: Type definitions */ -typedef int (*dasd_ioctl_fn_t) (void *inp, int no, long args); +struct dasd_device_t; + +typedef int (*dasd_ioctl_fn_t) (struct block_device *bdev, int no, long args); typedef struct { struct list_head list; @@ -139,9 +139,8 @@ do { \ /* messages to be written via klogd and dbf */ #define DEV_MESSAGE(d_loglevel,d_device,d_string,d_args...)\ do { \ - printk(d_loglevel PRINTK_HEADER " /dev/%-7s(%3d:%3d),%04x@%02x: " \ - d_string "\n", d_device->name, \ - major(d_device->kdev), minor(d_device->kdev), \ + printk(d_loglevel PRINTK_HEADER " %s,%04x@%02x: " \ + d_string "\n", bdevname(d_device->bdev), \ d_device->devinfo.devno, d_device->devinfo.irq, \ d_args); \ DBF_DEV_EVENT(DBF_ALERT, d_device, d_string, d_args); \ @@ -153,8 +152,6 @@ do { \ DBF_EVENT(DBF_ALERT, d_string, d_args); \ } while(0) -struct dasd_device_t; - typedef struct dasd_ccw_req_t { unsigned int magic; /* Eye catcher */ struct list_head list; /* list_head for request queueing. */ @@ -262,7 +259,8 @@ typedef struct dasd_discipline_t { typedef struct dasd_device_t { /* Block device stuff. */ char name[16]; /* The device name in /dev. */ - kdev_t kdev; + struct block_device *bdev; + struct gendisk *gdp; devfs_handle_t devfs_entry; request_queue_t *request_queue; spinlock_t request_queue_lock; @@ -467,6 +465,7 @@ dasd_devmap_t *dasd_devmap_from_devno(int); dasd_devmap_t *dasd_devmap_from_devindex(int); dasd_devmap_t *dasd_devmap_from_irq(int); dasd_devmap_t *dasd_devmap_from_kdev(kdev_t); +dasd_devmap_t *dasd_devmap_from_bdev(struct block_device *bdev); dasd_device_t *dasd_get_device(dasd_devmap_t *); void dasd_put_device(dasd_devmap_t *); @@ -478,9 +477,10 @@ int dasd_add_range(int, int, int); /* externals in dasd_gendisk.c */ int dasd_gendisk_init(void); void dasd_gendisk_exit(void); -int dasd_gendisk_new_major(void); int dasd_gendisk_major_index(int); -struct gendisk *dasd_gendisk_from_devindex(int); +int dasd_gendisk_index_major(int); +struct gendisk *dasd_gendisk_alloc(char *, int); +void dasd_gendisk_free(struct gendisk *); void dasd_setup_partitions(dasd_device_t *); void dasd_destroy_partitions(dasd_device_t *); diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index db3058b18b50..0045c43aed08 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -91,6 +91,7 @@ dasd_ioctl(struct inode *inp, struct file *filp, dasd_devmap_t *devmap; dasd_device_t *device; dasd_ioctl_list_t *ioctl; + struct block_device *bdev; struct list_head *l; const char *dir; int rc; @@ -101,13 +102,17 @@ dasd_ioctl(struct inode *inp, struct file *filp, PRINT_DEBUG("empty data ptr"); return -EINVAL; } - devmap = dasd_devmap_from_kdev(inp->i_rdev); + bdev = bdget(kdev_t_to_nr(inp->i_rdev)); + if (!bdev) + return -EINVAL; + + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) { MESSAGE(KERN_WARNING, - "No device registered as device (%d:%d)", - major(inp->i_rdev), minor(inp->i_rdev)); + "No device registered as device %s", bdevname(bdev)); + bdput(bdev); return -EINVAL; } dir = _IOC_DIR (no) == _IOC_NONE ? "0" : @@ -125,11 +130,12 @@ dasd_ioctl(struct inode *inp, struct file *filp, if (ioctl->owner) { if (try_inc_mod_count(ioctl->owner) != 0) continue; - rc = ioctl->handler(inp, no, data); + rc = ioctl->handler(bdev, no, data); __MOD_DEC_USE_COUNT(ioctl->owner); } else - rc = ioctl->handler(inp, no, data); + rc = ioctl->handler(bdev, no, data); dasd_put_device(devmap); + bdput(bdev); return rc; } } @@ -138,10 +144,12 @@ dasd_ioctl(struct inode *inp, struct file *filp, "unknown ioctl 0x%08x=%s'0x%x'%d(%d) data %8lx", no, dir, _IOC_TYPE(no), _IOC_NR(no), _IOC_SIZE(no), data); dasd_put_device(devmap); + bdput(bdev); return -ENOTTY; } -static int dasd_ioctl_api_version(void *inp, int no, long args) +static int +dasd_ioctl_api_version(struct block_device *bdev, int no, long args) { int ver = DASD_API_VERSION; return put_user(ver, (int *) args); @@ -150,7 +158,8 @@ static int dasd_ioctl_api_version(void *inp, int no, long args) /* * Enable device. */ -static int dasd_ioctl_enable(void *inp, int no, long args) +static int +dasd_ioctl_enable(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; @@ -158,7 +167,7 @@ static int dasd_ioctl_enable(void *inp, int no, long args) if (!capable(CAP_SYS_ADMIN)) return -EACCES; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -172,14 +181,15 @@ static int dasd_ioctl_enable(void *inp, int no, long args) /* * Disable device. */ -static int dasd_ioctl_disable(void *inp, int no, long args) +static int +dasd_ioctl_disable(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -245,7 +255,8 @@ dasd_format(dasd_device_t * device, format_data_t * fdata) /* * Format device. */ -static int dasd_ioctl_format(void *inp, int no, long args) +static int +dasd_ioctl_format(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; @@ -257,8 +268,8 @@ static int dasd_ioctl_format(void *inp, int no, long args) if (!args) return -EINVAL; /* fdata == NULL is no longer a valid arg to dasd_format ! */ - partn = minor(((struct inode *) inp)->i_rdev) & DASD_PARTN_MASK; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + partn = MINOR(bdev->bd_dev) & DASD_PARTN_MASK; + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -283,14 +294,15 @@ static int dasd_ioctl_format(void *inp, int no, long args) /* * Reset device profile information */ -static int dasd_ioctl_reset_profile(void *inp, int no, long args) +static int +dasd_ioctl_reset_profile(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -303,13 +315,14 @@ static int dasd_ioctl_reset_profile(void *inp, int no, long args) /* * Return device profile information */ -static int dasd_ioctl_read_profile(void *inp, int no, long args) +static int +dasd_ioctl_read_profile(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; int rc; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -322,12 +335,14 @@ static int dasd_ioctl_read_profile(void *inp, int no, long args) return rc; } #else -static int dasd_ioctl_reset_profile(void *inp, int no, long args) +static int +dasd_ioctl_reset_profile(struct block_device *bdev, int no, long args) { return -ENOSYS; } -static int dasd_ioctl_read_profile(void *inp, int no, long args) +static int +dasd_ioctl_read_profile(struct block_device *bdev, int no, long args) { return -ENOSYS; } @@ -336,15 +351,16 @@ static int dasd_ioctl_read_profile(void *inp, int no, long args) /* * Return dasd information. Used for BIODASDINFO and BIODASDINFO2. */ -static int dasd_ioctl_information(void *inp, int no, long args) +static int +dasd_ioctl_information(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; - dasd_information2_t dasd_info; + dasd_information2_t *dasd_info; unsigned long flags; int rc; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -354,20 +370,26 @@ static int dasd_ioctl_information(void *inp, int no, long args) return -EINVAL; } - rc = device->discipline->fill_info(device, &dasd_info); + dasd_info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL); + if (dasd_info == NULL) { + dasd_put_device(devmap); + return -ENOMEM; + } + rc = device->discipline->fill_info(device, dasd_info); if (rc) { dasd_put_device(devmap); + kfree(dasd_info); return rc; } - dasd_info.devno = device->devinfo.devno; - dasd_info.schid = device->devinfo.irq; - dasd_info.cu_type = device->devinfo.sid_data.cu_type; - dasd_info.cu_model = device->devinfo.sid_data.cu_model; - dasd_info.dev_type = device->devinfo.sid_data.dev_type; - dasd_info.dev_model = device->devinfo.sid_data.dev_model; - dasd_info.open_count = atomic_read(&device->open_count); - dasd_info.status = device->state; + dasd_info->devno = device->devinfo.devno; + dasd_info->schid = device->devinfo.irq; + dasd_info->cu_type = device->devinfo.sid_data.cu_type; + dasd_info->cu_model = device->devinfo.sid_data.cu_model; + dasd_info->dev_type = device->devinfo.sid_data.dev_type; + dasd_info->dev_model = device->devinfo.sid_data.dev_model; + dasd_info->open_count = atomic_read(&device->open_count); + dasd_info->status = device->state; /* * check if device is really formatted @@ -375,16 +397,16 @@ static int dasd_ioctl_information(void *inp, int no, long args) */ if ((device->state < DASD_STATE_READY) || (dasd_check_blocksize(device->bp_block))) - dasd_info.format = DASD_FORMAT_NONE; + dasd_info->format = DASD_FORMAT_NONE; - dasd_info.features = devmap->features; + dasd_info->features = devmap->features; if (device->discipline) - memcpy(dasd_info.type, device->discipline->name, 4); + memcpy(dasd_info->type, device->discipline->name, 4); else - memcpy(dasd_info.type, "none", 4); - dasd_info.req_queue_len = 0; - dasd_info.chanq_len = 0; + memcpy(dasd_info->type, "none", 4); + dasd_info->req_queue_len = 0; + dasd_info->chanq_len = 0; if (device->request_queue->request_fn) { struct list_head *l; #ifdef DASD_EXTENDED_PROFILING @@ -392,45 +414,46 @@ static int dasd_ioctl_information(void *inp, int no, long args) struct list_head *l; spin_lock_irqsave(&device->lock, flags); list_for_each(l, &device->request_queue->queue_head) - dasd_info.req_queue_len++; + dasd_info->req_queue_len++; spin_unlock_irqrestore(&device->lock, flags); } #endif /* DASD_EXTENDED_PROFILING */ spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags); list_for_each(l, &device->ccw_queue) - dasd_info.chanq_len++; + dasd_info->chanq_len++; spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags); } rc = 0; - if (copy_to_user((long *) args, (long *) &dasd_info, + if (copy_to_user((long *) args, (long *) dasd_info, ((no == (unsigned int) BIODASDINFO2) ? sizeof (dasd_information2_t) : sizeof (dasd_information_t)))) rc = -EFAULT; dasd_put_device(devmap); + kfree(dasd_info); return rc; } /* * Set read only */ -static int dasd_ioctl_set_ro(void *inp, int no, long args) +static int +dasd_ioctl_set_ro(struct block_device *bdev, int no, long args) { dasd_devmap_t *devmap; dasd_device_t *device; - int major, minor; int intval, i; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (minor(((struct inode *) inp)->i_rdev) & DASD_PARTN_MASK) + if (MINOR(bdev->bd_dev) & DASD_PARTN_MASK) // ro setting is not allowed for partitions return -EINVAL; if (get_user(intval, (int *) args)) return -EFAULT; - devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -439,10 +462,8 @@ static int dasd_ioctl_set_ro(void *inp, int no, long args) devmap->features |= DASD_FEATURE_READONLY; else devmap->features &= ~DASD_FEATURE_READONLY; - major = major(device->kdev); - minor = minor(device->kdev); for (i = 0; i < (1 << DASD_PARTN_BITS); i++) - set_device_ro(mk_kdev(major, minor + i), intval); + set_device_ro(to_kdev_t(bdev->bd_dev + i), intval); dasd_put_device(devmap); return 0; } @@ -450,16 +471,15 @@ static int dasd_ioctl_set_ro(void *inp, int no, long args) /* * Return disk geometry. */ -static int dasd_ioctl_getgeo(void *inp, int no, long args) +static int +dasd_ioctl_getgeo(struct block_device *bdev, int no, long args) { struct hd_geometry geo = { 0, }; - struct inode *inode = inp; dasd_devmap_t *devmap; dasd_device_t *device; - kdev_t kdev = inode->i_rdev; int rc; - devmap = dasd_devmap_from_kdev(kdev); + devmap = dasd_devmap_from_bdev(bdev); device = (devmap != NULL) ? dasd_get_device(devmap) : ERR_PTR(-ENODEV); if (IS_ERR(device)) @@ -468,7 +488,7 @@ static int dasd_ioctl_getgeo(void *inp, int no, long args) if (device != NULL && device->discipline != NULL && device->discipline->fill_geometry != NULL) { device->discipline->fill_geometry(device, &geo); - geo.start = get_start_sect(inode->i_bdev); + geo.start = get_start_sect(bdev); if (copy_to_user((struct hd_geometry *) args, &geo, sizeof (struct hd_geometry))) rc = -EFAULT; diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 5106b5ed6a13..2aea6349e2f5 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -123,7 +124,7 @@ dasd_devices_write(struct file *file, const char *user_buf, } features = dasd_feature_list(str, &str); /* Negative numbers in from/to/features indicate errors */ - if (from < 0 || to < 0 || features < 0) + if (from < 0 || to < 0 || from > 65546 || to > 65536 || features < 0) goto out_error; if (add_or_set == 0) { @@ -152,10 +153,8 @@ static inline int dasd_devices_print(dasd_devmap_t *devmap, char *str) { dasd_device_t *device; - struct gendisk *gdp; - char buffer[7]; char *substr; - int minor; + int major, minor; int len; device = dasd_get_device(devmap); @@ -169,11 +168,11 @@ dasd_devices_print(dasd_devmap_t *devmap, char *str) else len += sprintf(str + len, "(none)"); /* Print kdev. */ - gdp = dasd_gendisk_from_devindex(devmap->devindex); - minor = devmap->devindex % DASD_PER_MAJOR; - len += sprintf(str + len, " at (%3d:%3d)", gdp->major, minor); + major = MAJOR(device->bdev->bd_dev); + minor = MINOR(device->bdev->bd_dev); + len += sprintf(str + len, " at (%3d:%3d)", major, minor); /* Print device name. */ - len += sprintf(str + len, " is %-7s", gdp->disk_name); + len += sprintf(str + len, " is %-7s", device->name); /* Print devices features. */ substr = (devmap->features & DASD_FEATURE_READONLY) ? "(ro)" : " "; len += sprintf(str + len, "%4s: ", substr); diff --git a/include/asm-s390/dasd.h b/include/asm-s390/dasd.h index 1ea18f182184..d536b229bfc9 100644 --- a/include/asm-s390/dasd.h +++ b/include/asm-s390/dasd.h @@ -13,6 +13,8 @@ * 12/06/01 DASD_API_VERSION 2 - binary compatible to 0 (new BIODASDINFO2) * 01/23/02 DASD_API_VERSION 3 - added BIODASDPSRD (and BIODASDENAPAV) IOCTL * 02/15/02 DASD_API_VERSION 4 - added BIODASDSATTR IOCTL + * ##/##/## DASD_API_VERSION 5 - added boxed dasd support TOBEDONE + * 21/06/02 DASD_API_VERSION 6 - fixed HDIO_GETGEO: geo.start is in sectors! * */ @@ -22,7 +24,7 @@ #define DASD_IOCTL_LETTER 'D' -#define DASD_API_VERSION 4 +#define DASD_API_VERSION 6 /* * struct dasd_information2_t diff --git a/include/asm-s390x/dasd.h b/include/asm-s390x/dasd.h index 1ea18f182184..d536b229bfc9 100644 --- a/include/asm-s390x/dasd.h +++ b/include/asm-s390x/dasd.h @@ -13,6 +13,8 @@ * 12/06/01 DASD_API_VERSION 2 - binary compatible to 0 (new BIODASDINFO2) * 01/23/02 DASD_API_VERSION 3 - added BIODASDPSRD (and BIODASDENAPAV) IOCTL * 02/15/02 DASD_API_VERSION 4 - added BIODASDSATTR IOCTL + * ##/##/## DASD_API_VERSION 5 - added boxed dasd support TOBEDONE + * 21/06/02 DASD_API_VERSION 6 - fixed HDIO_GETGEO: geo.start is in sectors! * */ @@ -22,7 +24,7 @@ #define DASD_IOCTL_LETTER 'D' -#define DASD_API_VERSION 4 +#define DASD_API_VERSION 6 /* * struct dasd_information2_t -- cgit v1.2.3 From 8bf69f6f79bd32f723777fb8eb70d7c8cbc04e1c Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 3 Oct 2002 21:46:52 -0700 Subject: [PATCH] s390 update (10/27): bitops bug. Fix broken bitops for unaligned atomic operations on s390. --- include/asm-s390/bitops.h | 10 +++++----- include/asm-s390x/bitops.h | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/asm-s390/bitops.h b/include/asm-s390/bitops.h index 29f287dd9f87..b848a9dd86af 100644 --- a/include/asm-s390/bitops.h +++ b/include/asm-s390/bitops.h @@ -59,8 +59,8 @@ static inline void set_bit_cs(int nr, volatile void *ptr) addr = (unsigned long) ptr; #if ALIGN_CS == 1 - addr ^= addr & 3; /* align address to 4 */ nr += (addr & 3) << 3; /* add alignment to bit number */ + addr ^= addr & 3; /* align address to 4 */ #endif addr += (nr ^ (nr & 31)) >> 3; /* calculate address for CS */ mask = 1UL << (nr & 31); /* make OR mask */ @@ -84,8 +84,8 @@ static inline void clear_bit_cs(int nr, volatile void *ptr) addr = (unsigned long) ptr; #if ALIGN_CS == 1 - addr ^= addr & 3; /* align address to 4 */ nr += (addr & 3) << 3; /* add alignment to bit number */ + addr ^= addr & 3; /* align address to 4 */ #endif addr += (nr ^ (nr & 31)) >> 3; /* calculate address for CS */ mask = ~(1UL << (nr & 31)); /* make AND mask */ @@ -109,8 +109,8 @@ static inline void change_bit_cs(int nr, volatile void *ptr) addr = (unsigned long) ptr; #if ALIGN_CS == 1 - addr ^= addr & 3; /* align address to 4 */ nr += (addr & 3) << 3; /* add alignment to bit number */ + addr ^= addr & 3; /* align address to 4 */ #endif addr += (nr ^ (nr & 31)) >> 3; /* calculate address for CS */ mask = 1UL << (nr & 31); /* make XOR mask */ @@ -160,8 +160,8 @@ static inline int test_and_clear_bit_cs(int nr, volatile void *ptr) addr = (unsigned long) ptr; #if ALIGN_CS == 1 - addr ^= addr & 3; /* align address to 4 */ nr += (addr & 3) << 3; /* add alignment to bit number */ + addr ^= addr & 3; /* align address to 4 */ #endif addr += (nr ^ (nr & 31)) >> 3; /* calculate address for CS */ mask = ~(1UL << (nr & 31)); /* make AND mask */ @@ -186,8 +186,8 @@ static inline int test_and_change_bit_cs(int nr, volatile void *ptr) addr = (unsigned long) ptr; #if ALIGN_CS == 1 - addr ^= addr & 3; /* align address to 4 */ nr += (addr & 3) << 3; /* add alignment to bit number */ + addr ^= addr & 3; /* align address to 4 */ #endif addr += (nr ^ (nr & 31)) >> 3; /* calculate address for CS */ mask = 1UL << (nr & 31); /* make XOR mask */ diff --git a/include/asm-s390x/bitops.h b/include/asm-s390x/bitops.h index ac889a324bbb..8e95aa43780d 100644 --- a/include/asm-s390x/bitops.h +++ b/include/asm-s390x/bitops.h @@ -63,8 +63,8 @@ static inline void set_bit_cs(unsigned long nr, volatile void *ptr) addr = (unsigned long) ptr; #if ALIGN_CS == 1 - addr ^= addr & 7; /* align address to 8 */ nr += (addr & 7) << 3; /* add alignment to bit number */ + addr ^= addr & 7; /* align address to 8 */ #endif addr += (nr ^ (nr & 63)) >> 3; /* calculate address for CS */ mask = 1UL << (nr & 63); /* make OR mask */ @@ -88,8 +88,8 @@ static inline void clear_bit_cs(unsigned long nr, volatile void *ptr) addr = (unsigned long) ptr; #if ALIGN_CS == 1 - addr ^= addr & 7; /* align address to 8 */ nr += (addr & 7) << 3; /* add alignment to bit number */ + addr ^= addr & 7; /* align address to 8 */ #endif addr += (nr ^ (nr & 63)) >> 3; /* calculate address for CS */ mask = ~(1UL << (nr & 63)); /* make AND mask */ @@ -113,8 +113,8 @@ static inline void change_bit_cs(unsigned long nr, volatile void *ptr) addr = (unsigned long) ptr; #if ALIGN_CS == 1 - addr ^= addr & 7; /* align address to 8 */ nr += (addr & 7) << 3; /* add alignment to bit number */ + addr ^= addr & 7; /* align address to 8 */ #endif addr += (nr ^ (nr & 63)) >> 3; /* calculate address for CS */ mask = 1UL << (nr & 63); /* make XOR mask */ @@ -139,8 +139,8 @@ test_and_set_bit_cs(unsigned long nr, volatile void *ptr) addr = (unsigned long) ptr; #if ALIGN_CS == 1 - addr ^= addr & 7; /* align address to 8 */ nr += (addr & 7) << 3; /* add alignment to bit number */ + addr ^= addr & 7; /* align address to 8 */ #endif addr += (nr ^ (nr & 63)) >> 3; /* calculate address for CS */ mask = 1UL << (nr & 63); /* make OR/test mask */ @@ -166,8 +166,8 @@ test_and_clear_bit_cs(unsigned long nr, volatile void *ptr) addr = (unsigned long) ptr; #if ALIGN_CS == 1 - addr ^= addr & 7; /* align address to 8 */ nr += (addr & 7) << 3; /* add alignment to bit number */ + addr ^= addr & 7; /* align address to 8 */ #endif addr += (nr ^ (nr & 63)) >> 3; /* calculate address for CS */ mask = ~(1UL << (nr & 63)); /* make AND mask */ @@ -193,8 +193,8 @@ test_and_change_bit_cs(unsigned long nr, volatile void *ptr) addr = (unsigned long) ptr; #if ALIGN_CS == 1 - addr ^= addr & 7; /* align address to 8 */ nr += (addr & 7) << 3; /* add alignment to bit number */ + addr ^= addr & 7; /* align address to 8 */ #endif addr += (nr ^ (nr & 63)) >> 3; /* calculate address for CS */ mask = 1UL << (nr & 63); /* make XOR mask */ -- cgit v1.2.3 From b2a749f0cb5518b48b791ac63bab61c5d767478f Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 3 Oct 2002 21:47:37 -0700 Subject: [PATCH] s390 update (13/27): preemption support. Add support for kernel preemption on s390/s390x. --- arch/s390/config.in | 1 + arch/s390/defconfig | 1 + arch/s390/kernel/entry.S | 170 +++++++++++++++++++++++----------------- arch/s390x/config.in | 1 + arch/s390x/defconfig | 1 + arch/s390x/kernel/entry.S | 151 +++++++++++++++++++++-------------- include/asm-s390/hardirq.h | 12 ++- include/asm-s390/softirq.h | 2 + include/asm-s390/thread_info.h | 6 +- include/asm-s390x/hardirq.h | 12 ++- include/asm-s390x/thread_info.h | 6 +- 11 files changed, 221 insertions(+), 142 deletions(-) (limited to 'include') diff --git a/arch/s390/config.in b/arch/s390/config.in index 82ed7d7991a2..01ea49a84023 100644 --- a/arch/s390/config.in +++ b/arch/s390/config.in @@ -30,6 +30,7 @@ if [ "$CONFIG_QDIO" != "n" ]; then fi comment 'Misc' +bool 'Preemptible Kernel' CONFIG_PREEMPT bool 'Builtin IPL record support' CONFIG_IPL if [ "$CONFIG_IPL" = "y" ]; then choice 'IPL method generated into head.S' \ diff --git a/arch/s390/defconfig b/arch/s390/defconfig index c311a7507bb5..f565806d4fd2 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -50,6 +50,7 @@ CONFIG_QDIO=m # # Misc # +# CONFIG_PREEMPT is not set CONFIG_IPL=y # CONFIG_IPL_TAPE is not set CONFIG_IPL_VM=y diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index a5553e7bdde2..fe8abc26f1df 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -48,7 +48,7 @@ SP_ORIG_R2 = STACK_FRAME_OVERHEAD + PT_ORIGGPR2 SP_TRAP = (SP_ORIG_R2+GPR_SIZE) SP_SIZE = (SP_TRAP+4) -_TIF_WORK_MASK = (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED) +_TIF_WORK_MASK = (_TIF_SIGPENDING | _TIF_NEED_RESCHED) /* * Base Address of this Module --- saved in __LC_ENTRY_BASE @@ -198,33 +198,44 @@ sysc_leave: RESTORE_ALL 1 # -# One of the work bits _TIF_NOTIFY_RESUME, _TIF_SIGPENDING or -# _TIF_NEED_RESCHED is on. Find out which one. +# recheck if there is more work to do +# +sysc_work_loop: + stnsm 24(%r15),0xfc # disable I/O and ext. interrupts + GET_THREAD_INFO # load pointer to task_struct to R9 + tm __TI_flags+3(%r9),_TIF_WORK_MASK + bz BASED(sysc_leave) # there is no work to do +# +# One of the work bits is on. Find out which one. +# Checked are: _TIF_SIGPENDING and _TIF_NEED_RESCHED # sysc_work: - tm SP_PSW+1(%r15),0x01 # returning to user ? - bno BASED(sysc_leave) # no-> skip resched & signal tm __TI_flags+3(%r9),_TIF_NEED_RESCHED bo BASED(sysc_reschedule) - # add a test for TIF_NOTIFY_RESUME here when it is used. - # _TIF_SIGPENDING is the only flag left -# -# call do_signal before return -# -sysc_signal_return: - la %r2,SP_PTREGS(%r15) # load pt_regs - sr %r3,%r3 # clear *oldset - l %r1,BASED(.Ldo_signal) - la %r14,BASED(sysc_return) - br %r1 # return point is sysc_return + tm __TI_flags+3(%r9),_TIF_SIGPENDING + bo BASED(sysc_sigpending) + b BASED(sysc_leave) # -# call schedule with sysc_return as return-address -# +# _TIF_NEED_RESCHED is set, call schedule +# sysc_reschedule: + stosm 24(%r15),0x03 # reenable interrupts l %r1,BASED(.Lschedule) - la %r14,BASED(sysc_return) - br %r1 # call scheduler, return to sysc_return + la %r14,BASED(sysc_work_loop) + br %r1 # call scheduler + +# +# _TIF_SIGPENDING is set, call do_signal +# +sysc_sigpending: + stosm 24(%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 + b BASED(sysc_leave) # out of here, do NOT recheck # # call trace before and after sys_call @@ -257,9 +268,7 @@ ret_from_fork: basr %r13,0 l %r13,.Lentry_base-.(%r13) # setup base pointer to &entry_base GET_THREAD_INFO # load pointer to task_struct to R9 - sr %r0,%r0 # child returns 0 - st %r0,SP_R2(%r15) # store return value (change R2 on stack) -#ifdef CONFIG_SMP +#if CONFIG_SMP || CONFIG_PREEMPT l %r1,BASED(.Lschedtail) la %r14,BASED(sysc_return) br %r1 # call schedule_tail, return to sysc_return @@ -615,13 +624,15 @@ pgm_check_handler: tm __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception bnz BASED(pgm_per) # got per exception -> special case SAVE_ALL __LC_PGM_OLD_PSW,1 + la %r8,0x7f + l %r3,__LC_PGM_ILC # load program interruption code l %r7,BASED(.Ljump_table) - lh %r8,__LC_PGM_INT_CODE + nr %r8,%r3 sll %r8,2 GET_THREAD_INFO + stosm 24(%r15),0x03 # reenable interrupts l %r7,0(%r8,%r7) # load address of handler routine la %r2,SP_PTREGS(%r15) # address of register-save area - l %r3,__LC_PGM_ILC # load program interruption code la %r14,BASED(sysc_return) br %r7 # branch to interrupt-handler @@ -646,6 +657,7 @@ pgm_per_std: GET_THREAD_INFO la %r4,0x7f l %r3,__LC_PGM_ILC # load program interruption code + stosm 24(%r15),0x03 # reenable interrupts nr %r4,%r3 # clear per-event-bit and ilc be BASED(pgm_per_only) # only per or per+check ? l %r1,BASED(.Ljump_table) @@ -665,9 +677,9 @@ pgm_per_only: pgm_svcper: SAVE_ALL __LC_SVC_OLD_PSW,1 GET_THREAD_INFO # load pointer to task_struct to R9 + stosm 24(%r15),0x03 # reenable interrupts lh %r8,0x8a # get svc number from lowcore sll %r8,2 - stosm 24(%r15),0x03 # reenable interrupts l %r8,sys_call_table-entry_base(%r8,%r13) # get system call addr. tm __TI_flags+3(%r9),_TIF_SYSCALL_TRACE bo BASED(pgm_tracesys) @@ -731,59 +743,81 @@ io_int_handler: basr %r14,%r1 # branch to standard irq handler io_return: -# -# check, if bottom-half has to be done -# - l %r1,__TI_cpu(%r9) - sll %r1,L1_CACHE_SHIFT - al %r1,BASED(.Lirq_stat) # get address of irq_stat - icm %r0,15,0(%r1) # test irq_stat[#cpu].__softirq_pending - bnz BASED(io_handle_bottom_half) -io_return_bh: + tm SP_PSW+1(%r15),0x01 # returning to user ? +#ifdef CONFIG_PREEMPT + bno BASED(io_preempt) # no -> check for preemptive scheduling +#else + bno BASED(io_leave) # no-> skip resched & signal +#endif tm __TI_flags+3(%r9),_TIF_WORK_MASK bnz BASED(io_work) # there is work to do (signals etc.) io_leave: - stnsm 24(%r15),0xfc # disable I/O and ext. interrupts RESTORE_ALL 0 +#ifdef CONFIG_PREEMPT +io_preempt: + icm %r0,15,__TI_precount(%r9) + bnz BASED(io_leave) +io_resume_loop: + tm __TI_flags+3(%r9),_TIF_NEED_RESCHED + bno BASED(io_leave) + mvc __TI_precount(4,%r9),.Lc_pactive + # hmpf, we are on the async. stack but to call schedule + # we have to move the interrupt frame to the process stack + l %r1,SP_R15(%r15) + s %r1,BASED(.Lc_spsize) + n %r1,BASED(.Lc0xfffffff8) + mvc SP_PTREGS(SP_SIZE-SP_PTREGS,%r1),SP_PTREGS(%r15) + xc 0(4,%r1),0(%r1) # clear back chain + lr %r15,%r1 + stosm 24(%r15),0x03 # reenable interrupts + l %r1,BASED(.Lschedule) + basr %r14,%r1 # call schedule + stnsm 24(%r15),0xfc # disable I/O and ext. interrupts + GET_THREAD_INFO # load pointer to task_struct to R9 + xc __TI_precount(4,%r9),__TI_precount(%r9) + b BASED(io_resume_loop) +#endif + # -# call do_softirq +# recheck if there is more work to do # -io_handle_bottom_half: - l %r1,BASED(.Ldo_softirq) - la %r14,BASED(io_return_bh) - br %r1 # call do_softirq - +io_work_loop: + stnsm 24(%r15),0xfc # disable I/O and ext. interrupts + GET_THREAD_INFO # load pointer to task_struct to R9 + tm __TI_flags+3(%r9),_TIF_WORK_MASK + bz BASED(io_leave) # there is no work to do # -# One of the work bits _TIF_NOTIFY_RESUME, _TIF_SIGPENDING or -# _TIF_NEED_RESCHED is on. Find out which one. +# One of the work bits is on. Find out which one. +# Checked are: _TIF_SIGPENDING and _TIF_NEED_RESCHED # io_work: - tm SP_PSW+1(%r15),0x01 # returning to user ? - bno BASED(io_leave) # no-> skip resched & signal - stosm 24(%r15),0x03 # reenable interrupts tm __TI_flags+3(%r9),_TIF_NEED_RESCHED bo BASED(io_reschedule) - # add a test for TIF_NOTIFY_RESUME here when it is used. - # _TIF_SIGPENDING is the only flag left + tm __TI_flags+3(%r9),_TIF_SIGPENDING + bo BASED(io_sigpending) + b BASED(io_leave) # -# call do_signal before return -# -io_signal_return: - la %r2,SP_PTREGS(%r15) # load pt_regs - sr %r3,%r3 # clear *oldset - l %r1,BASED(.Ldo_signal) - la %r14,BASED(io_leave) - br %r1 # return point is io_leave +# _TIF_NEED_RESCHED is set, call schedule +# +io_reschedule: + stosm 24(%r15),0x03 # reenable interrupts + l %r1,BASED(.Lschedule) + la %r14,BASED(io_work_loop) + br %r1 # call scheduler # -# call schedule with io_return as return-address +# _TIF_SIGPENDING is set, call do_signal # -io_reschedule: - l %r1,BASED(.Lschedule) - la %r14,BASED(io_return) - br %r1 # call scheduler, return to io_return +io_sigpending: + stosm 24(%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 + b BASED(io_leave) # out of here, do NOT recheck /* * External interrupt handler routine @@ -864,19 +898,12 @@ restart_go: .align 4 .Lc0xfffffff8: .long -8 # to align stack pointer to 8 .Lc0xffffe000: .long -8192 # to round stack pointer to &task_struct -.Lc8191: .long 8191 .Lc_spsize: .long SP_SIZE .Lc_overhead: .long STACK_FRAME_OVERHEAD .Lc_ac: .long 0,0,1 .Lc_ENOSYS: .long -ENOSYS -.Lc4: .long 4 -.Lc20: .long 20 -.Lc0x1202: .long 0x1202 -.Lc0x1004: .long 0x1004 -.Lc0x2401: .long 0x2401 -.Lc0x4000: .long 0x4000 +.Lc_pactive: .long PREEMPT_ACTIVE .Lc0xff: .long 0xff -.Lc128: .long 128 .Lc256: .long 256 /* @@ -889,7 +916,6 @@ restart_go: .Lentry_base: .long entry_base .Lext_hash: .long ext_int_hash .Lhandle_per: .long handle_per_exception -.Lirq_stat: .long irq_stat .Ljump_table: .long pgm_check_table .Lschedule: .long schedule .Lclone: .long sys_clone @@ -903,7 +929,7 @@ restart_go: .Lsigaltstack: .long sys_sigaltstack .Ltrace: .long syscall_trace .Lvfork: .long sys_vfork -#ifdef CONFIG_SMP +#if CONFIG_SMP || CONFIG_PREEMPT .Lschedtail: .long schedule_tail #endif diff --git a/arch/s390x/config.in b/arch/s390x/config.in index f488eb597ab3..20cc9e5e1c38 100644 --- a/arch/s390x/config.in +++ b/arch/s390x/config.in @@ -33,6 +33,7 @@ if [ "$CONFIG_QDIO" != "n" ]; then fi comment 'Misc' +bool 'Preemptible Kernel' CONFIG_PREEMPT bool 'Builtin IPL record support' CONFIG_IPL if [ "$CONFIG_IPL" = "y" ]; then choice 'IPL method generated into head.S' \ diff --git a/arch/s390x/defconfig b/arch/s390x/defconfig index 4622cc96635f..8d203e5bc54b 100644 --- a/arch/s390x/defconfig +++ b/arch/s390x/defconfig @@ -51,6 +51,7 @@ CONFIG_QDIO=y # # Misc # +# CONFIG_PREEMPT is not set CONFIG_IPL=y # CONFIG_IPL_TAPE is not set CONFIG_IPL_VM=y diff --git a/arch/s390x/kernel/entry.S b/arch/s390x/kernel/entry.S index 387aebfc0c2e..c16422ccc5e6 100644 --- a/arch/s390x/kernel/entry.S +++ b/arch/s390x/kernel/entry.S @@ -48,7 +48,7 @@ SP_ORIG_R2 = STACK_FRAME_OVERHEAD + PT_ORIGGPR2 SP_TRAP = (SP_ORIG_R2+GPR_SIZE) SP_SIZE = (SP_TRAP+4) -_TIF_WORK_MASK = (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED) +_TIF_WORK_MASK = (_TIF_SIGPENDING | _TIF_NEED_RESCHED) /* * Register usage in interrupt handlers: @@ -184,32 +184,42 @@ sysc_leave: RESTORE_ALL 1 # -# One of the work bits _TIF_NOTIFY_RESUME, _TIF_SIGPENDING or -# _TIF_NEED_RESCHED is on. Find out which one. +# recheck if there is more work to do +# +sysc_work_loop: + stnsm 48(%r15),0xfc # disable I/O and ext. interrupts + GET_THREAD_INFO # load pointer to task_struct to R9 + tm __TI_flags+7(%r9),_TIF_WORK_MASK + jz sysc_leave # there is no work to do +# +# One of the work bits is on. Find out which one. +# Checked are: _TIF_SIGPENDING and _TIF_NEED_RESCHED # sysc_work: - tm SP_PSW+1(%r15),0x01 # returning to user ? - jno sysc_leave # no-> skip resched & signal tm __TI_flags+7(%r9),_TIF_NEED_RESCHED jo sysc_reschedule - # add a test for TIF_NOTIFY_RESUME here when it is used. - # _TIF_SIGPENDING is the only flag left + tm __TI_flags+7(%r9),_TIF_SIGPENDING + jo sysc_sigpending + j sysc_leave # -# call do_signal before return -# -sysc_signal_return: - la %r2,SP_PTREGS(%r15) # load pt_regs - sgr %r3,%r3 # clear *oldset - larl %r14,sysc_return - jg do_signal # return point is sysc_return +# _TIF_NEED_RESCHED is set, call schedule +# +sysc_reschedule: + stosm 48(%r15),0x03 # reenable interrupts + larl %r14,sysc_work_loop + jg schedule # return point is sysc_return # -# call schedule with sysc_return as return-address +# _TIF_SIGPENDING is set, call do_signal # -sysc_reschedule: - larl %r14,sysc_return - jg schedule # return point is sysc_return +sysc_sigpending: + stosm 48(%r15),0x03 # reenable interrupts + la %r2,SP_PTREGS(%r15) # load pt_regs + sgr %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 # # call syscall_trace before and after system call @@ -241,8 +251,7 @@ sysc_tracego: .globl ret_from_fork ret_from_fork: GET_THREAD_INFO # load pointer to task_struct to R9 - xc SP_R2(8,%r15),SP_R2(%r15) # child returns 0 -#ifdef CONFIG_SMP +#if CONFIG_SMP || CONFIG_PREEMPT larl %r14,sysc_return jg schedule_tail # return to sysc_return #else @@ -550,8 +559,8 @@ sys_call_table: .long SYSCALL(sys_rt_sigtimedwait,sys32_rt_sigtimedwait_wrapper) .long SYSCALL(sys_rt_sigqueueinfo,sys32_rt_sigqueueinfo_wrapper) .long SYSCALL(sys_rt_sigsuspend_glue,sys32_rt_sigsuspend_glue) - .long SYSCALL(sys_pread64,sys32_pread_wrapper) /* 180 */ - .long SYSCALL(sys_pwrite64,sys32_pwrite_wrapper) + .long SYSCALL(sys_pread64,sys32_pread64_wrapper) /* 180 */ + .long SYSCALL(sys_pwrite64,sys32_pwrite64_wrapper) .long SYSCALL(sys_ni_syscall,sys32_chown16_wrapper) /* old chown16 syscall */ .long SYSCALL(sys_getcwd,sys32_getcwd_wrapper) .long SYSCALL(sys_capget,sys32_capget_wrapper) @@ -645,13 +654,15 @@ pgm_check_handler: tm __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception jnz pgm_per # got per exception -> special case SAVE_ALL __LC_PGM_OLD_PSW,1 - llgh %r8,__LC_PGM_INT_CODE + lghi %r8,0x7f + lgf %r3,__LC_PGM_ILC # load program interruption code + ngr %r8,%r3 sll %r8,3 GET_THREAD_INFO + stosm 48(%r15),0x03 # reenable interrupts larl %r1,pgm_check_table lg %r1,0(%r8,%r1) # load address of handler routine la %r2,SP_PTREGS(%r15) # address of register-save area - lgf %r3,__LC_PGM_ILC # load program interruption code larl %r14,sysc_return br %r1 # branch to interrupt-handler @@ -675,6 +686,7 @@ pgm_per_std: GET_THREAD_INFO lghi %r4,0x7f lgf %r3,__LC_PGM_ILC # load program interruption code + stosm 48(%r15),0x03 # reenable interrupts nr %r4,%r3 # clear per-event-bit and ilc je pgm_per_only # only per of per+check ? sll %r4,3 @@ -758,57 +770,79 @@ io_int_handler: brasl %r14,do_IRQ # call standard irq handler io_return: -# -# check, if bottom-half has to be done -# - lgf %r1,__TI_cpu(%r9) - larl %r2,irq_stat - sll %r1,L1_CACHE_SHIFT - la %r1,0(%r1,%r2) - icm %r0,15,0(%r1) # test irq_stat[#cpu].__softirq_pending - jnz io_handle_bottom_half -io_return_bh: + tm SP_PSW+1(%r15),0x01 # returning to user ? +#ifdef CONFIG_PREEMPT + jno io_preempt # no -> check for preemptive scheduling +#else + jno io_leave # no-> skip resched & signal +#endif tm __TI_flags+7(%r9),_TIF_WORK_MASK jnz io_work # there is work to do (signals etc.) io_leave: - stnsm 48(%r15),0xfc # disable I/O and ext. interrupts RESTORE_ALL 0 +#ifdef CONFIG_PREEMPT +io_preempt: + icm %r0,15,__TI_precount(%r9) + jnz io_leave +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) + # hmpf, we are on the async. stack but to call schedule + # we have to move the interrupt frame to the process stack + lg %r1,SP_R15(%r15) + aghi %r1,-SP_SIZE + nill %r1,0xfff8 + mvc SP_PTREGS(SP_SIZE-SP_PTREGS,%r1),SP_PTREGS(%r15) + xc 0(8,%r1),0(%r1) # clear back chain + lgr %r15,%r1 + stosm 48(%r15),0x03 # reenable interrupts + brasl %r14,schedule # call schedule + stnsm 48(%r15),0xfc # disable I/O and ext. interrupts + GET_THREAD_INFO # load pointer to task_struct to R9 + xc __TI_precount(4,%r9),__TI_precount(%r9) + j io_resume_loop +#endif + # -# call do_softirq +# recheck if there is more work to do # -io_handle_bottom_half: - larl %r14,io_return_bh - jg do_softirq # return point is io_return_bh - +io_work_loop: + stnsm 48(%r15),0xfc # disable I/O and ext. interrupts + GET_THREAD_INFO # load pointer to task_struct to R9 + tm __TI_flags+7(%r9),_TIF_WORK_MASK + jz io_leave # there is no work to do # -# One of the work bits _TIF_NOTIFY_RESUME, _TIF_SIGPENDING or -# _TIF_NEED_RESCHED is on. Find out which one. +# One of the work bits is on. Find out which one. +# Checked are: _TIF_SIGPENDING and _TIF_NEED_RESCHED # io_work: - tm SP_PSW+1(%r15),0x01 # returning to user ? - jno io_leave # no-> skip resched & signal - stosm 48(%r15),0x03 # reenable interrupts tm __TI_flags+7(%r9),_TIF_NEED_RESCHED jo io_reschedule - # add a test for TIF_NOTIFY_RESUME here when it is used. - # _TIF_SIGPENDING is the only flag left + tm __TI_flags+7(%r9),_TIF_SIGPENDING + jo io_sigpending + j io_leave # -# call do_signal before return -# -io_signal_return: - la %r2,SP_PTREGS(%r15) # load pt_regs - slgr %r3,%r3 # clear *oldset - larl %r14,io_leave - jg do_signal # return point is io_leave +# _TIF_NEED_RESCHED is set, call schedule +# +io_reschedule: + stosm 48(%r15),0x03 # reenable interrupts + larl %r14,io_work_loop + jg schedule # call scheduler # -# call schedule with io_return as return-address +# _TIF_SIGPENDING is set, call do_signal # -io_reschedule: - larl %r14,io_return - jg schedule # call scheduler, return to io_return +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 /* * External interrupt handler routine @@ -882,4 +916,5 @@ restart_go: */ .align 4 .Lc_ac: .long 0,0,1 +.Lc_pactive: .long PREEMPT_ACTIVE .Lc256: .quad 256 diff --git a/include/asm-s390/hardirq.h b/include/asm-s390/hardirq.h index a8b241ec8e9e..7f38c6f01e05 100644 --- a/include/asm-s390/hardirq.h +++ b/include/asm-s390/hardirq.h @@ -80,15 +80,21 @@ typedef struct { extern void do_call_softirq(void); -#define in_atomic() (preempt_count() != 0) -#define IRQ_EXIT_OFFSET HARDIRQ_OFFSET +#if CONFIG_PREEMPT +# define in_atomic() (in_interrupt() || preempt_count() == PREEMPT_ACTIVE) +# 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() -= HARDIRQ_OFFSET; \ + preempt_count() -= IRQ_EXIT_OFFSET; \ if (!in_interrupt() && softirq_pending(smp_processor_id())) \ /* Use the async. stack for softirq */ \ do_call_softirq(); \ + preempt_enable_no_resched(); \ } while (0) #ifndef CONFIG_SMP diff --git a/include/asm-s390/softirq.h b/include/asm-s390/softirq.h index 25d7b42c426f..91f9853561dd 100644 --- a/include/asm-s390/softirq.h +++ b/include/asm-s390/softirq.h @@ -10,6 +10,7 @@ #define __ASM_SOFTIRQ_H #include +#include #include #include @@ -28,6 +29,7 @@ do { \ if (!in_interrupt() && softirq_pending(smp_processor_id())) \ /* Use the async. stack for softirq */ \ do_call_softirq(); \ + preempt_check_resched(); \ } while (0) #endif /* __ASM_SOFTIRQ_H */ diff --git a/include/asm-s390/thread_info.h b/include/asm-s390/thread_info.h index 329f401b52c0..26a7c4d21be8 100644 --- a/include/asm-s390/thread_info.h +++ b/include/asm-s390/thread_info.h @@ -25,11 +25,9 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned int cpu; /* current CPU */ - int preempt_count; /* 0 => preemptable, <0 => BUG */ + unsigned int preempt_count; /* 0 => preemptable */ }; -#define PREEMPT_ACTIVE 0x4000000 - /* * macros/functions for gaining access to the thread information structure */ @@ -84,4 +82,6 @@ static inline struct thread_info *current_thread_info(void) #endif /* __KERNEL__ */ +#define PREEMPT_ACTIVE 0x4000000 + #endif /* _ASM_THREAD_INFO_H */ diff --git a/include/asm-s390x/hardirq.h b/include/asm-s390x/hardirq.h index 8933c4c096e9..05ead85061d9 100644 --- a/include/asm-s390x/hardirq.h +++ b/include/asm-s390x/hardirq.h @@ -81,15 +81,21 @@ typedef struct { extern void do_call_softirq(void); -#define in_atomic() (preempt_count() != 0) -#define IRQ_EXIT_OFFSET HARDIRQ_OFFSET +#if CONFIG_PREEMPT +# define in_atomic() (in_interrupt() || preempt_count() == PREEMPT_ACTIVE) +# 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() -= HARDIRQ_OFFSET; \ + preempt_count() -= IRQ_EXIT_OFFSET; \ if (!in_interrupt() && softirq_pending(smp_processor_id())) \ /* Use the async. stack for softirq */ \ do_call_softirq(); \ + preempt_enable_no_resched(); \ } while (0) #ifndef CONFIG_SMP diff --git a/include/asm-s390x/thread_info.h b/include/asm-s390x/thread_info.h index 788dc05c66ab..788c7e6654ce 100644 --- a/include/asm-s390x/thread_info.h +++ b/include/asm-s390x/thread_info.h @@ -25,11 +25,9 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned int cpu; /* current CPU */ - int preempt_count; /* 0 => preemptable, <0 => BUG */ + unsigned int preempt_count; /* 0 => preemptable */ }; -#define PREEMPT_ACTIVE 0x4000000 - /* * macros/functions for gaining access to the thread information structure */ @@ -84,4 +82,6 @@ static inline struct thread_info *current_thread_info(void) #endif /* __KERNEL__ */ +#define PREEMPT_ACTIVE 0x4000000 + #endif /* _ASM_THREAD_INFO_H */ -- cgit v1.2.3 From 33ccf4bdf8dd55930b4cfe91d437c9308d3d50f8 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 3 Oct 2002 21:47:55 -0700 Subject: [PATCH] s390 update (14/27): inline optimizations. Inline csum_partial for s390, the only reason it was out-of-line previously is that some older compilers could not get the inline version right. --- arch/s390/lib/Makefile | 3 +- arch/s390/lib/checksum.c | 57 -------------------- arch/s390/lib/misaligned.c | 29 ---------- arch/s390x/lib/Makefile | 3 +- arch/s390x/lib/checksum.c | 40 -------------- arch/s390x/lib/misaligned.c | 34 ------------ include/asm-s390/checksum.h | 40 ++++++++------ include/asm-s390/system.h | 109 ++++++++++++++++---------------------- include/asm-s390x/checksum.h | 38 +++++++++---- include/asm-s390x/system.h | 123 +++++++++++++++++++------------------------ 10 files changed, 153 insertions(+), 323 deletions(-) delete mode 100644 arch/s390/lib/checksum.c delete mode 100644 arch/s390/lib/misaligned.c delete mode 100644 arch/s390x/lib/checksum.c delete mode 100644 arch/s390x/lib/misaligned.c (limited to 'include') diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile index f0d60bc2c745..4972d861f0bd 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile @@ -6,8 +6,7 @@ L_TARGET = lib.a EXTRA_AFLAGS := -traditional -obj-y = checksum.o delay.o memset.o misaligned.o strcmp.o strncpy.o uaccess.o -export-objs += misaligned.o +obj-y = delay.o memset.o strcmp.o strncpy.o uaccess.o include $(TOPDIR)/Rules.make diff --git a/arch/s390/lib/checksum.c b/arch/s390/lib/checksum.c deleted file mode 100644 index 985bdbba8bb0..000000000000 --- a/arch/s390/lib/checksum.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * arch/s390/lib/checksum.c - * S390 fast network checksum routines - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Ulrich Hild (first version), - * Martin Schwidefsky (schwidefsky@de.ibm.com), - * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), - * - * This file contains network checksum routines - */ - -#include -#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) -{ - register_pair rp; - /* - * Experiments with ethernet and slip connections show that buff - * is aligned on either a 2-byte or 4-byte boundary. - */ - rp.subreg.even = (unsigned long) buff; - rp.subreg.odd = (unsigned long) len; - __asm__ __volatile__ ( - "0: cksm %0,%1\n" /* do checksum on longs */ - " jo 0b\n" - : "+&d" (sum), "+&a" (rp) : : "cc" ); - return sum; -} - -/* - * Fold a partial checksum without adding pseudo headers - */ -unsigned short csum_fold(unsigned int sum) -{ - register_pair rp; - - __asm__ __volatile__ ( - " slr %N1,%N1\n" /* %0 = H L */ - " lr %1,%0\n" /* %0 = H L, %1 = H L 0 0 */ - " srdl %1,16\n" /* %0 = H L, %1 = 0 H L 0 */ - " alr %1,%N1\n" /* %0 = H L, %1 = L H L 0 */ - " alr %0,%1\n" /* %0 = H+L+C L+H */ - " srl %0,16\n" /* %0 = H+L+C */ - : "+&d" (sum), "=d" (rp) : : "cc" ); - return ((unsigned short) ~sum); -} - diff --git a/arch/s390/lib/misaligned.c b/arch/s390/lib/misaligned.c deleted file mode 100644 index c80c30466138..000000000000 --- a/arch/s390/lib/misaligned.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * arch/s390/lib/misaligned.c - * S390 misalignment panic stubs - * - * S390 version - * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com). - * - * xchg wants to panic if the pointer is not aligned. To avoid multiplying - * the panic message over and over again, the panic is done in the helper - * functions __misaligned_u32 and __misaligned_u16. - */ - -#include -#include - -void __misaligned_u16(void) -{ - panic("misaligned (__u16 *) in __xchg\n"); -} - -void __misaligned_u32(void) -{ - panic("misaligned (__u32 *) in __xchg\n"); -} - -EXPORT_SYMBOL(__misaligned_u16); -EXPORT_SYMBOL(__misaligned_u32); - diff --git a/arch/s390x/lib/Makefile b/arch/s390x/lib/Makefile index f0d60bc2c745..4972d861f0bd 100644 --- a/arch/s390x/lib/Makefile +++ b/arch/s390x/lib/Makefile @@ -6,8 +6,7 @@ L_TARGET = lib.a EXTRA_AFLAGS := -traditional -obj-y = checksum.o delay.o memset.o misaligned.o strcmp.o strncpy.o uaccess.o -export-objs += misaligned.o +obj-y = delay.o memset.o strcmp.o strncpy.o uaccess.o include $(TOPDIR)/Rules.make diff --git a/arch/s390x/lib/checksum.c b/arch/s390x/lib/checksum.c deleted file mode 100644 index 489299b3a71e..000000000000 --- a/arch/s390x/lib/checksum.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * arch/s390/lib/checksum.c - * S390 fast network checksum routines - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Ulrich Hild (first version), - * Martin Schwidefsky (schwidefsky@de.ibm.com), - * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), - * - * This file contains network checksum routines - */ - -#include -#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) -{ - /* - * Experiments with ethernet and slip connections show that buff - * is aligned on either a 2-byte or 4-byte boundary. - */ - __asm__ __volatile__ ( - " lgr 2,%1\n" /* address in gpr 2 */ - " lgfr 3,%2\n" /* length in gpr 3 */ - "0: cksm %0,2\n" /* do checksum on longs */ - " jo 0b\n" - : "+&d" (sum) - : "d" (buff), "d" (len) - : "cc", "2", "3" ); - return sum; -} - diff --git a/arch/s390x/lib/misaligned.c b/arch/s390x/lib/misaligned.c deleted file mode 100644 index 7f87d711baa6..000000000000 --- a/arch/s390x/lib/misaligned.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * arch/s390/lib/misaligned.c - * S390 misalignment panic stubs - * - * S390 version - * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com). - * - * xchg wants to panic if the pointer is not aligned. To avoid multiplying - * the panic message over and over again, the panic is done in the helper - * functions __misaligned_u64, __misaligned_u32 and __misaligned_u16. - */ - -#include -#include - -void __misaligned_u16(void) -{ - panic("misaligned (__u16 *) in __xchg\n"); -} - -void __misaligned_u32(void) -{ - panic("misaligned (__u32 *) in __xchg\n"); -} - -void __misaligned_u64(void) -{ - panic("misaligned (__u64 *) in __xchg\n"); -} - -EXPORT_SYMBOL(__misaligned_u16); -EXPORT_SYMBOL(__misaligned_u32); -EXPORT_SYMBOL(__misaligned_u64); diff --git a/include/asm-s390/checksum.h b/include/asm-s390/checksum.h index f2621064c8dc..fb53233f0399 100644 --- a/include/asm-s390/checksum.h +++ b/include/asm-s390/checksum.h @@ -27,13 +27,27 @@ * * it's best to have buff aligned on a 32-bit boundary */ -unsigned int -csum_partial(const unsigned char * buff, int len, unsigned int sum); +static inline unsigned int +csum_partial(const unsigned char * buff, int len, unsigned int sum) +{ + register_pair rp; + /* + * Experiments with ethernet and slip connections show that buf + * is aligned on either a 2-byte or 4-byte boundary. + */ + rp.subreg.even = (unsigned long) buff; + rp.subreg.odd = (unsigned long) len; + __asm__ __volatile__ ( + "0: cksm %0,%1\n" /* do checksum on longs */ + " jo 0b\n" + : "+&d" (sum), "+&a" (rp) : : "cc" ); + return sum; +} /* * csum_partial as an inline function */ -extern inline unsigned int +static inline unsigned int csum_partial_inline(const unsigned char * buff, int len, unsigned int sum) { register_pair rp; @@ -55,7 +69,7 @@ csum_partial_inline(const unsigned char * buff, int len, unsigned int sum) * better 64-bit) boundary */ -extern inline unsigned int +static inline unsigned int csum_partial_copy(const char *src, char *dst, int len,unsigned int sum) { memcpy(dst,src,len); @@ -71,7 +85,7 @@ csum_partial_copy(const char *src, char *dst, int len,unsigned int sum) * Copy from userspace and compute checksum. If we catch an exception * then zero the rest of the buffer. */ -extern inline unsigned int +static inline unsigned int csum_partial_copy_from_user (const char *src, char *dst, int len, unsigned int sum, int *err_ptr) @@ -88,7 +102,7 @@ csum_partial_copy_from_user (const char *src, char *dst, } -extern inline unsigned int +static inline unsigned int csum_partial_copy_nocheck (const char *src, char *dst, int len, unsigned int sum) { memcpy(dst,src,len); @@ -98,10 +112,7 @@ csum_partial_copy_nocheck (const char *src, char *dst, int len, unsigned int sum /* * Fold a partial checksum without adding pseudo headers */ -#if 1 -unsigned short csum_fold(unsigned int sum); -#else -extern inline unsigned short +static inline unsigned short csum_fold(unsigned int sum) { register_pair rp; @@ -116,14 +127,13 @@ csum_fold(unsigned int sum) : "+&d" (sum), "=d" (rp) : : "cc" ); return ((unsigned short) ~sum); } -#endif /* * This is a version of ip_compute_csum() optimized for IP headers, * which always checksum on 4 octet boundaries. * */ -extern inline unsigned short +static inline unsigned short ip_fast_csum(unsigned char *iph, unsigned int ihl) { register_pair rp; @@ -143,7 +153,7 @@ ip_fast_csum(unsigned char *iph, unsigned int ihl) * computes the checksum of the TCP/UDP pseudo-header * returns a 32-bit checksum */ -extern inline unsigned int +static inline unsigned int csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr, unsigned short len, unsigned short proto, unsigned int sum) @@ -176,7 +186,7 @@ csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr, * returns a 16-bit checksum, already complemented */ -extern inline unsigned short int +static inline unsigned short int csum_tcpudp_magic(unsigned long saddr, unsigned long daddr, unsigned short len, unsigned short proto, unsigned int sum) @@ -189,7 +199,7 @@ csum_tcpudp_magic(unsigned long saddr, unsigned long daddr, * in icmp.c */ -extern inline unsigned short +static inline unsigned short ip_compute_csum(unsigned char * buff, int len) { return csum_fold(csum_partial(buff, len, 0)); diff --git a/include/asm-s390/system.h b/include/asm-s390/system.h index 2257425edae2..4e8bc560a200 100644 --- a/include/asm-s390/system.h +++ b/include/asm-s390/system.h @@ -30,73 +30,56 @@ struct task_struct; #define nop() __asm__ __volatile__ ("nop") -#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) - -extern void __misaligned_u16(void); -extern void __misaligned_u32(void); +#define xchg(ptr,x) \ + ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) static inline unsigned long __xchg(unsigned long x, void * ptr, int size) { + unsigned long addr, old; + int shift; + switch (size) { - case 1: - asm volatile ( - " lhi 1,3\n" - " nr 1,%0\n" /* isolate last 2 bits */ - " xr %0,1\n" /* align ptr */ - " bras 2,0f\n" - " icm 1,8,3(%1)\n" /* for ptr&3 == 0 */ - " stcm 0,8,3(%1)\n" - " icm 1,4,3(%1)\n" /* for ptr&3 == 1 */ - " stcm 0,4,3(%1)\n" - " icm 1,2,3(%1)\n" /* for ptr&3 == 2 */ - " stcm 0,2,3(%1)\n" - " icm 1,1,3(%1)\n" /* for ptr&3 == 3 */ - " stcm 0,1,3(%1)\n" - "0: sll 1,3\n" - " la 2,0(1,2)\n" /* r2 points to an icm */ - " l 0,0(%0)\n" /* get fullword */ - "1: lr 1,0\n" /* cs loop */ - " ex 0,0(2)\n" /* insert x */ - " cs 0,1,0(%0)\n" - " jl 1b\n" - " ex 0,4(2)" /* store *ptr to x */ - : "+a&" (ptr) : "a" (&x) - : "memory", "cc", "0", "1", "2"); - break; - case 2: - if(((__u32)ptr)&1) - __misaligned_u16(); - asm volatile ( - " lhi 1,2\n" - " nr 1,%0\n" /* isolate bit 2^1 */ - " xr %0,1\n" /* align ptr */ - " bras 2,0f\n" - " icm 1,12,2(%1)\n" /* for ptr&2 == 0 */ - " stcm 0,12,2(%1)\n" - " icm 1,3,2(%1)\n" /* for ptr&2 == 1 */ - " stcm 0,3,2(%1)\n" - "0: sll 1,2\n" - " la 2,0(1,2)\n" /* r2 points to an icm */ - " l 0,0(%0)\n" /* get fullword */ - "1: lr 1,0\n" /* cs loop */ - " ex 0,0(2)\n" /* insert x */ - " cs 0,1,0(%0)\n" - " jl 1b\n" - " ex 0,4(2)" /* store *ptr to x */ - : "+a&" (ptr) : "a" (&x) - : "memory", "cc", "0", "1", "2"); - break; - case 4: - if(((__u32)ptr)&3) - __misaligned_u32(); - asm volatile ( - " l 0,0(%1)\n" - "0: cs 0,%0,0(%1)\n" - " jl 0b\n" - " lr %0,0\n" - : "+d&" (x) : "a" (ptr) - : "memory", "cc", "0" ); - break; + case 1: + addr = (unsigned long) ptr; + shift = (3 ^ (addr & 3)) << 3; + addr ^= addr & 3; + asm volatile( + " l %0,0(%3)\n" + "0: lr 0,%0\n" + " nr 0,%2\n" + " or 0,%1\n" + " cs %0,0,0(%3)\n" + " jl 0b\n" + : "=&d" (old) + : "d" (x << shift), "d" (~(255 << shift)), "a" (addr) + : "memory", "cc", "0" ); + x = old >> shift; + break; + case 2: + addr = (unsigned long) ptr; + shift = (2 ^ (addr & 2)) << 3; + addr ^= addr & 2; + asm volatile( + " l %0,0(%3)\n" + "0: lr 0,%0\n" + " nr 0,%2\n" + " or 0,%1\n" + " cs %0,0,0(%3)\n" + " jl 0b\n" + : "=&d" (old) + : "d" (x << shift), "d" (~(65535 << shift)), "a" (addr) + : "memory", "cc", "0" ); + x = old >> shift; + break; + case 4: + asm volatile ( + " l %0,0(%2)\n" + "0: cs %0,%1,0(%2)\n" + " jl 0b\n" + : "=&d" (old) : "d" (x), "a" (ptr) + : "memory", "cc", "0" ); + x = old; + break; } return x; } diff --git a/include/asm-s390x/checksum.h b/include/asm-s390x/checksum.h index 8c436737e6f0..e44eb70fb63f 100644 --- a/include/asm-s390x/checksum.h +++ b/include/asm-s390x/checksum.h @@ -27,13 +27,29 @@ * * it's best to have buff aligned on a 32-bit boundary */ -unsigned int -csum_partial(const unsigned char * buff, int len, unsigned int sum); +static inline unsigned int +csum_partial(const unsigned char * buff, int len, unsigned int sum) +{ + /* + * Experiments with ethernet and slip connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. + */ + __asm__ __volatile__ ( + " lgr 2,%1\n" /* address in gpr 2 */ + " lgfr 3,%2\n" /* length in gpr 3 */ + "0: cksm %0,2\n" /* do checksum on longs */ + " jo 0b\n" + : "+&d" (sum) + : "d" (buff), "d" (len) + : "cc", "2", "3" ); + return sum; + +} /* * csum_partial as an inline function */ -extern inline unsigned int +static inline unsigned int csum_partial_inline(const unsigned char * buff, int len, unsigned int sum) { __asm__ __volatile__ ( @@ -55,7 +71,7 @@ csum_partial_inline(const unsigned char * buff, int len, unsigned int sum) * better 64-bit) boundary */ -extern inline unsigned int +static inline unsigned int csum_partial_copy(const char *src, char *dst, int len,unsigned int sum) { memcpy(dst,src,len); @@ -71,7 +87,7 @@ csum_partial_copy(const char *src, char *dst, int len,unsigned int sum) * Copy from userspace and compute checksum. If we catch an exception * then zero the rest of the buffer. */ -extern inline unsigned int +static inline unsigned int csum_partial_copy_from_user (const char *src, char *dst, int len, unsigned int sum, int *err_ptr) @@ -87,7 +103,7 @@ csum_partial_copy_from_user (const char *src, char *dst, return csum_partial(dst, len, sum); } -extern inline unsigned int +static inline unsigned int csum_partial_copy_nocheck (const char *src, char *dst, int len, unsigned int sum) { memcpy(dst,src,len); @@ -97,7 +113,7 @@ csum_partial_copy_nocheck (const char *src, char *dst, int len, unsigned int sum /* * Fold a partial checksum without adding pseudo headers */ -extern inline unsigned short +static inline unsigned short csum_fold(unsigned int sum) { __asm__ __volatile__ ( @@ -116,7 +132,7 @@ csum_fold(unsigned int sum) * which always checksum on 4 octet boundaries. * */ -extern inline unsigned short +static inline unsigned short ip_fast_csum(unsigned char *iph, unsigned int ihl) { unsigned long sum; @@ -137,7 +153,7 @@ ip_fast_csum(unsigned char *iph, unsigned int ihl) * computes the checksum of the TCP/UDP pseudo-header * returns a 32-bit checksum */ -extern inline unsigned int +static inline unsigned int csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr, unsigned short len, unsigned short proto, unsigned int sum) @@ -170,7 +186,7 @@ csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr, * returns a 16-bit checksum, already complemented */ -extern inline unsigned short int +static inline unsigned short int csum_tcpudp_magic(unsigned long saddr, unsigned long daddr, unsigned short len, unsigned short proto, unsigned int sum) @@ -183,7 +199,7 @@ csum_tcpudp_magic(unsigned long saddr, unsigned long daddr, * in icmp.c */ -extern inline unsigned short +static inline unsigned short ip_compute_csum(unsigned char * buff, int len) { return csum_fold(csum_partial_inline(buff, len, 0)); diff --git a/include/asm-s390x/system.h b/include/asm-s390x/system.h index 44f4acbb36fa..1189995c4f65 100644 --- a/include/asm-s390x/system.h +++ b/include/asm-s390x/system.h @@ -39,77 +39,60 @@ extern void __misaligned_u64(void); static inline unsigned long __xchg(unsigned long x, void * ptr, int size) { + unsigned long addr, old; + int shift; + switch (size) { - case 1: - asm volatile ( - " lghi 1,3\n" - " nr 1,%0\n" /* isolate last 2 bits */ - " xr %0,1\n" /* align ptr */ - " bras 2,0f\n" - " icm 1,8,7(%1)\n" /* for ptr&3 == 0 */ - " stcm 0,8,7(%1)\n" - " icm 1,4,7(%1)\n" /* for ptr&3 == 1 */ - " stcm 0,4,7(%1)\n" - " icm 1,2,7(%1)\n" /* for ptr&3 == 2 */ - " stcm 0,2,7(%1)\n" - " icm 1,1,7(%1)\n" /* for ptr&3 == 3 */ - " stcm 0,1,7(%1)\n" - "0: sll 1,3\n" - " la 2,0(1,2)\n" /* r2 points to an icm */ - " l 0,0(%0)\n" /* get fullword */ - "1: lr 1,0\n" /* cs loop */ - " ex 0,0(2)\n" /* insert x */ - " cs 0,1,0(%0)\n" - " jl 1b\n" - " ex 0,4(2)" /* store *ptr to x */ - : "+&a" (ptr) : "a" (&x) - : "memory", "cc", "0", "1", "2"); - break; - case 2: - if(((addr_t)ptr)&1) - __misaligned_u16(); - asm volatile ( - " lghi 1,2\n" - " nr 1,%0\n" /* isolate bit 2^1 */ - " xr %0,1\n" /* align ptr */ - " bras 2,0f\n" - " icm 1,12,6(%1)\n" /* for ptr&2 == 0 */ - " stcm 0,12,6(%1)\n" - " icm 1,3,2(%1)\n" /* for ptr&2 == 1 */ - " stcm 0,3,2(%1)\n" - "0: sll 1,2\n" - " la 2,0(1,2)\n" /* r2 points to an icm */ - " l 0,0(%0)\n" /* get fullword */ - "1: lr 1,0\n" /* cs loop */ - " ex 0,0(2)\n" /* insert x */ - " cs 0,1,0(%0)\n" - " jl 1b\n" - " ex 0,4(2)" /* store *ptr to x */ - : "+&a" (ptr) : "a" (&x) - : "memory", "cc", "0", "1", "2"); - break; - case 4: - if(((addr_t)ptr)&3) - __misaligned_u32(); - asm volatile ( - " l 0,0(%1)\n" - "0: cs 0,%0,0(%1)\n" - " jl 0b\n" - " lgfr %0,0\n" - : "+d" (x) : "a" (ptr) - : "memory", "cc", "0" ); - break; - case 8: - if(((addr_t)ptr)&7) - __misaligned_u64(); - asm volatile ( - " lg 0,0(%1)\n" - "0: csg 0,%0,0(%1)\n" - " jl 0b\n" - " lgr %0,0\n" - : "+d" (x) : "a" (ptr) - : "memory", "cc", "0" ); - break; + case 1: + addr = (unsigned long) ptr; + shift = (3 ^ (addr & 3)) << 3; + addr ^= addr & 3; + asm volatile( + " l %0,0(%3)\n" + "0: lr 0,%0\n" + " nr 0,%2\n" + " or 0,%1\n" + " cs %0,0,0(%3)\n" + " jl 0b\n" + : "=&d" (old) + : "d" (x << shift), "d" (~(255 << shift)), "a" (addr) + : "memory", "cc", "0" ); + x = old >> shift; + break; + case 2: + addr = (unsigned long) ptr; + shift = (2 ^ (addr & 2)) << 3; + addr ^= addr & 2; + asm volatile( + " l %0,0(%3)\n" + "0: lr 0,%0\n" + " nr 0,%2\n" + " or 0,%1\n" + " cs %0,0,0(%3)\n" + " jl 0b\n" + : "=&d" (old) + : "d" (x << shift), "d" (~(65535 << shift)), "a" (addr) + : "memory", "cc", "0" ); + x = old >> shift; + break; + case 4: + asm volatile ( + " l %0,0(%2)\n" + "0: cs %0,%1,0(%2)\n" + " jl 0b\n" + : "=&d" (old) : "d" (x), "a" (ptr) + : "memory", "cc", "0" ); + x = old; + break; + case 8: + asm volatile ( + " lg %0,0(%2)\n" + "0: csg %0,%1,0(%2)\n" + " jl 0b\n" + : "=&d" (old) : "d" (x), "a" (ptr) + : "memory", "cc", "0" ); + x = old; + break; } return x; } -- cgit v1.2.3 From 9f0ff519599618456c540149802bc77e3379d4da Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 3 Oct 2002 21:48:12 -0700 Subject: [PATCH] s390 update (15/27): 64 bit spinlocks. Use diag 0x44 on s390x for spinlocks. --- arch/s390x/kernel/head.S | 13 +++++++++++++ include/asm-s390x/lowcore.h | 5 ++++- include/asm-s390x/setup.h | 9 +++++---- include/asm-s390x/spinlock.h | 32 ++++++++++++++++++++++---------- 4 files changed, 44 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/arch/s390x/kernel/head.S b/arch/s390x/kernel/head.S index d311d2578d66..23b1543cb6ad 100644 --- a/arch/s390x/kernel/head.S +++ b/arch/s390x/kernel/head.S @@ -555,6 +555,17 @@ startup:basr %r13,0 # get base oi 7(%r12),16 # set MVPG flag 0: +# +# find out if the diag 0x44 works in 64 bit mode +# + la %r1,0f-.LPG1(%r13) # set program check address + stg %r1,__LC_PGM_NEW_PSW+8 + mvc __LC_DIAG44_OPCODE(8),.Lnop-.LPG1(%r13) + diag 0,0,0x44 # test diag 0x44 + oi 7(%r12),32 # set diag44 flag + mvc __LC_DIAG44_OPCODE(8),.Ldiag44-.LPG1(%r13) +0: + lpswe .Lentry-.LPG1(13) # jump to _stext in primary-space, # virtual and never return ... .align 16 @@ -578,6 +589,8 @@ startup:basr %r13,0 # get base .Lpcmsk:.quad 0x0000000180000000 .L4malign:.quad 0xffffffffffc00000 .Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8 +.Lnop: .long 0x07000700 +.Ldiag44:.long 0x83000044 .org PARMAREA-64 .Lduct: .long 0,0,0,0,0,0,0,0 diff --git a/include/asm-s390x/lowcore.h b/include/asm-s390x/lowcore.h index edb29f4df6bf..4ed57785ffaf 100644 --- a/include/asm-s390x/lowcore.h +++ b/include/asm-s390x/lowcore.h @@ -38,6 +38,8 @@ #define __LC_IO_INT_WORD 0x0C0 #define __LC_MCCK_CODE 0x0E8 +#define __LC_DIAG44_OPCODE 0x214 + #define __LC_SAVE_AREA 0xC00 #define __LC_KERNEL_STACK 0xD40 #define __LC_ASYNC_STACK 0xD48 @@ -146,7 +148,8 @@ struct _lowcore psw_t io_new_psw; /* 0x1f0 */ psw_t return_psw; /* 0x200 */ __u32 sync_io_word; /* 0x210 */ - __u8 pad8[0xc00-0x214]; /* 0x214 */ + __u32 diag44_opcode; /* 0x214 */ + __u8 pad8[0xc00-0x218]; /* 0x218 */ /* System info area */ __u64 save_area[16]; /* 0xc00 */ __u8 pad9[0xd40-0xc80]; /* 0xc80 */ diff --git a/include/asm-s390x/setup.h b/include/asm-s390x/setup.h index 0a0419089bf5..9aa13a641520 100644 --- a/include/asm-s390x/setup.h +++ b/include/asm-s390x/setup.h @@ -25,11 +25,12 @@ */ extern unsigned long machine_flags; -#define MACHINE_IS_VM (machine_flags & 1) -#define MACHINE_IS_P390 (machine_flags & 4) -#define MACHINE_HAS_MVPG (machine_flags & 16) +#define MACHINE_IS_VM (machine_flags & 1) +#define MACHINE_IS_P390 (machine_flags & 4) +#define MACHINE_HAS_MVPG (machine_flags & 16) +#define MACHINE_HAS_DIAG44 (machine_flags & 32) -#define MACHINE_HAS_HWC (!MACHINE_IS_P390) +#define MACHINE_HAS_HWC (!MACHINE_IS_P390) /* * Console mode. Override with conmode= diff --git a/include/asm-s390x/spinlock.h b/include/asm-s390x/spinlock.h index f8e8bbdefe61..e9e226f5f23d 100644 --- a/include/asm-s390x/spinlock.h +++ b/include/asm-s390x/spinlock.h @@ -16,7 +16,14 @@ * asm/spinlock.h. The diagnose is only available in kernel * context. */ +#ifdef __KERNEL__ #include +#define __DIAG44_INSN "ex" +#define __DIAG44_OPERAND __LC_DIAG44_OPCODE +#else +#define __DIAG44_INSN "#" +#define __DIAG44_OPERAND 0 +#endif /* * Simple spin lock operations. There are two variants, one clears IRQ's @@ -38,12 +45,13 @@ extern inline void _raw_spin_lock(spinlock_t *lp) { unsigned long reg1, reg2; __asm__ __volatile(" bras %1,1f\n" - "0: # diag 0,0,68\n" + "0: " __DIAG44_INSN " 0,%4\n" "1: slr %0,%0\n" " cs %0,%1,0(%3)\n" " jl 0b\n" : "=&d" (reg1), "=&d" (reg2), "+m" (lp->lock) - : "a" (&lp->lock) : "cc" ); + : "a" (&lp->lock), "i" (__DIAG44_OPERAND) + : "cc" ); } extern inline int _raw_spin_trylock(spinlock_t *lp) @@ -88,43 +96,47 @@ typedef struct { #define _raw_read_lock(rw) \ asm volatile(" lg 2,0(%1)\n" \ " j 1f\n" \ - "0: # diag 0,0,68\n" \ + "0: " __DIAG44_INSN " 0,%2\n" \ "1: nihh 2,0x7fff\n" /* clear high (=write) bit */ \ " la 3,1(2)\n" /* one more reader */ \ " csg 2,3,0(%1)\n" /* try to write new value */ \ " jl 0b" \ - : "+m" ((rw)->lock) : "a" (&(rw)->lock) \ + : "+m" ((rw)->lock) \ + : "a" (&(rw)->lock), "i" (__DIAG44_OPERAND) \ : "2", "3", "cc" ) #define _raw_read_unlock(rw) \ asm volatile(" lg 2,0(%1)\n" \ " j 1f\n" \ - "0: # diag 0,0,68\n" \ + "0: " __DIAG44_INSN " 0,%2\n" \ "1: lgr 3,2\n" \ " bctgr 3,0\n" /* one less reader */ \ " csg 2,3,0(%1)\n" \ " jl 0b" \ - : "+m" ((rw)->lock) : "a" (&(rw)->lock) \ + : "+m" ((rw)->lock) \ + : "a" (&(rw)->lock), "i" (__DIAG44_OPERAND) \ : "2", "3", "cc" ) #define _raw_write_lock(rw) \ asm volatile(" llihh 3,0x8000\n" /* new lock value = 0x80...0 */ \ " j 1f\n" \ - "0: # diag 0,0,68\n" \ + "0: " __DIAG44_INSN " 0,%2\n" \ "1: slgr 2,2\n" /* old lock value must be 0 */ \ " csg 2,3,0(%1)\n" \ " jl 0b" \ - : "+m" ((rw)->lock) : "a" (&(rw)->lock) \ + : "+m" ((rw)->lock) \ + : "a" (&(rw)->lock), "i" (__DIAG44_OPERAND) \ : "2", "3", "cc" ) #define _raw_write_unlock(rw) \ asm volatile(" slgr 3,3\n" /* new lock value = 0 */ \ " j 1f\n" \ - "0: # diag 0,0,68\n" \ + "0: " __DIAG44_INSN " 0,%2\n" \ "1: llihh 2,0x8000\n" /* old lock value must be 0x8..0 */\ " csg 2,3,0(%1)\n" \ " jl 0b" \ - : "+m" ((rw)->lock) : "a" (&(rw)->lock) \ + : "+m" ((rw)->lock) \ + : "a" (&(rw)->lock), "i" (__DIAG44_OPERAND) \ : "2", "3", "cc" ) #endif /* __ASM_SPINLOCK_H */ -- cgit v1.2.3 From fbd32c90af279619d151d01b39d2af6ce651b8f7 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 3 Oct 2002 21:48:47 -0700 Subject: [PATCH] s390 update (17/27): beautification. Remove bogus sanity checks and code cleanup. --- arch/s390/kernel/process.c | 10 +++++----- arch/s390/kernel/setup.c | 5 ++++- arch/s390/kernel/smp.c | 12 +++++------ arch/s390/mm/fault.c | 10 ++++++---- arch/s390x/kernel/process.c | 10 +++++----- arch/s390x/kernel/setup.c | 5 ++++- arch/s390x/kernel/smp.c | 12 +++++------ arch/s390x/mm/fault.c | 10 ++++++---- drivers/s390/char/hwc_rw.c | 2 -- drivers/s390/cio/airq.c | 6 +++--- drivers/s390/cio/blacklist.c | 8 ++++---- drivers/s390/cio/cio.c | 46 ++++++++++++++++--------------------------- drivers/s390/cio/cio_debug.h | 8 ++++---- drivers/s390/cio/ioinfo.c | 24 +++++++++++----------- drivers/s390/cio/requestirq.c | 8 ++------ drivers/s390/cio/s390io.c | 40 +++++++++++++------------------------ drivers/s390/s390mach.c | 6 +++--- include/asm-s390/debug.h | 6 ++++-- include/asm-s390/lowcore.h | 17 ++++------------ include/asm-s390/system.h | 6 +++--- include/asm-s390x/debug.h | 6 ++++-- include/asm-s390x/lowcore.h | 16 ++++----------- include/asm-s390x/system.h | 8 ++++---- 23 files changed, 124 insertions(+), 157 deletions(-) (limited to 'include') diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index f5fe1a927b95..dd0c32df7485 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -302,15 +302,15 @@ void dump_thread(struct pt_regs * regs, struct user * dump) dump->magic = CMAGIC; dump->start_code = 0; dump->start_stack = regs->gprs[15] & ~(PAGE_SIZE - 1); - dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; - dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; + dump->u_tsize = current->mm->end_code >> PAGE_SHIFT; + dump->u_dsize = (current->mm->brk + PAGE_SIZE - 1) >> PAGE_SHIFT; dump->u_dsize -= dump->u_tsize; dump->u_ssize = 0; if (dump->start_stack < TASK_SIZE) - dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; - memcpy(&dump->regs.gprs[0],regs,sizeof(s390_regs)); + dump->u_ssize = (TASK_SIZE - dump->start_stack) >> PAGE_SHIFT; + memcpy(&dump->regs, regs, sizeof(s390_regs)); dump_fpu (regs, &dump->regs.fp_regs); - memcpy(&dump->regs.per_info,¤t->thread.per_info,sizeof(per_struct)); + dump->regs.per_info = current->thread.per_info; } /* diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 860fcfcf3353..366c673bb101 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -526,7 +526,10 @@ static int show_cpuinfo(struct seq_file *m, void *v) (loops_per_jiffy/(5000/HZ))%100); } if (cpu_online_map & (1 << n)) { - cpuinfo = &safe_get_cpu_lowcore(n)->cpu_data; + if (smp_processor_id() == n) + cpuinfo = &S390_lowcore.cpu_data; + else + cpuinfo = &lowcore_ptr[n]->cpu_data; seq_printf(m, "processor %li: " "version = %02X, " "identification = %06X, " diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index d1d189dd1bec..b5ca80658f19 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -173,7 +173,7 @@ static inline void do_store_status(void) for (i = 0; i < NR_CPUS; i++) { if (!cpu_online(i) || smp_processor_id() == i) continue; - low_core_addr = (unsigned long)get_cpu_lowcore(i); + low_core_addr = (unsigned long) lowcore_ptr[i]; do { rc = signal_processor_ps(&dummy, low_core_addr, i, sigp_store_status_at_address); @@ -188,7 +188,7 @@ static inline void do_store_status(void) void smp_send_stop(void) { /* write magic number to zero page (absolute 0) */ - get_cpu_lowcore(smp_processor_id())->panic_magic = __PANIC_MAGIC; + lowcore_ptr[smp_processor_id()]->panic_magic = __PANIC_MAGIC; /* stop other processors. */ do_send_stop(); @@ -296,7 +296,7 @@ void do_ext_call_interrupt(struct pt_regs *regs, __u16 code) */ static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig) { - struct _lowcore *lowcore = get_cpu_lowcore(cpu); + struct _lowcore *lowcore = lowcore_ptr[cpu]; sigp_ccode ccode; /* @@ -319,7 +319,7 @@ static void smp_ext_bitcall_others(ec_bit_sig sig) for (i = 0; i < NR_CPUS; i++) { if (!cpu_online(i) || smp_processor_id() == i) continue; - lowcore = get_cpu_lowcore(i); + lowcore = lowcore_ptr[i]; /* * Set signaling bit in lowcore of target cpu and kick it */ @@ -519,7 +519,7 @@ int __cpu_up(unsigned int cpu) unhash_process(idle); - cpu_lowcore = get_cpu_lowcore(cpu); + cpu_lowcore = lowcore_ptr[cpu]; cpu_lowcore->save_area[15] = idle->thread.ksp; cpu_lowcore->kernel_stack = (__u32) idle->thread_info + (2*PAGE_SIZE); __asm__ __volatile__("la 1,%0\n\t" @@ -555,7 +555,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus) /* * Initialize prefix pages and stacks for all possible cpus */ - print_cpu_info(&safe_get_cpu_lowcore(0)->cpu_data); + print_cpu_info(&S390_lowcore.cpu_data); for(i = 0; i < NR_CPUS; i++) { if (!cpu_possible(i)) diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index bc52665b908f..ab523fd4eebf 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -233,16 +233,18 @@ survive: * the fault. */ switch (handle_mm_fault(mm, vma, address, error_code == 4)) { - case 1: + case VM_FAULT_MINOR: tsk->min_flt++; break; - case 2: + case VM_FAULT_MAJOR: tsk->maj_flt++; break; - case 0: + case VM_FAULT_SIGBUS: goto do_sigbus; - default: + case VM_FAULT_OOM: goto out_of_memory; + default: + BUG(); } up_read(&mm->mmap_sem); diff --git a/arch/s390x/kernel/process.c b/arch/s390x/kernel/process.c index f62c40991635..6f536c756351 100644 --- a/arch/s390x/kernel/process.c +++ b/arch/s390x/kernel/process.c @@ -292,15 +292,15 @@ void dump_thread(struct pt_regs * regs, struct user * dump) dump->magic = CMAGIC; dump->start_code = 0; dump->start_stack = regs->gprs[15] & ~(PAGE_SIZE - 1); - dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; - dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; + dump->u_tsize = current->mm->end_code >> PAGE_SHIFT; + dump->u_dsize = (current->mm->brk + PAGE_SIZE - 1) >> PAGE_SHIFT; dump->u_dsize -= dump->u_tsize; dump->u_ssize = 0; if (dump->start_stack < TASK_SIZE) - dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; - memcpy(&dump->regs.gprs[0],regs,sizeof(s390_regs)); + dump->u_ssize = (TASK_SIZE - dump->start_stack) >> PAGE_SHIFT; + memcpy(&dump->regs, regs, sizeof(s390_regs)); dump_fpu (regs, &dump->regs.fp_regs); - memcpy(&dump->regs.per_info,¤t->thread.per_info,sizeof(per_struct)); + dump->regs.per_info = current->thread.per_info; } /* diff --git a/arch/s390x/kernel/setup.c b/arch/s390x/kernel/setup.c index 7894f4a708c8..c082607d74a4 100644 --- a/arch/s390x/kernel/setup.c +++ b/arch/s390x/kernel/setup.c @@ -516,7 +516,10 @@ static int show_cpuinfo(struct seq_file *m, void *v) (loops_per_jiffy/(5000/HZ))%100); } if (cpu_online_map & (1 << n)) { - cpuinfo = &safe_get_cpu_lowcore(n)->cpu_data; + if (smp_processor_id() == n) + cpuinfo = &S390_lowcore.cpu_data; + else + cpuinfo = &lowcore_ptr[n]->cpu_data; seq_printf(m, "processor %li: " "version = %02X, " "identification = %06X, " diff --git a/arch/s390x/kernel/smp.c b/arch/s390x/kernel/smp.c index d602ffde0e00..ff2b6007d23e 100644 --- a/arch/s390x/kernel/smp.c +++ b/arch/s390x/kernel/smp.c @@ -172,7 +172,7 @@ static inline void do_store_status(void) for (i = 0; i < NR_CPUS; i++) { if (!cpu_online(i) || smp_processor_id() == i) continue; - low_core_addr = (unsigned long)get_cpu_lowcore(i); + low_core_addr = (unsigned long) lowcore_ptr[i]; do { rc = signal_processor_ps(&dummy, low_core_addr, i, sigp_store_status_at_address); @@ -187,7 +187,7 @@ static inline void do_store_status(void) void smp_send_stop(void) { /* write magic number to zero page (absolute 0) */ - get_cpu_lowcore(smp_processor_id())->panic_magic = __PANIC_MAGIC; + lowcore_ptr[smp_processor_id()]->panic_magic = __PANIC_MAGIC; /* stop other processors. */ do_send_stop(); @@ -298,7 +298,7 @@ static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig) /* * Set signaling bit in lowcore of target cpu and kick it */ - set_bit(sig, &(get_cpu_lowcore(cpu)->ext_call_fast)); + set_bit(sig, &lowcore_ptr[cpu]->ext_call_fast); ccode = signal_processor(cpu, sigp_external_call); return ccode; } @@ -317,7 +317,7 @@ static void smp_ext_bitcall_others(ec_bit_sig sig) /* * Set signaling bit in lowcore of target cpu and kick it */ - set_bit(sig, &(get_cpu_lowcore(i)->ext_call_fast)); + set_bit(sig, &lowcore_ptr[i]->ext_call_fast); while (signal_processor(i, sigp_external_call) == sigp_busy) udelay(10); } @@ -499,7 +499,7 @@ int __cpu_up(unsigned int cpu) unhash_process(idle); - cpu_lowcore = get_cpu_lowcore(cpu); + cpu_lowcore = lowcore_ptr[cpu]; cpu_lowcore->save_area[15] = idle->thread.ksp; cpu_lowcore->kernel_stack = (__u64) idle->thread_info + (4*PAGE_SIZE); __asm__ __volatile__("la 1,%0\n\t" @@ -535,7 +535,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus) /* * Initialize prefix pages and stacks for all possible cpus */ - print_cpu_info(&safe_get_cpu_lowcore(0)->cpu_data); + print_cpu_info(&S390_lowcore.cpu_data); for(i = 0; i < NR_CPUS; i++) { if (!cpu_possible(i)) diff --git a/arch/s390x/mm/fault.c b/arch/s390x/mm/fault.c index 29b5514e4598..fe6bcb82d920 100644 --- a/arch/s390x/mm/fault.c +++ b/arch/s390x/mm/fault.c @@ -233,16 +233,18 @@ survive: * the fault. */ switch (handle_mm_fault(mm, vma, address, error_code == 4)) { - case 1: + case VM_FAULT_MINOR: tsk->min_flt++; break; - case 2: + case VM_FAULT_MAJOR: tsk->maj_flt++; break; - case 0: + case VM_FAULT_SIGBUS: goto do_sigbus; - default: + case VM_FAULT_OOM: goto out_of_memory; + default: + BUG(); } up_read(&mm->mmap_sem); diff --git a/drivers/s390/char/hwc_rw.c b/drivers/s390/char/hwc_rw.c index 1ddc2a367487..0959d609d1cd 100644 --- a/drivers/s390/char/hwc_rw.c +++ b/drivers/s390/char/hwc_rw.c @@ -2215,8 +2215,6 @@ hwc_do_interrupt (u32 ext_int_param) void hwc_interrupt_handler (struct pt_regs *regs, __u16 code) { - int cpu = smp_processor_id (); - u32 ext_int_param = hwc_ext_int_param (); irq_enter (); diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index 30feebe99ed5..5e100440b4c2 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c @@ -3,7 +3,7 @@ * S/390 common I/O routines -- special interrupt registration * currently used only by qdio * - * $Revision: 1.2 $ + * $Revision: 1.3 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -60,7 +60,7 @@ s390_register_adapter_interrupt (adapter_int_handler_t handler) sprintf (dbf_txt, "ret:%d", ret); CIO_TRACE_EVENT (4, dbf_txt); - return (ret); + return ret; } int @@ -85,7 +85,7 @@ s390_unregister_adapter_interrupt (adapter_int_handler_t handler) sprintf (dbf_txt, "ret:%d", ret); CIO_TRACE_EVENT (4, dbf_txt); - return (ret); + return ret; } void diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index 2346e48477c5..46db7d21aed2 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/blacklist.c * S/390 common I/O routines -- blacklisting of specific devices - * $Revision: 1.5 $ + * $Revision: 1.7 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -134,7 +134,7 @@ __setup ("cio_ignore=", blacklist_setup); int is_blacklisted (int devno) { - return (test_bit (devno, &bl_dev)); + return test_bit (devno, &bl_dev); } #ifdef CONFIG_PROC_FS @@ -243,10 +243,10 @@ static int cio_ignore_write (struct file *file, const char *user_buf, return -EFAULT; } buf[user_len] = '\0'; - +#if 0 CIO_DEBUG(KERN_DEBUG, 2, "/proc/cio_ignore: '%s'\n", buf); - +#endif blacklist_parse_proc_parameters (buf); vfree (buf); diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 693465656daf..7fb61539b12d 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/cio.c * S/390 common I/O routines -- low level i/o calls - * $Revision: 1.15 $ + * $Revision: 1.25 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -52,7 +52,7 @@ s390_displayhex (void *ptr, s32 cnt, int level) } DBG ("%s\n", buffer); if (cio_debug_initialized) - debug_text_event (cio_debug_trace_id, level, buffer); + debug_text_event (cio_debug_msg_id, level, buffer); } @@ -433,8 +433,6 @@ s390_start_IO (int irq, /* IRQ */ int ret = 0; char dbf_txt[15]; - SANITY_CHECK (irq); - /* * The flag usage is mutal exclusive ... */ @@ -456,7 +454,7 @@ s390_start_IO (int irq, /* IRQ */ ret = enable_cpu_sync_isc (irq); if (ret) - return (ret); + return ret; } @@ -556,7 +554,7 @@ s390_start_IO (int irq, /* IRQ */ if (flag & DOIO_DONT_CALL_INTHDLR) ioinfo[irq]->ui.flags.repnone = 0; - return (ret); + return ret; } int @@ -618,7 +616,7 @@ do_IO (int irq, /* IRQ */ } - return (ret); + return ret; } @@ -677,7 +675,7 @@ resume_IO (int irq) } - return (ret); + return ret; } /* @@ -720,7 +718,7 @@ halt_IO (int irq, unsigned long user_intparm, unsigned long flag) ret = enable_cpu_sync_isc (irq); if (ret) - return (ret); + return ret; } /* @@ -784,7 +782,7 @@ halt_IO (int irq, unsigned long user_intparm, unsigned long flag) if (flag & DOIO_WAIT_FOR_INTERRUPT) disable_cpu_sync_isc (irq); - return (ret); + return ret; } /* @@ -803,7 +801,7 @@ clear_IO (int irq, unsigned long user_intparm, unsigned long flag) SANITY_CHECK (irq); if (ioinfo[irq] == INVALID_STORAGE_AREA) - return (-ENODEV); + return -ENODEV; /* * we only allow for clear_IO if the device has an I/O handler associated @@ -829,7 +827,7 @@ clear_IO (int irq, unsigned long user_intparm, unsigned long flag) ret = enable_cpu_sync_isc (irq); if (ret) - return (ret); + return ret; } /* @@ -891,7 +889,7 @@ clear_IO (int irq, unsigned long user_intparm, unsigned long flag) if (flag & DOIO_WAIT_FOR_INTERRUPT) disable_cpu_sync_isc (irq); - return (ret); + return ret; } /* @@ -908,8 +906,6 @@ cancel_IO (int irq) char dbf_txt[15]; int ret = 0; - SANITY_CHECK (irq); - sprintf (dbf_txt, "cancelIO%x", irq); CIO_TRACE_EVENT (2, dbf_txt); @@ -955,7 +951,6 @@ do_IRQ (struct pt_regs regs) * Get interrupt info from lowcore */ volatile tpi_info_t *tpi_info = (tpi_info_t *) (__LC_SUBCHANNEL_ID); - int cpu = smp_processor_id (); /* * take fast exit if CPU is in sync. I/O state @@ -1183,7 +1178,7 @@ s390_process_IRQ_normal(unsigned int irq, * take fast exit if no handler is available */ if (!ioinfo[irq]->ui.flags.ready) - return (ending_status); + return ending_status; /* * Check whether we must issue a SENSE CCW ourselves if there is no @@ -1485,7 +1480,8 @@ s390_process_IRQ (unsigned int irq) s390_irq_count[cpu]++; } - sprintf (dbf_txt, "procIRQ%x", irq); + CIO_TRACE_EVENT (3, "procIRQ"); + sprintf (dbf_txt, "%x", irq); CIO_TRACE_EVENT (3, dbf_txt); if (ioinfo[irq] == INVALID_STORAGE_AREA) { @@ -1497,14 +1493,8 @@ s390_process_IRQ (unsigned int irq) "for non-initialized subchannel!\n", irq); tsch (irq, &p_init_irb); - return (1); - - } - - if (ioinfo[irq]->st) { - /* can't be */ - BUG(); return 1; + } dp = &ioinfo[irq]->devstat; @@ -1715,8 +1705,6 @@ set_cons_dev (int irq) int rc = 0; char dbf_txt[15]; - SANITY_CHECK (irq); - if (cons_dev != -1) return -EBUSY; @@ -1750,7 +1738,7 @@ set_cons_dev (int irq) } } - return (rc); + return rc; } int @@ -1801,7 +1789,7 @@ wait_cons_dev (int irq) } - return (rc); + return rc; } /* diff --git a/drivers/s390/cio/cio_debug.h b/drivers/s390/cio/cio_debug.h index 9ded9a58e790..2d6cd007f67e 100644 --- a/drivers/s390/cio/cio_debug.h +++ b/drivers/s390/cio/cio_debug.h @@ -3,9 +3,9 @@ #define SANITY_CHECK(irq) do { \ if (irq > highest_subchannel || irq < 0) \ - return (-ENODEV); \ + return -ENODEV; \ if (ioinfo[irq] == INVALID_STORAGE_AREA) \ - return (-ENODEV); \ + return -ENODEV; \ if (ioinfo[irq]->st) \ return -ENODEV; \ } while(0) @@ -20,14 +20,14 @@ if (irq > highest_subchannel || irq < 0) \ #define CIO_MSG_EVENT(imp, args...) do { \ if (cio_debug_initialized) \ debug_sprintf_event(cio_debug_msg_id, \ - imp, \ + imp , \ ##args); \ } while (0) #define CIO_CRW_EVENT(imp, args...) do { \ if (cio_debug_initialized) \ debug_sprintf_event(cio_debug_crw_id, \ - imp, \ + imp , \ ##args); \ } while (0) diff --git a/drivers/s390/cio/ioinfo.c b/drivers/s390/cio/ioinfo.c index bdaa655a3cef..9212ca9ea5bb 100644 --- a/drivers/s390/cio/ioinfo.c +++ b/drivers/s390/cio/ioinfo.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/ioinfo.c * S/390 common I/O routines -- the ioinfo structure - * $Revision: 1.3 $ + * $Revision: 1.4 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -102,12 +102,12 @@ get_irq_next (int irq) int get_dev_info_by_irq (int irq, s390_dev_info_t * pdi) { - if (irq > highest_subchannel || irq < 0) \ - return (-ENODEV); \ - if (ioinfo[irq] == INVALID_STORAGE_AREA) \ - return (-ENODEV); \ - if (ioinfo[irq]->st) \ - return -ENODEV; \ + if (irq > highest_subchannel || irq < 0) + return -ENODEV; + if (ioinfo[irq] == INVALID_STORAGE_AREA) + return -ENODEV; + if (ioinfo[irq]->st) + return -ENODEV; if (pdi == NULL) return -EINVAL; @@ -189,7 +189,7 @@ get_dev_info_by_devno (__u16 devno, s390_dev_info_t * pdi) } } - return (rc); + return rc; } @@ -211,7 +211,7 @@ get_irq_by_devno (__u16 devno) } } - return (rc); + return rc; } unsigned int @@ -235,7 +235,7 @@ get_devno_by_irq (int irq) * defined who's device number isn't valid ... */ if (ioinfo[irq]->schib.pmcw.dnv) - return (ioinfo[irq]->schib.pmcw.dev); + return ioinfo[irq]->schib.pmcw.dev; else return -1; } @@ -258,9 +258,9 @@ int s390_set_private_data(int irq, void *data) { if (irq > highest_subchannel || irq < 0) - return (-ENODEV); + return -ENODEV; if (ioinfo[irq] == INVALID_STORAGE_AREA) - return (-ENODEV); + return -ENODEV; if (ioinfo[irq]->st) return -ENODEV; ioinfo[irq]->private_data = data; diff --git a/drivers/s390/cio/requestirq.c b/drivers/s390/cio/requestirq.c index 22ee10b3c65e..5648b69c7e88 100644 --- a/drivers/s390/cio/requestirq.c +++ b/drivers/s390/cio/requestirq.c @@ -253,8 +253,6 @@ enable_subchannel (unsigned int irq) int retry = 5; char dbf_txt[15]; - SANITY_CHECK (irq); - sprintf (dbf_txt, "ensch%x", irq); CIO_TRACE_EVENT (2, dbf_txt); @@ -324,7 +322,7 @@ enable_subchannel (unsigned int irq) sprintf (dbf_txt, "ret:%d", ret); CIO_TRACE_EVENT (2, dbf_txt); - return (ret); + return ret; } /* @@ -338,8 +336,6 @@ disable_subchannel (unsigned int irq) int retry = 5; char dbf_txt[15]; - SANITY_CHECK (irq); - sprintf (dbf_txt, "dissch%x", irq); CIO_TRACE_EVENT (2, dbf_txt); @@ -427,7 +423,7 @@ disable_subchannel (unsigned int irq) sprintf (dbf_txt, "ret:%d", ret); CIO_TRACE_EVENT (2, dbf_txt); - return (ret); + return ret; } /* FIXME: there must be a cleaner way to express what happens */ diff --git a/drivers/s390/cio/s390io.c b/drivers/s390/cio/s390io.c index 55949ea71dfb..163a16512ac9 100644 --- a/drivers/s390/cio/s390io.c +++ b/drivers/s390/cio/s390io.c @@ -565,17 +565,13 @@ read_dev_chars (int irq, void **buffer, int length) char dbf_txt[15]; - if (!buffer || !length) { - return (-EINVAL); - - } + if (!buffer || !length) + return -EINVAL; SANITY_CHECK (irq); - if (ioinfo[irq]->ui.flags.oper == 0) { - return (-ENODEV); - - } + if (ioinfo[irq]->ui.flags.oper == 0) + return -ENODEV; sprintf (dbf_txt, "rddevch%x", irq); CIO_TRACE_EVENT (4, dbf_txt); @@ -687,11 +683,11 @@ read_conf_data (int irq, void **buffer, int *length, __u8 lpm) SANITY_CHECK (irq); if (!buffer || !length) { - return (-EINVAL); + return -EINVAL; } else if (ioinfo[irq]->ui.flags.oper == 0) { - return (-ENODEV); + return -ENODEV; } else if (ioinfo[irq]->ui.flags.esid == 0) { - return (-EOPNOTSUPP); + return -EOPNOTSUPP; } @@ -857,7 +853,7 @@ read_conf_data (int irq, void **buffer, int *length, __u8 lpm) } - return (ret); + return ret; } @@ -1299,7 +1295,7 @@ s390_validate_subchannel (int irq, int enable) } - return (ret); + return ret; } /* @@ -1336,10 +1332,8 @@ s390_SenseID (int irq, senseid_t * sid, __u8 lpm) int i; int failure = 0; /* nothing went wrong yet */ - SANITY_CHECK (irq); - if (ioinfo[irq]->ui.flags.oper == 0) { - return (-ENODEV); + return -ENODEV; } @@ -1939,10 +1933,8 @@ s390_SetPGID (int irq, __u8 lpm, pgid_t * pgid) int inlreq = 0; /* inline request_irq() */ int mpath = 1; /* try multi-path first */ - SANITY_CHECK (irq); - if (ioinfo[irq]->ui.flags.oper == 0) { - return (-ENODEV); + return -ENODEV; } @@ -2150,7 +2142,7 @@ s390_SetPGID (int irq, __u8 lpm, pgid_t * pgid) if (inlreq) free_irq (irq, pdevstat); - return (irq_ret); + return irq_ret; } /* @@ -2172,10 +2164,8 @@ s390_SensePGID (int irq, __u8 lpm, pgid_t * pgid) int inlreq = 0; /* inline request_irq() */ unsigned long flags; - SANITY_CHECK (irq); - if (ioinfo[irq]->ui.flags.oper == 0) { - return (-ENODEV); + return -ENODEV; } @@ -2339,7 +2329,7 @@ s390_SensePGID (int irq, __u8 lpm, pgid_t * pgid) if (inlreq) free_irq (irq, pdevstat); - return (irq_ret); + return irq_ret; } /* @@ -2360,8 +2350,6 @@ s390_send_nop(int irq, __u8 lpm) int irq_ret = 0; int inlreq = 0; - SANITY_CHECK(irq); - if (!ioinfo[irq]->ui.flags.oper) /* no sense in trying */ return -ENODEV; diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index 234a21ad851a..df578c454da2 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -314,7 +314,7 @@ s390_machine_check_handler(void *parm) } while (1); - return (0); + return 0; } /* @@ -557,7 +557,7 @@ s390_collect_crw_info(void) } while (ccode == 0); - return (count); + return count; } #ifdef CONFIG_MACHCHK_WARNING @@ -587,6 +587,6 @@ s390_post_warning(void) DBG(KERN_DEBUG "post_warning : 1 warning machine check posted\n"); - return (1); + return 1; } #endif diff --git a/include/asm-s390/debug.h b/include/asm-s390/debug.h index e46698ba43b1..56a4043a1155 100644 --- a/include/asm-s390/debug.h +++ b/include/asm-s390/debug.h @@ -160,7 +160,8 @@ debug_text_event(debug_info_t* id, int level, const char* txt) } extern debug_entry_t * -debug_sprintf_event(debug_info_t* id,int level,char *string,...); +debug_sprintf_event(debug_info_t* id,int level,char *string,...) + __attribute__ ((format(printf, 3, 4))); extern inline debug_entry_t* @@ -195,7 +196,8 @@ debug_text_exception(debug_info_t* id, int level, const char* txt) extern debug_entry_t * -debug_sprintf_exception(debug_info_t* id,int level,char *string,...); +debug_sprintf_exception(debug_info_t* id,int level,char *string,...) + __attribute__ ((format(printf, 3, 4))); int debug_register_view(debug_info_t* id, struct debug_view* view); int debug_unregister_view(debug_info_t* id, struct debug_view* view); diff --git a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h index d5ad56f99aa4..bf2f3bf7b5a3 100644 --- a/include/asm-s390/lowcore.h +++ b/include/asm-s390/lowcore.h @@ -176,25 +176,16 @@ struct _lowcore __u8 pad12[0x1000-0xe04]; /* 0xe04 */ } __attribute__((packed)); /* End structure*/ +#define S390_lowcore (*((struct _lowcore *) 0)) +extern struct _lowcore *lowcore_ptr[]; + extern __inline__ void set_prefix(__u32 address) { __asm__ __volatile__ ("spx %0" : : "m" (address) : "memory" ); } -#define S390_lowcore (*((struct _lowcore *) 0)) -extern struct _lowcore *lowcore_ptr[]; - -#ifndef CONFIG_SMP -#define get_cpu_lowcore(cpu) (&S390_lowcore) -#define safe_get_cpu_lowcore(cpu) (&S390_lowcore) -#else -#define get_cpu_lowcore(cpu) (lowcore_ptr[(cpu)]) -#define safe_get_cpu_lowcore(cpu) \ - ((cpu) == smp_processor_id() ? &S390_lowcore : lowcore_ptr[(cpu)]) -#endif -#endif /* __ASSEMBLY__ */ - #define __PANIC_MAGIC 0xDEADC0DE #endif +#endif diff --git a/include/asm-s390/system.h b/include/asm-s390/system.h index 4e8bc560a200..3aa63017653a 100644 --- a/include/asm-s390/system.h +++ b/include/asm-s390/system.h @@ -184,18 +184,18 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) #define local_irq_enable() ({ \ __u8 __dummy; \ __asm__ __volatile__ ( \ - "stosm 0(%0),0x03" : : "a" (&__dummy) : "memory"); \ + "stosm 0(%1),0x03" : "=m" (__dummy) : "a" (&__dummy) ); \ }) #define local_irq_disable() ({ \ __u32 __flags; \ __asm__ __volatile__ ( \ - "stnsm 0(%0),0xFC" : : "a" (&__flags) : "memory"); \ + "stnsm 0(%1),0xFC" : "=m" (__flags) : "a" (&__flags) ); \ __flags; \ }) #define local_save_flags(x) \ - __asm__ __volatile__("stosm 0(%0),0" : : "a" (&x) : "memory") + __asm__ __volatile__("stosm 0(%1),0" : "=m" (x) : "a" (&x) ) #define local_irq_restore(x) \ __asm__ __volatile__("ssm 0(%0)" : : "a" (&x) : "memory") diff --git a/include/asm-s390x/debug.h b/include/asm-s390x/debug.h index e46698ba43b1..56a4043a1155 100644 --- a/include/asm-s390x/debug.h +++ b/include/asm-s390x/debug.h @@ -160,7 +160,8 @@ debug_text_event(debug_info_t* id, int level, const char* txt) } extern debug_entry_t * -debug_sprintf_event(debug_info_t* id,int level,char *string,...); +debug_sprintf_event(debug_info_t* id,int level,char *string,...) + __attribute__ ((format(printf, 3, 4))); extern inline debug_entry_t* @@ -195,7 +196,8 @@ debug_text_exception(debug_info_t* id, int level, const char* txt) extern debug_entry_t * -debug_sprintf_exception(debug_info_t* id,int level,char *string,...); +debug_sprintf_exception(debug_info_t* id,int level,char *string,...) + __attribute__ ((format(printf, 3, 4))); int debug_register_view(debug_info_t* id, struct debug_view* view); int debug_unregister_view(debug_info_t* id, struct debug_view* view); diff --git a/include/asm-s390x/lowcore.h b/include/asm-s390x/lowcore.h index 4ed57785ffaf..fb7cdb090813 100644 --- a/include/asm-s390x/lowcore.h +++ b/include/asm-s390x/lowcore.h @@ -194,25 +194,17 @@ struct _lowcore __u8 pad17[0x2000-0x1400]; /* 0x1400 */ } __attribute__((packed)); /* End structure*/ +#define S390_lowcore (*((struct _lowcore *) 0)) +extern struct _lowcore *lowcore_ptr[]; + extern __inline__ void set_prefix(__u32 address) { __asm__ __volatile__ ("spx %0" : : "m" (address) : "memory" ); } -#define S390_lowcore (*((struct _lowcore *) 0)) -extern struct _lowcore *lowcore_ptr[]; +#define __PANIC_MAGIC 0xDEADC0DE -#ifndef CONFIG_SMP -#define get_cpu_lowcore(cpu) (&S390_lowcore) -#define safe_get_cpu_lowcore(cpu) (&S390_lowcore) -#else -#define get_cpu_lowcore(cpu) (lowcore_ptr[(cpu)]) -#define safe_get_cpu_lowcore(cpu) \ - ((cpu) == smp_processor_id() ? &S390_lowcore : lowcore_ptr[(cpu)]) #endif -#endif /* __ASSEMBLY__ */ - -#define __PANIC_MAGIC 0xDEADC0DE #endif diff --git a/include/asm-s390x/system.h b/include/asm-s390x/system.h index 1189995c4f65..a77c1995018d 100644 --- a/include/asm-s390x/system.h +++ b/include/asm-s390x/system.h @@ -202,21 +202,21 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) #define local_irq_enable() ({ \ unsigned long __dummy; \ __asm__ __volatile__ ( \ - "stosm 0(%0),0x03" : : "a" (&__dummy) : "memory"); \ + "stosm 0(%1),0x03" : "=m" (__dummy) : "a" (&__dummy) ); \ }) #define local_irq_disable() ({ \ unsigned long __flags; \ __asm__ __volatile__ ( \ - "stnsm 0(%0),0xFC" : : "a" (&__flags) : "memory"); \ + "stnsm 0(%1),0xfc" : "=m" (__flags) : "a" (&__flags) ); \ __flags; \ }) #define local_save_flags(x) \ - __asm__ __volatile__("stosm 0(%0),0" : : "a" (&x) : "memory") + __asm__ __volatile__("stosm 0(%1),0" : "=m" (x) : "a" (&x) ) #define local_irq_restore(x) \ - __asm__ __volatile__("ssm 0(%0)" : : "a" (&x) : "memory") + __asm__ __volatile__("ssm 0(%0)" : : "a" (&x) ) #define irqs_disabled() \ ({ \ -- cgit v1.2.3 From 91b9f2e464f90d211734420a9573c302450c9f60 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 3 Oct 2002 21:49:01 -0700 Subject: [PATCH] s390 update (18/27): fpu registers. Cleanup load/store of fpu register on s390. --- arch/s390/kernel/Makefile | 2 +- arch/s390/kernel/process.c | 35 ++++++----- arch/s390/kernel/s390fpu.c | 138 ------------------------------------------- arch/s390/kernel/signal.c | 55 +++++++++-------- arch/s390x/kernel/Makefile | 2 +- arch/s390x/kernel/process.c | 13 ++-- arch/s390x/kernel/s390fpu.c | 87 --------------------------- arch/s390x/kernel/signal.c | 49 ++++++++------- arch/s390x/kernel/signal32.c | 87 +++++++++++++-------------- include/asm-s390/system.h | 85 ++++++++++++++++++++------ include/asm-s390x/system.h | 73 +++++++++++++++++------ 11 files changed, 242 insertions(+), 384 deletions(-) delete mode 100644 arch/s390/kernel/s390fpu.c delete mode 100644 arch/s390x/kernel/s390fpu.c (limited to 'include') diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 5b804b0acb3f..839941e154d6 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -8,7 +8,7 @@ EXTRA_AFLAGS := -traditional export-objs := debug.o ebcdic.o s390_ext.o smp.o s390_ksyms.o obj-y := entry.o bitmap.o traps.o time.o process.o \ setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ - semaphore.o s390fpu.o reipl.o s390_ext.o debug.o + semaphore.o reipl.o s390_ext.o debug.o obj-$(CONFIG_MODULES) += s390_ksyms.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index dd0c32df7485..72bb0b10cf47 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -199,8 +199,13 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long new_stackp, /* fake return stack for resume(), don't go back to schedule */ frame->gprs[9] = (unsigned long) frame; - /* save fprs, if used in last task */ - save_fp_regs(&p->thread.fp_regs); + /* + * save fprs to current->thread.fp_regs to merge them with + * the emulated registers and then copy the result to the child. + */ + save_fp_regs(¤t->thread.fp_regs); + memcpy(&p->thread.fp_regs, ¤t->thread.fp_regs, + sizeof(s390_fp_regs)); p->thread.user_seg = __pa((unsigned long) p->mm->pgd) | _SEGMENT_TABLE; /* start process with ar4 pointing to the correct address space */ p->thread.ar4 = get_fs().ar4; @@ -262,20 +267,13 @@ asmlinkage int sys_execve(struct pt_regs regs) error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, (char **) regs.gprs[3], (char **) regs.gprs[4], ®s); - if (error == 0) - { + error = do_execve(filename, (char **) regs.gprs[3], + (char **) regs.gprs[4], ®s); + if (error == 0) { current->ptrace &= ~PT_DTRACE; - current->thread.fp_regs.fpc=0; - if(MACHINE_HAS_IEEE) - { - __asm__ __volatile__ - ("sr 0,0\n\t" - "sfpc 0,0\n\t" - : - : - :"0"); - } + current->thread.fp_regs.fpc = 0; + if (MACHINE_HAS_IEEE) + asm volatile("sfpc %0,%0" : : "d" (0)); } putname(filename); out: @@ -288,7 +286,12 @@ out: */ int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs) { - save_fp_regs(fpregs); + /* + * save fprs to current->thread.fp_regs to merge them with + * the emulated registers and then copy the result to the dump. + */ + save_fp_regs(¤t->thread.fp_regs); + memcpy(fpregs, ¤t->thread.fp_regs, sizeof(s390_fp_regs)); return 1; } diff --git a/arch/s390/kernel/s390fpu.c b/arch/s390/kernel/s390fpu.c deleted file mode 100644 index cc0de96fd03d..000000000000 --- a/arch/s390/kernel/s390fpu.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * arch/s390/kernel/s390fpu.c - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) - * - * s390fpu.h functions for saving & restoring the fpu state. - * - * I couldn't inline these as linux/sched.h included half the world - * & was required to at the task structure. - * & the functions were too complex to make macros from. - * ( & as usual I didn't feel like debugging inline code ). - */ - -#include -#include - -int save_fp_regs1(s390_fp_regs *fpregs) -{ - int has_ieee=MACHINE_HAS_IEEE; -/* - I don't think we can use STE here as this would load - fp registers 0 & 2 into memory locations 0 & 1 etc. - */ - asm volatile ("STD 0,8(%0)\n\t" - "STD 2,24(%0)\n\t" - "STD 4,40(%0)\n\t" - "STD 6,56(%0)" - : - : "a" (fpregs) - : "memory" - ); - if(has_ieee) - { - asm volatile ("STFPC 0(%0)\n\t" - "STD 1,16(%0)\n\t" - "STD 3,32(%0)\n\t" - "STD 5,48(%0)\n\t" - "STD 7,64(%0)\n\t" - "STD 8,72(%0)\n\t" - "STD 9,80(%0)\n\t" - "STD 10,88(%0)\n\t" - "STD 11,96(%0)\n\t" - "STD 12,104(%0)\n\t" - "STD 13,112(%0)\n\t" - "STD 14,120(%0)\n\t" - "STD 15,128(%0)\n\t" - : - : "a" (fpregs) - : "memory" - ); - } - return(has_ieee); -} - - -void save_fp_regs(s390_fp_regs *fpregs) -{ -#if CONFIG_MATHEMU - s390_fp_regs *currentfprs; - - if(!save_fp_regs1(fpregs)) - { - currentfprs=¤t->thread.fp_regs; - fpregs->fpc=currentfprs->fpc; - fpregs->fprs[1].d=currentfprs->fprs[1].d; - fpregs->fprs[3].d=currentfprs->fprs[3].d; - fpregs->fprs[5].d=currentfprs->fprs[5].d; - fpregs->fprs[7].d=currentfprs->fprs[7].d; - memcpy(&fpregs->fprs[8].d,¤tfprs->fprs[8].d,sizeof(freg_t)*8); - } -#else - save_fp_regs1(fpregs); -#endif -} - - -int restore_fp_regs1(s390_fp_regs *fpregs) -{ - int has_ieee=MACHINE_HAS_IEEE; - - /* If we don't mask with the FPC_VALID_MASK here - * we've got a very quick shutdown -h now command - * via a kernel specification exception. - */ - fpregs->fpc&=FPC_VALID_MASK; - asm volatile ("LD 0,8(%0)\n\t" - "LD 2,24(%0)\n\t" - "LD 4,40(%0)\n\t" - "LD 6,56(%0)" - : - : "a" (fpregs) - : "memory" - ); - if(has_ieee) - { - asm volatile ("LFPC 0(%0)\n\t" - "LD 1,16(%0)\n\t" - "LD 3,32(%0)\n\t" - "LD 5,48(%0)\n\t" - "LD 7,64(%0)\n\t" - "LD 8,72(%0)\n\t" - "LD 9,80(%0)\n\t" - "LD 10,88(%0)\n\t" - "LD 11,96(%0)\n\t" - "LD 12,104(%0)\n\t" - "LD 13,112(%0)\n\t" - "LD 14,120(%0)\n\t" - "LD 15,128(%0)\n\t" - : - : "a" (fpregs) - : "memory" - ); - } - return(has_ieee); -} - -void restore_fp_regs(s390_fp_regs *fpregs) -{ -#if CONFIG_MATHEMU - s390_fp_regs *currentfprs; - - if(!restore_fp_regs1(fpregs)) - { - currentfprs=¤t->thread.fp_regs; - currentfprs->fpc=fpregs->fpc; - currentfprs->fprs[1].d=fpregs->fprs[1].d; - currentfprs->fprs[3].d=fpregs->fprs[3].d; - currentfprs->fprs[5].d=fpregs->fprs[5].d; - currentfprs->fprs[7].d=fpregs->fprs[7].d; - memcpy(¤tfprs->fprs[8].d,&fpregs->fprs[8].d,sizeof(freg_t)*8); - } -#else - restore_fp_regs1(fpregs); -#endif -} - diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index f13b392e0d23..ecc9771858fa 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -49,13 +49,14 @@ typedef struct struct ucontext uc; } rt_sigframe; -asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset)); +int do_signal(struct pt_regs *regs, sigset_t *oldset); /* * Atomically swap in the new signal mask, and wait for a signal. */ asmlinkage int -sys_sigsuspend(struct pt_regs * regs,int history0, int history1, old_sigset_t mask) +sys_sigsuspend(struct pt_regs * regs, int history0, int history1, + old_sigset_t mask) { sigset_t saveset; @@ -147,37 +148,39 @@ sys_sigaltstack(const stack_t *uss, stack_t *uoss, struct pt_regs *regs) static int save_sigregs(struct pt_regs *regs,_sigregs *sregs) { int err; - s390_fp_regs fpregs; - err = __copy_to_user(&sregs->regs,regs,sizeof(_s390_regs_common)); - if(!err) - { - save_fp_regs(&fpregs); - err=__copy_to_user(&sregs->fpregs,&fpregs,sizeof(fpregs)); - } - return(err); - + err = __copy_to_user(&sregs->regs, regs, sizeof(_s390_regs_common)); + if (err != 0) + return err; + /* + * We have to store the fp registers to current->thread.fp_regs + * to merge them with the emulated registers. + */ + save_fp_regs(¤t->thread.fp_regs); + return __copy_to_user(&sregs->fpregs, ¤t->thread.fp_regs, + sizeof(s390_fp_regs)); } /* Returns positive number on error */ static int restore_sigregs(struct pt_regs *regs,_sigregs *sregs) { int err; - s390_fp_regs fpregs; - psw_t saved_psw=regs->psw; - err=__copy_from_user(regs,&sregs->regs,sizeof(_s390_regs_common)); - if(!err) - { - regs->trap = -1; /* disable syscall checks */ - regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)| - (regs->psw.mask&PSW_MASK_DEBUGCHANGE); - regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)| - (regs->psw.addr&PSW_ADDR_DEBUGCHANGE); - err=__copy_from_user(&fpregs,&sregs->fpregs,sizeof(fpregs)); - if(!err) - restore_fp_regs(&fpregs); - } - return(err); + + err = __copy_from_user(regs, &sregs->regs, sizeof(_s390_regs_common)); + regs->psw.mask = _USER_PSW_MASK | (regs->psw.mask & PSW_MASK_DEBUGCHANGE); + regs->psw.addr |= _ADDR_31; + if (err) + return err; + + err = __copy_from_user(¤t->thread.fp_regs, &sregs->fpregs, + sizeof(s390_fp_regs)); + current->thread.fp_regs.fpc &= FPC_VALID_MASK; + if (err) + return err; + + restore_fp_regs(¤t->thread.fp_regs); + regs->trap = -1; /* disable syscall checks */ + return 0; } asmlinkage long sys_sigreturn(struct pt_regs *regs) diff --git a/arch/s390x/kernel/Makefile b/arch/s390x/kernel/Makefile index 54c69349197c..09c0fcf2d6cb 100644 --- a/arch/s390x/kernel/Makefile +++ b/arch/s390x/kernel/Makefile @@ -10,7 +10,7 @@ export-objs := debug.o ebcdic.o s390_ext.o smp.o s390_ksyms.o \ obj-y := entry.o bitmap.o traps.o time.o process.o \ setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ - semaphore.o s390fpu.o reipl.o s390_ext.o debug.o + semaphore.o reipl.o s390_ext.o debug.o obj-$(CONFIG_MODULES) += s390_ksyms.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/s390x/kernel/process.c b/arch/s390x/kernel/process.c index 6f536c756351..20b4223c5a72 100644 --- a/arch/s390x/kernel/process.c +++ b/arch/s390x/kernel/process.c @@ -257,15 +257,12 @@ asmlinkage int sys_execve(struct pt_regs regs) error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, (char **) regs.gprs[3], (char **) regs.gprs[4], ®s); - if (error == 0) - { + error = do_execve(filename, (char **) regs.gprs[3], + (char **) regs.gprs[4], ®s); + if (error == 0) { current->ptrace &= ~PT_DTRACE; - current->thread.fp_regs.fpc=0; - __asm__ __volatile__ - ("sr 0,0\n\t" - "sfpc 0,0\n\t" - : : :"0"); + current->thread.fp_regs.fpc = 0; + asm volatile("sfpc %0,%0" : : "d" (0)); } putname(filename); out: diff --git a/arch/s390x/kernel/s390fpu.c b/arch/s390x/kernel/s390fpu.c deleted file mode 100644 index 7dfea3fbac8b..000000000000 --- a/arch/s390x/kernel/s390fpu.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * arch/s390/kernel/s390fpu.c - * - * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) - * - * s390fpu.h functions for saving & restoring the fpu state. - * - * I couldn't inline these as linux/sched.h included half the world - * & was required to at the task structure. - * & the functions were too complex to make macros from. - * ( & as usual I didn't feel like debugging inline code ). - */ - -#include - -void save_fp_regs(s390_fp_regs *fpregs) -{ -/* - * I don't think we can use STE here as this would load - * fp registers 0 & 2 into memory locations 0 & 1 etc. - */ - asm volatile ("STFPC 0(%0)\n\t" - "STD 0,8(%0)\n\t" - "STD 1,16(%0)\n\t" - "STD 2,24(%0)\n\t" - "STD 3,32(%0)\n\t" - "STD 4,40(%0)\n\t" - "STD 5,48(%0)\n\t" - "STD 6,56(%0)\n\t" - "STD 7,64(%0)\n\t" - "STD 8,72(%0)\n\t" - "STD 9,80(%0)\n\t" - "STD 10,88(%0)\n\t" - "STD 11,96(%0)\n\t" - "STD 12,104(%0)\n\t" - "STD 13,112(%0)\n\t" - "STD 14,120(%0)\n\t" - "STD 15,128(%0)\n\t" - : - : "a" (fpregs) - : "memory" - ); -} - -void restore_fp_regs(s390_fp_regs *fpregs) -{ - /* If we don't mask with the FPC_VALID_MASK here - * we've got a very quick shutdown -h now command - * via a kernel specification exception. - */ - fpregs->fpc&=FPC_VALID_MASK; - asm volatile ("LFPC 0(%0)\n\t" - "LD 0,8(%0)\n\t" - "LD 1,16(%0)\n\t" - "LD 2,24(%0)\n\t" - "LD 3,32(%0)\n\t" - "LD 4,40(%0)\n\t" - "LD 5,48(%0)\n\t" - "LD 6,56(%0)\n\t" - "LD 7,64(%0)\n\t" - "LD 8,72(%0)\n\t" - "LD 9,80(%0)\n\t" - "LD 10,88(%0)\n\t" - "LD 11,96(%0)\n\t" - "LD 12,104(%0)\n\t" - "LD 13,112(%0)\n\t" - "LD 14,120(%0)\n\t" - "LD 15,128(%0)\n\t" - : - : "a" (fpregs) - : "memory" - ); -} - - - - - - - - - - - - diff --git a/arch/s390x/kernel/signal.c b/arch/s390x/kernel/signal.c index 0215decea5ba..6d4d9fe2812f 100644 --- a/arch/s390x/kernel/signal.c +++ b/arch/s390x/kernel/signal.c @@ -144,40 +144,37 @@ sys_sigaltstack(const stack_t *uss, stack_t *uoss, struct pt_regs *regs) /* Returns non-zero on fault */ -static int save_sigregs(struct pt_regs *regs,_sigregs *sregs) +static int save_sigregs(struct pt_regs *regs, _sigregs *sregs) { int err; - s390_fp_regs fpregs; - err = __copy_to_user(&sregs->regs,regs,sizeof(_s390_regs_common)); - if(!err) - { - save_fp_regs(&fpregs); - err=__copy_to_user(&sregs->fpregs,&fpregs,sizeof(fpregs)); - } - return(err); - + err = __copy_to_user(&sregs->regs, regs, sizeof(_s390_regs_common)); + if (err != 0) + return err; + save_fp_regs(¤t->thread.fp_regs); + return __copy_to_user(&sregs->fpregs, ¤t->thread.fp_regs, + sizeof(s390_fp_regs)); } /* Returns positive number on error */ -static int restore_sigregs(struct pt_regs *regs,_sigregs *sregs) +static int restore_sigregs(struct pt_regs *regs, _sigregs *sregs) { int err; - s390_fp_regs fpregs; - psw_t saved_psw=regs->psw; - err=__copy_from_user(regs,&sregs->regs,sizeof(_s390_regs_common)); - if(!err) - { - regs->trap = -1; /* disable syscall checks */ - regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)| - (regs->psw.mask&PSW_MASK_DEBUGCHANGE); - regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)| - (regs->psw.addr&PSW_ADDR_DEBUGCHANGE); - err=__copy_from_user(&fpregs,&sregs->fpregs,sizeof(fpregs)); - if(!err) - restore_fp_regs(&fpregs); - } - return(err); + + err = __copy_from_user(regs, &sregs->regs, sizeof(_s390_regs_common)); + regs->psw.mask = _USER_PSW_MASK | (regs->psw.mask & PSW_MASK_DEBUGCHANGE); + if (err) + return err; + + err = __copy_from_user(¤t->thread.fp_regs, &sregs->fpregs, + sizeof(s390_fp_regs)); + current->thread.fp_regs.fpc &= FPC_VALID_MASK; + if (err) + return err; + + restore_fp_regs(¤t->thread.fp_regs); + regs->trap = -1; /* disable syscall checks */ + return 0; } asmlinkage long sys_sigreturn(struct pt_regs *regs) diff --git a/arch/s390x/kernel/signal32.c b/arch/s390x/kernel/signal32.c index 7e52bc68df9e..c16a04d33eea 100644 --- a/arch/s390x/kernel/signal32.c +++ b/arch/s390x/kernel/signal32.c @@ -32,7 +32,11 @@ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) +#define _ADDR_31 0x80000000 +#define _USER_PSW_MASK_EMU32 0x070DC000 #define _USER_PSW_MASK32 0x0705C00080000000 +#define PSW_MASK_DEBUGCHANGE32 0x00003000UL +#define PSW_ADDR_DEBUGCHANGE32 0x7FFFFFFFUL typedef struct { @@ -290,55 +294,48 @@ sys32_sigaltstack(const stack_t32 *uss, stack_t32 *uoss, struct pt_regs *regs) static int save_sigregs32(struct pt_regs *regs,_sigregs32 *sregs) { - int err = 0; - s390_fp_regs fpregs; - int i; - - for(i=0; igprs[i], &sregs->regs.gprs[i]); - for(i=0; iacrs[i], &sregs->regs.acrs[i]); - err |= __copy_to_user(&sregs->regs.psw.mask, ®s->psw.mask, 4); - err |= __copy_to_user(&sregs->regs.psw.addr, ((char*)®s->psw.addr)+4, 4); - if(!err) - { - save_fp_regs(&fpregs); - __put_user(fpregs.fpc, &sregs->fpregs.fpc); - for(i=0; ifpregs.fprs[i].d); - } - return(err); - + _s390_regs_common32 regs32; + int err, i; + + regs32.psw.mask = _USER_PSW_MASK_EMU32 | + (__u32)((regs->psw.mask & PSW_MASK_DEBUGCHANGE) >> 32); + regs32.psw.addr = _ADDR_31 | (__u32) regs->psw.addr; + for (i = 0; i < NUM_GPRS; i++) + regs32.gprs[i] = (__u32) regs->gprs[i]; + memcpy(regs32.acrs, regs->acrs, sizeof(regs32.acrs)); + err = __copy_to_user(&sregs->regs, ®s32, sizeof(regs32)); + if (err) + return err; + save_fp_regs(¤t->thread.fp_regs); + /* s390_fp_regs and _s390_fp_regs32 are the same ! */ + return __copy_to_user(&sregs->fpregs, ¤t->thread.fp_regs, + sizeof(_s390_fp_regs32)); } static int restore_sigregs32(struct pt_regs *regs,_sigregs32 *sregs) { - int err = 0; - s390_fp_regs fpregs; - psw_t saved_psw=regs->psw; - int i; - - for(i=0; igprs[i], &sregs->regs.gprs[i]); - for(i=0; iacrs[i], &sregs->regs.acrs[i]); - err |= __copy_from_user(®s->psw.mask, &sregs->regs.psw.mask, 4); - err |= __copy_from_user(((char*)®s->psw.addr)+4, &sregs->regs.psw.addr, 4); - - if(!err) - { - regs->trap = -1; /* disable syscall checks */ - regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)| - (regs->psw.mask&PSW_MASK_DEBUGCHANGE); - regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)| - (regs->psw.addr&PSW_ADDR_DEBUGCHANGE); - __get_user(fpregs.fpc, &sregs->fpregs.fpc); - for(i=0; ifpregs.fprs[i].d); - if(!err) - restore_fp_regs(&fpregs); - } - return(err); + _s390_regs_common32 regs32; + int err, i; + + err = __copy_from_user(®s32, &sregs->regs, sizeof(regs32)); + if (err) + return err; + regs->psw.mask = _USER_PSW_MASK32 | + (__u64)(regs32.psw.mask & PSW_MASK_DEBUGCHANGE32) << 32; + regs->psw.addr = (__u64)(regs32.psw.addr & PSW_ADDR_DEBUGCHANGE32); + for (i = 0; i < NUM_GPRS; i++) + regs->gprs[i] = (__u64) regs32.gprs[i]; + memcpy(regs->acrs, regs32.acrs, sizeof(regs32.acrs)); + + err = __copy_from_user(¤t->thread.fp_regs, &sregs->fpregs, + sizeof(_s390_fp_regs32)); + current->thread.fp_regs.fpc &= FPC_VALID_MASK; + if (err) + return err; + + restore_fp_regs(¤t->thread.fp_regs); + regs->trap = -1; /* disable syscall checks */ + return 0; } asmlinkage long sys32_sigreturn(struct pt_regs *regs) diff --git a/include/asm-s390/system.h b/include/asm-s390/system.h index 3aa63017653a..e0a825fdc763 100644 --- a/include/asm-s390/system.h +++ b/include/asm-s390/system.h @@ -12,22 +12,79 @@ #define __ASM_SYSTEM_H #include +#include #include +#include +#include + #ifdef __KERNEL__ -#include -#endif -#include + +struct task_struct; + +extern struct task_struct *resume(void *, void *); + +static inline void save_fp_regs(s390_fp_regs *fpregs) +{ + asm volatile ( + " std 0,8(%0)\n" + " std 2,24(%0)\n" + " std 4,40(%0)\n" + " std 6,56(%0)" + : : "a" (fpregs) : "memory" ); + if (!MACHINE_HAS_IEEE) + return; + asm volatile( + " stfpc 0(%0)\n" + " std 1,16(%0)\n" + " std 3,32(%0)\n" + " std 5,48(%0)\n" + " std 7,64(%0)\n" + " std 8,72(%0)\n" + " std 9,80(%0)\n" + " std 10,88(%0)\n" + " std 11,96(%0)\n" + " std 12,104(%0)\n" + " std 13,112(%0)\n" + " std 14,120(%0)\n" + " std 15,128(%0)\n" + : : "a" (fpregs) : "memory" ); +} + +static inline void restore_fp_regs(s390_fp_regs *fpregs) +{ + asm volatile ( + " ld 0,8(%0)\n" + " ld 2,24(%0)\n" + " ld 4,40(%0)\n" + " ld 6,56(%0)" + : : "a" (fpregs)); + if (!MACHINE_HAS_IEEE) + return; + asm volatile( + " lfpc 0(%0)\n" + " ld 1,16(%0)\n" + " ld 3,32(%0)\n" + " ld 5,48(%0)\n" + " ld 7,64(%0)\n" + " ld 8,72(%0)\n" + " ld 9,80(%0)\n" + " ld 10,88(%0)\n" + " ld 11,96(%0)\n" + " ld 12,104(%0)\n" + " ld 13,112(%0)\n" + " ld 14,120(%0)\n" + " ld 15,128(%0)\n" + : : "a" (fpregs)); +} #define switch_to(prev,next,last) do { \ if (prev == next) \ break; \ - save_fp_regs1(&prev->thread.fp_regs); \ - restore_fp_regs1(&next->thread.fp_regs); \ + save_fp_regs(&prev->thread.fp_regs); \ + restore_fp_regs(&next->thread.fp_regs); \ resume(prev,next); \ } while (0) -struct task_struct; - #define nop() __asm__ __volatile__ ("nop") #define xchg(ptr,x) \ @@ -281,23 +338,13 @@ extern void smp_ctl_clear_bit(int cr, int bit); #define ctl_set_bit(cr, bit) __ctl_set_bit(cr, bit) #define ctl_clear_bit(cr, bit) __ctl_clear_bit(cr, bit) -#endif - -#ifdef __KERNEL__ -extern struct task_struct *resume(void *, void *); - -extern int save_fp_regs1(s390_fp_regs *fpregs); -extern void save_fp_regs(s390_fp_regs *fpregs); -extern int restore_fp_regs1(s390_fp_regs *fpregs); -extern void restore_fp_regs(s390_fp_regs *fpregs); +#endif /* CONFIG_SMP */ extern void (*_machine_restart)(char *command); extern void (*_machine_halt)(void); extern void (*_machine_power_off)(void); -#endif +#endif /* __KERNEL__ */ #endif - - diff --git a/include/asm-s390x/system.h b/include/asm-s390x/system.h index a77c1995018d..74470fc09edd 100644 --- a/include/asm-s390x/system.h +++ b/include/asm-s390x/system.h @@ -12,11 +12,62 @@ #define __ASM_SYSTEM_H #include +#include #include +#include +#include + #ifdef __KERNEL__ -#include -#endif -#include + +struct task_struct; + +extern struct task_struct *resume(void *, void *); + +static inline void save_fp_regs(s390_fp_regs *fpregs) +{ + asm volatile ( + " stfpc 0(%0)\n" + " std 0,8(%0)\n" + " std 1,16(%0)\n" + " std 2,24(%0)\n" + " std 3,32(%0)\n" + " std 4,40(%0)\n" + " std 5,48(%0)\n" + " std 6,56(%0)\n" + " std 7,64(%0)\n" + " std 8,72(%0)\n" + " std 9,80(%0)\n" + " std 10,88(%0)\n" + " std 11,96(%0)\n" + " std 12,104(%0)\n" + " std 13,112(%0)\n" + " std 14,120(%0)\n" + " std 15,128(%0)\n" + : : "a" (fpregs) : "memory" ); +} + +static inline void restore_fp_regs(s390_fp_regs *fpregs) +{ + asm volatile ( + " lfpc 0(%0)\n" + " ld 0,8(%0)\n" + " ld 1,16(%0)\n" + " ld 2,24(%0)\n" + " ld 3,32(%0)\n" + " ld 4,40(%0)\n" + " ld 5,48(%0)\n" + " ld 6,56(%0)\n" + " ld 7,64(%0)\n" + " ld 8,72(%0)\n" + " ld 9,80(%0)\n" + " ld 10,88(%0)\n" + " ld 11,96(%0)\n" + " ld 12,104(%0)\n" + " ld 13,112(%0)\n" + " ld 14,120(%0)\n" + " ld 15,128(%0)\n" + : : "a" (fpregs)); +} #define switch_to(prev,next,last) do { \ if (prev == next) \ @@ -26,8 +77,6 @@ resume(prev,next); \ } while (0) -struct task_struct; - #define nop() __asm__ __volatile__ ("nop") #define xchg(ptr,x) \ @@ -297,23 +346,13 @@ extern void smp_ctl_clear_bit(int cr, int bit); #define ctl_set_bit(cr, bit) __ctl_set_bit(cr, bit) #define ctl_clear_bit(cr, bit) __ctl_clear_bit(cr, bit) -#endif - -#ifdef __KERNEL__ -extern struct task_struct *resume(void *, void *); - -extern int save_fp_regs1(s390_fp_regs *fpregs); -extern void save_fp_regs(s390_fp_regs *fpregs); -extern int restore_fp_regs1(s390_fp_regs *fpregs); -extern void restore_fp_regs(s390_fp_regs *fpregs); +#endif /* CONFIG_SMP */ extern void (*_machine_restart)(char *command); extern void (*_machine_halt)(void); extern void (*_machine_power_off)(void); -#endif +#endif /* __KERNEL __ */ #endif - - -- cgit v1.2.3 From f90dc9f36d0c957335c7c399a226051c48d08839 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 3 Oct 2002 21:49:19 -0700 Subject: [PATCH] s390 update (19/27): ptrace cleanup. Rewrite s390 ptrace code in a more readable and less buggy way. As a part of this, all psw related definitions are moved into ptrace.h from a number of different locations. --- arch/s390/kernel/process.c | 11 +- arch/s390/kernel/ptrace.c | 502 ++++++++++++----------- arch/s390/kernel/setup.c | 41 +- arch/s390/kernel/signal.c | 30 +- arch/s390/kernel/traps.c | 42 +- arch/s390/mm/extable.c | 4 +- arch/s390/mm/fault.c | 12 +- arch/s390x/kernel/linux32.h | 30 +- arch/s390x/kernel/process.c | 9 +- arch/s390x/kernel/ptrace.c | 924 +++++++++++++++++++++--------------------- arch/s390x/kernel/ptrace32.h | 86 ++++ arch/s390x/kernel/setup.c | 45 +- arch/s390x/kernel/signal.c | 28 +- arch/s390x/kernel/signal32.c | 45 +- arch/s390x/kernel/traps.c | 36 +- arch/s390x/mm/fault.c | 10 +- drivers/s390/cio/cio.c | 57 +-- include/asm-s390/lowcore.h | 31 +- include/asm-s390/processor.h | 22 +- include/asm-s390/ptrace.h | 35 +- include/asm-s390x/lowcore.h | 26 -- include/asm-s390x/processor.h | 22 +- include/asm-s390x/ptrace.h | 33 +- 23 files changed, 1046 insertions(+), 1035 deletions(-) create mode 100644 arch/s390x/kernel/ptrace32.h (limited to 'include') diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 72bb0b10cf47..0b0167acf9f3 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -75,9 +75,10 @@ void default_idle(void) /* * Wait for external, I/O or machine check interrupt and - * switch of machine check bit after the wait has ended. + * switch off machine check bit after the wait has ended. */ - wait_psw.mask = _WAIT_PSW_MASK; + wait_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK | PSW_MASK_WAIT | + PSW_MASK_IO | PSW_MASK_EXT; asm volatile ( " basr %0,0\n" "0: la %0,1f-0b(%0)\n" @@ -114,7 +115,7 @@ void show_regs(struct pt_regs *regs) show_registers(regs); /* Show stack backtrace if pt_regs is from kernel mode */ - if (!(regs->psw.mask & PSW_PROBLEM_STATE)) + if (!(regs->psw.mask & PSW_MASK_PSTATE)) show_trace((unsigned long *) regs->gprs[15]); } @@ -135,8 +136,8 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) struct pt_regs regs; memset(®s, 0, sizeof(regs)); - regs.psw.mask = _SVC_PSW_MASK; - regs.psw.addr = (__u32) kernel_thread_starter | _ADDR_31; + regs.psw.mask = PSW_KERNEL_BITS; + regs.psw.addr = (__u32) kernel_thread_starter | PSW_ADDR_AMODE31; regs.gprs[7] = STACK_FRAME_OVERHEAD; regs.gprs[8] = __LC_KERNEL_STACK; regs.gprs[9] = (unsigned long) fn; diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index ff361cbc2abb..c1a2508cfb96 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -4,6 +4,7 @@ * S390 version * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com) * * Based on PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -21,7 +22,6 @@ * this archive for more details. */ -#include #include #include #include @@ -39,169 +39,52 @@ #include -void FixPerRegisters(struct task_struct *task) +static void FixPerRegisters(struct task_struct *task) { - struct pt_regs *regs = __KSTK_PTREGS(task); - per_struct *per_info= - (per_struct *)&task->thread.per_info; + struct pt_regs *regs; + per_struct *per_info; - per_info->control_regs.bits.em_instruction_fetch= - per_info->single_step|per_info->instruction_fetch; + regs = __KSTK_PTREGS(task); + per_info = (per_struct *) &task->thread.per_info; + per_info->control_regs.bits.em_instruction_fetch = + per_info->single_step | per_info->instruction_fetch; - if(per_info->single_step) - { - per_info->control_regs.bits.starting_addr=0; - per_info->control_regs.bits.ending_addr=0x7fffffffUL; + if (per_info->single_step) { + per_info->control_regs.bits.starting_addr = 0; + per_info->control_regs.bits.ending_addr = 0x7fffffffUL; + } else { + per_info->control_regs.bits.starting_addr = + per_info->starting_addr; + per_info->control_regs.bits.ending_addr = + per_info->ending_addr; } + /* + * if any of the control reg tracing bits are on + * we switch on per in the psw + */ + if (per_info->control_regs.words.cr[0] & PER_EM_MASK) + regs->psw.mask |= PSW_MASK_PER; else - { - per_info->control_regs.bits.starting_addr= - per_info->starting_addr; - per_info->control_regs.bits.ending_addr= - per_info->ending_addr; - } - /* if any of the control reg tracing bits are on - we switch on per in the psw */ - if(per_info->control_regs.words.cr[0]&PER_EM_MASK) - regs->psw.mask |=PSW_PER_MASK; - else - regs->psw.mask &= ~PSW_PER_MASK; + regs->psw.mask &= ~PSW_MASK_PER; + if (per_info->control_regs.bits.em_storage_alteration) - { - per_info->control_regs.bits.storage_alt_space_ctl=1; - //((pgd_t *)__pa(task->mm->pgd))->pgd |= USER_STD_MASK; - } + per_info->control_regs.bits.storage_alt_space_ctl = 1; else - { - per_info->control_regs.bits.storage_alt_space_ctl=0; - //((pgd_t *)__pa(task->mm->pgd))->pgd &= ~USER_STD_MASK; - } + per_info->control_regs.bits.storage_alt_space_ctl = 0; } void set_single_step(struct task_struct *task) { - per_struct *per_info= - (per_struct *)&task->thread.per_info; - - per_info->single_step=1; /* Single step */ + task->thread.per_info.single_step = 1; FixPerRegisters(task); } void clear_single_step(struct task_struct *task) { - per_struct *per_info= - (per_struct *)&task->thread.per_info; - - per_info->single_step=0; + task->thread.per_info.single_step = 0; FixPerRegisters(task); } -int ptrace_usercopy(addr_t realuseraddr,addr_t copyaddr,int len,int tofromuser,int writeuser,u32 mask) -{ - u32 tempuser; - int retval=0; - - if(writeuser&&realuseraddr==(addr_t)NULL) - return(0); - if(mask!=0xffffffff) - { - tempuser=*((u32 *)realuseraddr); - if(!writeuser) - { - tempuser&=mask; - realuseraddr=(addr_t)&tempuser; - } - } - if(tofromuser) - { - if(writeuser) - { - retval=copy_from_user((void *)realuseraddr,(void *)copyaddr,len) ? -EFAULT : 0; - } - else - { - if(realuseraddr==(addr_t)NULL) - retval=(clear_user((void *)copyaddr,len) ? -EIO:0); - else - retval=(copy_to_user((void *)copyaddr,(void *)realuseraddr,len) ? -EIO:0); - } - } - else - { - if(writeuser) - memcpy((void *)realuseraddr,(void *)copyaddr,len); - else - memcpy((void *)copyaddr,(void *)realuseraddr,len); - } - if(mask!=0xffffffff&&writeuser) - (*((u32 *)realuseraddr))=(((*((u32 *)realuseraddr))&mask)|(tempuser&~mask)); - return(retval); -} - -int copy_user(struct task_struct *task,saddr_t useraddr,addr_t copyaddr,int len,int tofromuser,int writingtouser) -{ - int copylen=0,copymax; - addr_t realuseraddr; - saddr_t enduseraddr=useraddr+len; - - u32 mask; - - if (useraddr < 0 || enduseraddr > sizeof(struct user)|| - (useraddr < PT_ENDREGS && (useraddr&3))|| - (enduseraddr < PT_ENDREGS && (enduseraddr&3))) - return (-EIO); - while(len>0) - { - mask=0xffffffff; - if(useraddrthread.fp_regs)[useraddr-PT_FPC]); - } - else if(useraddrthread.per_info)[useraddr-PT_CR_9]); - } - else - { - copymax=sizeof(struct user); - realuseraddr=(addr_t)NULL; - } - copylen=copymax-useraddr; - copylen=(copylen>len ? len:copylen); - if(ptrace_usercopy(realuseraddr,copyaddr,copylen,tofromuser,writingtouser,mask)) - return (-EIO); - copyaddr+=copylen; - len-=copylen; - useraddr+=copylen; - } - FixPerRegisters(task); - return(0); -} - /* * Called by kernel/ptrace.c when detaching.. * @@ -213,92 +96,159 @@ void ptrace_disable(struct task_struct *child) clear_single_step(child); } -asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +/* + * Read the word at offset addr from the user area of a process. The + * trouble here is that the information is littered over different + * locations. The process registers are found on the kernel stack, + * the floating point stuff and the trace settings are stored in + * the task structure. In addition the different structures in + * struct user contain pad bytes that should be read as zeroes. + * Lovely... + */ +static int peek_user(struct task_struct *child, addr_t addr, addr_t data) { - struct task_struct *child; - int ret = -EPERM; - unsigned long tmp; - int copied; - ptrace_area parea; + struct user *dummy = NULL; + addr_t offset; + __u32 tmp; + + if ((addr & 3) || addr > sizeof(struct user) - 3) + return -EIO; + + if (addr <= (addr_t) &dummy->regs.orig_gpr2) { + /* + * psw, gprs, acrs and orig_gpr2 are stored on the stack + */ + tmp = *(__u32 *)((addr_t) __KSTK_PTREGS(child) + addr); + + } else if (addr >= (addr_t) &dummy->regs.fp_regs && + addr < (addr_t) (&dummy->regs.fp_regs + 1)) { + /* + * floating point regs. are stored in the thread structure + */ + offset = addr - (addr_t) &dummy->regs.fp_regs; + tmp = *(__u32 *)((addr_t) &child->thread.fp_regs + offset); + + } else if (addr >= (addr_t) &dummy->regs.per_info && + addr < (addr_t) (&dummy->regs.per_info + 1)) { + /* + * per_info is found in the thread structure + */ + offset = addr - (addr_t) &dummy->regs.per_info; + tmp = *(__u32 *)((addr_t) &child->thread.per_info + offset); + + } else + tmp = 0; + + return put_user(tmp, (__u32 *) data); +} + +/* + * Write a word to the user area of a process at location addr. This + * operation does have an additional problem compared to peek_user. + * Stores to the program status word and on the floating point + * control register needs to get checked for validity. + */ +static int poke_user(struct task_struct *child, addr_t addr, addr_t data) +{ + struct user *dummy = NULL; + addr_t offset; + + if ((addr & 3) || addr > sizeof(struct user) - 3) + return -EIO; + + if (addr <= (addr_t) &dummy->regs.orig_gpr2) { + /* + * psw, gprs, acrs and orig_gpr2 are stored on the stack + */ + if (addr == (addr_t) &dummy->regs.psw.mask && + (data & ~PSW_MASK_CC) != PSW_USER_BITS) + /* Invalid psw mask. */ + return -EINVAL; + if (addr == (addr_t) &dummy->regs.psw.addr) + /* I'd like to reject addresses without the + high order bit but older gdb's rely on it */ + data |= PSW_ADDR_AMODE31; + *(__u32 *)((addr_t) __KSTK_PTREGS(child) + addr) = data; + + } else if (addr >= (addr_t) &dummy->regs.fp_regs && + addr < (addr_t) (&dummy->regs.fp_regs + 1)) { + /* + * floating point regs. are stored in the thread structure + */ + if (addr == (addr_t) &dummy->regs.fp_regs.fpc && + (data & ~FPC_VALID_MASK) != 0) + return -EINVAL; + offset = addr - (addr_t) &dummy->regs.fp_regs; + *(__u32 *)((addr_t) &child->thread.fp_regs + offset) = data; + + } else if (addr >= (addr_t) &dummy->regs.per_info && + addr < (addr_t) (&dummy->regs.per_info + 1)) { + /* + * per_info is found in the thread structure + */ + offset = addr - (addr_t) &dummy->regs.per_info; + *(__u32 *)((addr_t) &child->thread.per_info + offset) = data; - lock_kernel(); - 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_tsk; - if (request == PTRACE_ATTACH) - { - ret = ptrace_attach(child); - goto out_tsk; } - ret = -ESRCH; - // printk("child=%lX child->flags=%lX",child,child->flags); - /* I added child!=current line so we can get the */ - /* ieee_instruction_pointer from the user structure DJB */ - if(child!=current) - { - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) - { - if (request != PTRACE_KILL) - goto out_tsk; - } - if (child->parent != current) - goto out_tsk; + + FixPerRegisters(child); + return 0; +} + +static int +do_ptrace(struct task_struct *child, long request, long addr, long data) +{ + unsigned long tmp; + ptrace_area parea; + int copied, ret; + + if (request == PTRACE_ATTACH) + return ptrace_attach(child); + + /* + * I added child != current line so we can get the + * ieee_instruction_pointer from the user structure DJB + */ + if (child != current) { + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + return ret; } - switch (request) - { - /* If I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: - copied = access_process_vm(child,ADDR_BITS_REMOVE(addr), &tmp, sizeof(tmp), 0); - ret = -EIO; + + /* Remove high order bit from address. */ + addr &= PSW_ADDR_INSN; + + switch (request) { + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + /* read word at location addr. */ + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); if (copied != sizeof(tmp)) - break; - ret = put_user(tmp,(unsigned long *) data); - break; + return -EIO; + return put_user(tmp, (unsigned long *) data); - /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: - ret=copy_user(child,addr,data,sizeof(unsigned long),1,0); - break; + /* read the word at location addr in the USER area. */ + return peek_user(child, addr, data); - /* If I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKETEXT: case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child,ADDR_BITS_REMOVE(addr), &data, sizeof(data), 1) == sizeof(data)) - break; - ret = -EIO; - break; - - case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ - ret=copy_user(child,addr,(addr_t)&data,sizeof(unsigned long),0,1); - break; - - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: /* restart after signal. */ - ret = -EIO; + /* write the word at location addr. */ + copied = access_process_vm(child, addr, &data, sizeof(data),1); + if (copied != sizeof(data)) + return -EIO; + return 0; + + case PTRACE_POKEUSR: + /* write the word at location addr in the USER area */ + return poke_user(child, addr, data); + + case PTRACE_SYSCALL: + /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: + /* restart after signal. */ if ((unsigned long) data >= _NSIG) - break; + return -EIO; if (request == PTRACE_SYSCALL) set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); else @@ -307,60 +257,104 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) /* make sure the single step bit is not set. */ clear_single_step(child); wake_up_process(child); - ret = 0; - break; + return 0; -/* - * 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: - ret = 0; + /* + * 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. + */ if (child->state == TASK_ZOMBIE) /* already dead */ - break; + return 0; child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ clear_single_step(child); wake_up_process(child); - /* make sure the single step bit is not set. */ - break; + return 0; - case PTRACE_SINGLESTEP: /* set the trap flag. */ - ret = -EIO; + case PTRACE_SINGLESTEP: + /* set the trap flag. */ if ((unsigned long) data >= _NSIG) - break; + return -EIO; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; set_single_step(child); /* give it a chance to run. */ wake_up_process(child); - ret = 0; - break; + return 0; + + case PTRACE_DETACH: + /* detach a process that was attached. */ + return ptrace_detach(child, data); - case PTRACE_DETACH: /* detach a process that was attached. */ - ret = ptrace_detach(child, data); - break; case PTRACE_PEEKUSR_AREA: case PTRACE_POKEUSR_AREA: - if((ret=copy_from_user(&parea,(void *)addr,sizeof(parea)))==0) - ret=copy_user(child,parea.kernel_addr,parea.process_addr, - parea.len,1,(request==PTRACE_POKEUSR_AREA)); - break; - case PTRACE_SETOPTIONS: { + if (!copy_from_user(&parea, (void *) addr, sizeof(parea))) + return -EFAULT; + addr = parea.kernel_addr; + data = parea.process_addr; + copied = 0; + while (copied < parea.len) { + if (request == PTRACE_PEEKUSR_AREA) + ret = peek_user(child, addr, data); + else + ret = poke_user(child, addr, data); + if (ret) + return ret; + addr += sizeof(unsigned long); + data += sizeof(unsigned long); + copied += sizeof(unsigned long); + } + return 0; + + case PTRACE_SETOPTIONS: if (data & PTRACE_O_TRACESYSGOOD) child->ptrace |= PT_TRACESYSGOOD; else child->ptrace &= ~PT_TRACESYSGOOD; - ret = 0; - break; + return 0; } - default: - ret = -EIO; - break; + return -EIO; +} + +asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret; + + lock_kernel(); + + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + ret = -EPERM; + if (current->ptrace & PT_PTRACED) + goto out; + ret = security_ops->ptrace(current->parent, current); + if (ret) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + goto out; } - out_tsk: + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + 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 = do_ptrace(child, request, addr, data); + put_task_struct(child); - out: +out: unlock_kernel(); return ret; } @@ -371,8 +365,8 @@ asmlinkage void syscall_trace(void) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); + current->exit_code = + SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0); current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 366c673bb101..181a6fce21a2 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -304,7 +304,7 @@ void __init setup_arch(char **cmdline_p) unsigned long start_pfn, end_pfn; static unsigned int smptrap=0; unsigned long delay = 0; - struct _lowcore *lowcore; + struct _lowcore *lc; int i; if (smptrap) @@ -451,27 +451,26 @@ void __init setup_arch(char **cmdline_p) /* * Setup lowcore for boot cpu */ - lowcore = (struct _lowcore *) - __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0); - memset(lowcore, 0, PAGE_SIZE); - lowcore->restart_psw.mask = _RESTART_PSW_MASK; - lowcore->restart_psw.addr = _ADDR_31 + (addr_t) &restart_int_handler; - lowcore->external_new_psw.mask = _EXT_PSW_MASK; - lowcore->external_new_psw.addr = _ADDR_31 + (addr_t) &ext_int_handler; - lowcore->svc_new_psw.mask = _SVC_PSW_MASK; - lowcore->svc_new_psw.addr = _ADDR_31 + (addr_t) &system_call; - lowcore->program_new_psw.mask = _PGM_PSW_MASK; - lowcore->program_new_psw.addr = _ADDR_31 + (addr_t) &pgm_check_handler; - lowcore->mcck_new_psw.mask = _MCCK_PSW_MASK; - lowcore->mcck_new_psw.addr = _ADDR_31 + (addr_t) &mcck_int_handler; - lowcore->io_new_psw.mask = _IO_PSW_MASK; - lowcore->io_new_psw.addr = _ADDR_31 + (addr_t) &io_int_handler; - lowcore->ipl_device = S390_lowcore.ipl_device; - lowcore->kernel_stack = ((__u32) &init_thread_union) + 8192; - lowcore->async_stack = (__u32) + lc = (struct _lowcore *) __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0); + memset(lc, 0, PAGE_SIZE); + lc->restart_psw.mask = PSW_BASE_BITS; + lc->restart_psw.addr = PSW_ADDR_AMODE31 + (__u32) restart_int_handler; + lc->external_new_psw.mask = PSW_KERNEL_BITS; + lc->external_new_psw.addr = PSW_ADDR_AMODE31 + (__u32) ext_int_handler; + lc->svc_new_psw.mask = PSW_KERNEL_BITS; + lc->svc_new_psw.addr = PSW_ADDR_AMODE31 + (__u32) system_call; + lc->program_new_psw.mask = PSW_KERNEL_BITS; + lc->program_new_psw.addr = PSW_ADDR_AMODE31 + (__u32)pgm_check_handler; + lc->mcck_new_psw.mask = PSW_KERNEL_BITS; + lc->mcck_new_psw.addr = PSW_ADDR_AMODE31 + (__u32) mcck_int_handler; + lc->io_new_psw.mask = PSW_KERNEL_BITS; + lc->io_new_psw.addr = PSW_ADDR_AMODE31 + (__u32) io_int_handler; + lc->ipl_device = S390_lowcore.ipl_device; + lc->kernel_stack = ((__u32) &init_thread_union) + 8192; + lc->async_stack = (__u32) __alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0) + 8192; - lowcore->jiffy_timer = -1LL; - set_prefix((__u32) lowcore); + lc->jiffy_timer = -1LL; + set_prefix((__u32) lc); cpu_init(); __cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr; diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index ecc9771858fa..3f0eda04e9d8 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -167,8 +167,8 @@ static int restore_sigregs(struct pt_regs *regs,_sigregs *sregs) int err; err = __copy_from_user(regs, &sregs->regs, sizeof(_s390_regs_common)); - regs->psw.mask = _USER_PSW_MASK | (regs->psw.mask & PSW_MASK_DEBUGCHANGE); - regs->psw.addr |= _ADDR_31; + regs->psw.mask = PSW_USER_BITS | (regs->psw.mask & PSW_MASK_CC); + regs->psw.addr |= PSW_ADDR_AMODE31; if (err) return err; @@ -298,9 +298,9 @@ static void setup_frame(int sig, struct k_sigaction *ka, /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { - regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + regs->gprs[14] = (__u32) ka->sa.sa_restorer | PSW_ADDR_AMODE31; } else { - regs->gprs[14] = FIX_PSW(frame->retcode); + regs->gprs[14] = (__u32) frame->retcode | PSW_ADDR_AMODE31; if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, (u16 *)(frame->retcode))) goto give_sigsegv; @@ -311,12 +311,12 @@ static void setup_frame(int sig, struct k_sigaction *ka, goto give_sigsegv; /* Set up registers for signal handler */ - regs->gprs[15] = (addr_t)frame; - regs->psw.addr = FIX_PSW(ka->sa.sa_handler); - regs->psw.mask = _USER_PSW_MASK; + regs->gprs[15] = (__u32) frame; + regs->psw.addr = (__u32) ka->sa.sa_handler | PSW_ADDR_AMODE31; + regs->psw.mask = PSW_USER_BITS; regs->gprs[2] = map_signal(sig); - regs->gprs[3] = (addr_t)&frame->sc; + regs->gprs[3] = (__u32) &frame->sc; /* We forgot to include these in the sigcontext. To avoid breaking binary compatibility, they are passed as args. */ @@ -356,9 +356,9 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { - regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + regs->gprs[14] = (__u32) ka->sa.sa_restorer | PSW_ADDR_AMODE31; } else { - regs->gprs[14] = FIX_PSW(frame->retcode); + regs->gprs[14] = (__u32) frame->retcode | PSW_ADDR_AMODE31; err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, (u16 *)(frame->retcode)); } @@ -368,13 +368,13 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, goto give_sigsegv; /* Set up registers for signal handler */ - regs->gprs[15] = (addr_t)frame; - regs->psw.addr = FIX_PSW(ka->sa.sa_handler); - regs->psw.mask = _USER_PSW_MASK; + regs->gprs[15] = (__u32) frame; + regs->psw.addr = (__u32) ka->sa.sa_handler | PSW_ADDR_AMODE31; + regs->psw.mask = PSW_USER_BITS; regs->gprs[2] = map_signal(sig); - regs->gprs[3] = (addr_t)&frame->info; - regs->gprs[4] = (addr_t)&frame->uc; + regs->gprs[3] = (__u32) &frame->info; + regs->gprs[4] = (__u32) &frame->uc; return; give_sigsegv: diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 5fa9d2ae8338..da15086acc86 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -116,22 +116,22 @@ void show_trace(unsigned long * stack) stack = (unsigned long*)&stack; printk("Call Trace: "); - low_addr = ((unsigned long) stack) & PSW_ADDR_MASK; + 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_MASK; + backchain = *((unsigned long *) low_addr) & PSW_ADDR_INSN; /* Print up to 8 lines */ for (i = 0; i < 8; i++) { if (backchain < low_addr || backchain >= high_addr) break; - ret_addr = *((unsigned long *) (backchain+56)) & PSW_ADDR_MASK; + ret_addr = *((unsigned long *) (backchain+56)) & PSW_ADDR_INSN; if (!kernel_text_address(ret_addr)) break; if (i && ((i % 6) == 0)) printk("\n "); printk("[<%08lx>] ", ret_addr); low_addr = backchain; - backchain = *((unsigned long *) backchain) & PSW_ADDR_MASK; + backchain = *((unsigned long *) backchain) & PSW_ADDR_INSN; } printk("\n"); } @@ -184,7 +184,7 @@ void show_registers(struct pt_regs *regs) char *mode; int i; - mode = (regs->psw.mask & PSW_PROBLEM_STATE) ? "User" : "Krnl"; + mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl"; printk("%s PSW : %08lx %08lx\n", mode, (unsigned long) regs->psw.mask, (unsigned long) regs->psw.addr); @@ -210,7 +210,7 @@ void show_registers(struct pt_regs *regs) * time of the fault. */ old_fs = get_fs(); - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) set_fs(USER_DS); else set_fs(KERNEL_DS); @@ -287,10 +287,10 @@ static void inline do_trap(long interruption_code, int signr, char *str, * We got all needed information from the lowcore and can * now safely switch on interrupts. */ - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) local_irq_enable(); - if (regs->psw.mask & PSW_PROBLEM_STATE) { + if (regs->psw.mask & PSW_MASK_PSTATE) { struct task_struct *tsk = current; tsk->thread.trap_no = interruption_code & 0xffff; @@ -322,12 +322,12 @@ static void inline do_trap(long interruption_code, int signr, char *str, static inline void *get_check_address(struct pt_regs *regs) { - return (void *) ADDR_BITS_REMOVE(regs->psw.addr-S390_lowcore.pgm_ilc); + return (void *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN); } int do_debugger_trap(struct pt_regs *regs,int signal) { - if(regs->psw.mask&PSW_PROBLEM_STATE) + if(regs->psw.mask&PSW_MASK_PSTATE) { if(current->ptrace & PT_PTRACED) force_sig(signal,current); @@ -423,10 +423,10 @@ asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code) * We got all needed information from the lowcore and can * now safely switch on interrupts. */ - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) local_irq_enable(); - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) get_user(*((__u16 *) opcode), location); else *((__u16 *)opcode)=*((__u16 *)location); @@ -436,7 +436,7 @@ asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code) signal = SIGILL; } #ifdef CONFIG_MATHEMU - else if (regs->psw.mask & PSW_PROBLEM_STATE) + else if (regs->psw.mask & PSW_MASK_PSTATE) { if (opcode[0] == 0xb3) { get_user(*((__u16 *) (opcode+2)), location+1); @@ -484,10 +484,10 @@ specification_exception(struct pt_regs * regs, long interruption_code) * We got all needed information from the lowcore and can * now safely switch on interrupts. */ - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) local_irq_enable(); - if (regs->psw.mask & PSW_PROBLEM_STATE) { + if (regs->psw.mask & PSW_MASK_PSTATE) { get_user(*((__u16 *) opcode), location); switch (opcode[0]) { case 0x28: /* LDR Rx,Ry */ @@ -547,7 +547,7 @@ asmlinkage void data_exception(struct pt_regs * regs, long interruption_code) * We got all needed information from the lowcore and can * now safely switch on interrupts. */ - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) local_irq_enable(); if (MACHINE_HAS_IEEE) @@ -555,7 +555,7 @@ asmlinkage void data_exception(struct pt_regs * regs, long interruption_code) : "=m" (current->thread.fp_regs.fpc)); #ifdef CONFIG_MATHEMU - else if (regs->psw.mask & PSW_PROBLEM_STATE) { + else if (regs->psw.mask & PSW_MASK_PSTATE) { __u8 opcode[6]; get_user(*((__u16 *) opcode), location); switch (opcode[0]) { @@ -679,21 +679,19 @@ void __init trap_init(void) void handle_per_exception(struct pt_regs *regs) { - if(regs->psw.mask&PSW_PROBLEM_STATE) - { + if (regs->psw.mask & PSW_MASK_PSTATE) { per_struct *per_info=¤t->thread.per_info; per_info->lowcore.words.perc_atmid=S390_lowcore.per_perc_atmid; per_info->lowcore.words.address=S390_lowcore.per_address; per_info->lowcore.words.access_id=S390_lowcore.per_access_id; } - if(do_debugger_trap(regs,SIGTRAP)) - { + if (do_debugger_trap(regs,SIGTRAP)) { /* I've seen this possibly a task structure being reused ? */ printk("Spurious per exception detected\n"); printk("switching off per tracing for this task.\n"); show_regs(regs); /* Hopefully switching off per tracing will help us survive */ - regs->psw.mask &= ~PSW_PER_MASK; + regs->psw.mask &= ~PSW_MASK_PER; } } diff --git a/arch/s390/mm/extable.c b/arch/s390/mm/extable.c index 7d23869a340e..1ba564ee3d31 100644 --- a/arch/s390/mm/extable.c +++ b/arch/s390/mm/extable.c @@ -48,7 +48,7 @@ search_exception_table(unsigned long addr) addr &= 0x7fffffff; /* remove amode bit from address */ /* There is only the kernel to search. */ ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); - if (ret) ret = FIX_PSW(ret); + if (ret) ret = ret | PSW_ADDR_AMODE31; return ret; #else unsigned long flags; @@ -63,7 +63,7 @@ search_exception_table(unsigned long addr) ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, addr); if (ret) { - ret = FIX_PSW(ret); + ret = ret | PSW_ADDR_AMODE31; break; } } diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index ab523fd4eebf..db7b86042388 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -166,7 +166,7 @@ extern inline void do_exception(struct pt_regs *regs, unsigned long error_code) /* Low-address protection hit in kernel mode means NULL pointer write access in kernel mode. */ - if (!(regs->psw.mask & PSW_PROBLEM_STATE)) { + if (!(regs->psw.mask & PSW_MASK_PSTATE)) { address = 0; user_address = 0; goto no_context; @@ -258,7 +258,7 @@ bad_area: up_read(&mm->mmap_sem); /* User mode accesses just cause a SIGSEGV */ - if (regs->psw.mask & PSW_PROBLEM_STATE) { + 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); @@ -298,7 +298,7 @@ out_of_memory: goto survive; } printk("VM: killing process %s\n", tsk->comm); - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) do_exit(SIGKILL); goto no_context; @@ -314,7 +314,7 @@ do_sigbus: force_sig(SIGBUS, tsk); /* Kernel mode? Handle exceptions or die */ - if (!(regs->psw.mask & PSW_PROBLEM_STATE)) + if (!(regs->psw.mask & PSW_MASK_PSTATE)) goto no_context; } @@ -393,7 +393,7 @@ do_pseudo_page_fault(struct pt_regs *regs, unsigned long error_code) spin_unlock(&pseudo_wait_spinlock); } else { /* Pseudo page faults in kernel mode is a bad idea */ - if (!(regs->psw.mask & PSW_PROBLEM_STATE)) { + if (!(regs->psw.mask & PSW_MASK_PSTATE)) { /* * VM presents pseudo page faults if the interrupted * state was not disabled for interrupts. So we can @@ -528,7 +528,7 @@ pfault_interrupt(struct pt_regs *regs, __u16 error_code) * We got all needed information from the lowcore and can * now safely switch on interrupts. */ - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) local_irq_enable(); if (subcode & 0x0080) { diff --git a/arch/s390x/kernel/linux32.h b/arch/s390x/kernel/linux32.h index 7d16fff63553..d3c08cdef45b 100644 --- a/arch/s390x/kernel/linux32.h +++ b/arch/s390x/kernel/linux32.h @@ -8,8 +8,6 @@ #include #include -#ifdef CONFIG_S390_SUPPORT - /* Macro that masks the high order bit of an 32 bit pointer and converts it*/ /* to a 64 bit pointer */ #define A(__x) ((unsigned long)((__x) & 0x7FFFFFFFUL)) @@ -194,6 +192,32 @@ typedef struct __u32 addr; } _psw_t32 __attribute__ ((aligned(8))); +#define PSW32_MASK_PER 0x40000000UL +#define PSW32_MASK_DAT 0x04000000UL +#define PSW32_MASK_IO 0x02000000UL +#define PSW32_MASK_EXT 0x01000000UL +#define PSW32_MASK_KEY 0x00F00000UL +#define PSW32_MASK_MCHECK 0x00040000UL +#define PSW32_MASK_WAIT 0x00020000UL +#define PSW32_MASK_PSTATE 0x00010000UL +#define PSW32_MASK_ASC 0x0000C000UL +#define PSW32_MASK_CC 0x00003000UL +#define PSW32_MASK_PM 0x00000f00UL + +#define PSW32_ADDR_AMODE31 0x80000000UL +#define PSW32_ADDR_INSN 0x7FFFFFFFUL + +#define PSW32_BASE_BITS 0x00080000UL + +#define PSW32_ASC_PRIMARY 0x00000000UL +#define PSW32_ASC_ACCREG 0x00004000UL +#define PSW32_ASC_SECONDARY 0x00008000UL +#define PSW32_ASC_HOME 0x0000C000UL + +#define PSW32_USER_BITS (PSW32_BASE_BITS | PSW32_MASK_DAT | PSW32_ASC_HOME | \ + PSW32_MASK_IO | PSW32_MASK_EXT | PSW32_MASK_MCHECK | \ + PSW32_MASK_PSTATE) + typedef struct { _psw_t32 psw; @@ -241,6 +265,4 @@ struct ucontext32 { sigset_t32 uc_sigmask; /* mask last for extensibility */ }; -#endif /* !CONFIG_S390_SUPPORT */ - #endif /* _ASM_S390X_S390_H */ diff --git a/arch/s390x/kernel/process.c b/arch/s390x/kernel/process.c index 20b4223c5a72..5c3705dd8c44 100644 --- a/arch/s390x/kernel/process.c +++ b/arch/s390x/kernel/process.c @@ -75,9 +75,10 @@ void default_idle(void) /* * Wait for external, I/O or machine check interrupt and - * switch of machine check bit after the wait has ended. + * switch off machine check bit after the wait has ended. */ - wait_psw.mask = _WAIT_PSW_MASK; + wait_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK | PSW_MASK_WAIT | + PSW_MASK_IO | PSW_MASK_EXT; asm volatile ( " larl %0,0f\n" " stg %0,8(%1)\n" @@ -111,7 +112,7 @@ void show_regs(struct pt_regs *regs) show_registers(regs); /* Show stack backtrace if pt_regs is from kernel mode */ - if (!(regs->psw.mask & PSW_PROBLEM_STATE)) + if (!(regs->psw.mask & PSW_MASK_PSTATE)) show_trace((unsigned long *) regs->gprs[15]); } @@ -132,7 +133,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) struct pt_regs regs; memset(®s, 0, sizeof(regs)); - regs.psw.mask = _SVC_PSW_MASK; + regs.psw.mask = PSW_KERNEL_BITS; regs.psw.addr = (__u64) kernel_thread_starter; regs.gprs[7] = STACK_FRAME_OVERHEAD; regs.gprs[8] = __LC_KERNEL_STACK; diff --git a/arch/s390x/kernel/ptrace.c b/arch/s390x/kernel/ptrace.c index bd3dd773a94b..f6ee9655d457 100644 --- a/arch/s390x/kernel/ptrace.c +++ b/arch/s390x/kernel/ptrace.c @@ -4,6 +4,7 @@ * S390 version * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com) * * Based on PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -38,49 +39,48 @@ #include #include #include + #ifdef CONFIG_S390_SUPPORT -#include "linux32.h" -#else -#define parent_31bit 0 +#include "ptrace32.h" #endif - -void FixPerRegisters(struct task_struct *task) +static void FixPerRegisters(struct task_struct *task) { - struct pt_regs *regs = __KSTK_PTREGS(task); - per_struct *per_info= - (per_struct *)&task->thread.per_info; + struct pt_regs *regs; + per_struct *per_info; + regs = __KSTK_PTREGS(task); + per_info = (per_struct *) &task->thread.per_info; per_info->control_regs.bits.em_instruction_fetch = - per_info->single_step | per_info->instruction_fetch; + per_info->single_step | per_info->instruction_fetch; if (per_info->single_step) { - per_info->control_regs.bits.starting_addr=0; + per_info->control_regs.bits.starting_addr = 0; #ifdef CONFIG_S390_SUPPORT - if (current->thread.flags & S390_FLAG_31BIT) { - per_info->control_regs.bits.ending_addr=0x7fffffffUL; - } - else -#endif - { - per_info->control_regs.bits.ending_addr=-1L; - } + if (current->thread.flags & S390_FLAG_31BIT) + per_info->control_regs.bits.ending_addr = 0x7fffffffUL; + else +#endif + per_info->control_regs.bits.ending_addr = -1; } else { - per_info->control_regs.bits.starting_addr= - per_info->starting_addr; - per_info->control_regs.bits.ending_addr= - per_info->ending_addr; + per_info->control_regs.bits.starting_addr = + per_info->starting_addr; + per_info->control_regs.bits.ending_addr = + per_info->ending_addr; } - /* if any of the control reg tracing bits are on - we switch on per in the psw */ + /* + * if any of the control reg tracing bits are on + * we switch on per in the psw + */ if (per_info->control_regs.words.cr[0] & PER_EM_MASK) - regs->psw.mask |= PSW_PER_MASK; + regs->psw.mask |= PSW_MASK_PER; else - regs->psw.mask &= ~PSW_PER_MASK; - if (per_info->control_regs.bits.storage_alt_space_ctl) - task->thread.user_seg |= USER_STD_MASK; + regs->psw.mask &= ~PSW_MASK_PER; + + if (per_info->control_regs.bits.em_storage_alteration) + per_info->control_regs.bits.storage_alt_space_ctl = 1; else - task->thread.user_seg &= ~USER_STD_MASK; + per_info->control_regs.bits.storage_alt_space_ctl = 0; } void set_single_step(struct task_struct *task) @@ -99,424 +99,406 @@ void clear_single_step(struct task_struct *task) FixPerRegisters (task); } -int ptrace_usercopy(addr_t realuseraddr, addr_t copyaddr, int len, - int tofromuser, int writeuser, unsigned long mask) +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure single step bits etc are not set. + */ +void ptrace_disable(struct task_struct *child) { - unsigned long *realuserptr, *copyptr; - unsigned long tempuser; - int retval; - - retval = 0; - realuserptr = (unsigned long *) realuseraddr; - copyptr = (unsigned long *) copyaddr; - - if (writeuser && realuserptr == NULL) - return 0; - - if (mask != -1L) { - tempuser = *realuserptr; - if (!writeuser) { - tempuser &= mask; - realuserptr = &tempuser; - } - } - if (tofromuser) { - if (writeuser) { - retval = copy_from_user(realuserptr, copyptr, len); - } else { - if (realuserptr == NULL) - retval = clear_user(copyptr, len); - else - retval = copy_to_user(copyptr,realuserptr,len); - retval = retval ? -EIO : 0; - } - } else { - if (writeuser) - memcpy(realuserptr, copyptr, len); - else - memcpy(copyptr, realuserptr, len); - } - if (mask != -1L && writeuser) - *realuserptr = (*realuserptr & mask) | (tempuser & ~mask); - return retval; + /* make sure the single step bit is not set. */ + clear_single_step(child); } -#ifdef CONFIG_S390_SUPPORT - -typedef struct +/* + * Read the word at offset addr from the user area of a process. The + * trouble here is that the information is littered over different + * locations. The process registers are found on the kernel stack, + * the floating point stuff and the trace settings are stored in + * the task structure. In addition the different structures in + * struct user contain pad bytes that should be read as zeroes. + * Lovely... + */ +static int peek_user(struct task_struct *child, addr_t addr, addr_t data) { - __u32 cr[3]; -} per_cr_words32 __attribute__((packed)); + struct user *dummy = NULL; + addr_t offset; + __u64 tmp; + + if ((addr & 7) || addr > sizeof(struct user) - 7) + return -EIO; + + if (addr <= (addr_t) &dummy->regs.orig_gpr2) { + /* + * psw, gprs, acrs and orig_gpr2 are stored on the stack + */ + tmp = *(__u64 *)((addr_t) __KSTK_PTREGS(child) + addr); + + } else if (addr >= (addr_t) &dummy->regs.fp_regs && + addr < (addr_t) (&dummy->regs.fp_regs + 1)) { + /* + * floating point regs. are stored in the thread structure + */ + offset = addr - (addr_t) &dummy->regs.fp_regs; + tmp = *(__u64 *)((addr_t) &child->thread.fp_regs + offset); + + } else if (addr >= (addr_t) &dummy->regs.per_info && + addr < (addr_t) (&dummy->regs.per_info + 1)) { + /* + * per_info is found in the thread structure + */ + offset = addr - (addr_t) &dummy->regs.per_info; + tmp = *(__u64 *)((addr_t) &child->thread.per_info + offset); + + } else + tmp = 0; + + return put_user(tmp, (__u64 *) data); +} -typedef struct +/* + * Write a word to the user area of a process at location addr. This + * operation does have an additional problem compared to peek_user. + * Stores to the program status word and on the floating point + * control register needs to get checked for validity. + */ +static int poke_user(struct task_struct *child, addr_t addr, addr_t data) { - __u16 perc_atmid; /* 0x096 */ - __u32 address; /* 0x098 */ - __u8 access_id; /* 0x0a1 */ -} per_lowcore_words32 __attribute__((packed)); + struct user *dummy = NULL; + addr_t offset; -typedef struct -{ - union { - per_cr_words32 words; - } control_regs __attribute__((packed)); - /* - * Use these flags instead of setting em_instruction_fetch - * directly they are used so that single stepping can be - * switched on & off while not affecting other tracing - */ - unsigned single_step : 1; - unsigned instruction_fetch : 1; - unsigned : 30; - /* - * These addresses are copied into cr10 & cr11 if single - * stepping is switched off - */ - __u32 starting_addr; - __u32 ending_addr; - union { - per_lowcore_words32 words; - } lowcore; -} per_struct32 __attribute__((packed)); - -struct user_regs_struct32 -{ - _psw_t32 psw; - u32 gprs[NUM_GPRS]; - u32 acrs[NUM_ACRS]; - u32 orig_gpr2; - s390_fp_regs fp_regs; - /* - * These per registers are in here so that gdb can modify them - * itself as there is no "official" ptrace interface for hardware - * watchpoints. This is the way intel does it. - */ - per_struct32 per_info; - u32 ieee_instruction_pointer; - /* Used to give failing instruction back to user for ieee exceptions */ -}; - -struct user32 { - /* We start with the registers, to mimic the way that "memory" is returned - from the ptrace(3,...) function. */ - struct user_regs_struct32 regs; /* Where the registers are actually stored */ - /* The rest of this junk is to help gdb figure out what goes where */ - u32 u_tsize; /* Text segment size (pages). */ - u32 u_dsize; /* Data segment size (pages). */ - u32 u_ssize; /* Stack segment size (pages). */ - u32 start_code; /* Starting virtual address of text. */ - u32 start_stack; /* Starting virtual address of stack area. - This is actually the bottom of the stack, - the top of the stack is always found in the - esp register. */ - s32 signal; /* Signal that caused the core dump. */ - u32 u_ar0; /* Used by gdb to help find the values for */ - /* the registers. */ - u32 magic; /* To uniquely identify a core file */ - char u_comm[32]; /* User command that was responsible */ -}; - - -#define PT32_PSWMASK 0x0 -#define PT32_PSWADDR 0x04 -#define PT32_GPR0 0x08 -#define PT32_GPR15 0x44 -#define PT32_ACR0 0x48 -#define PT32_ACR15 0x84 -#define PT32_ORIGGPR2 0x88 -#define PT32_FPC 0x90 -#define PT32_FPR0_HI 0x98 -#define PT32_FPR15_LO 0x114 -#define PT32_CR_9 0x118 -#define PT32_CR_11 0x120 -#define PT32_IEEE_IP 0x13C -#define PT32_LASTOFF PT32_IEEE_IP -#define PT32_ENDREGS 0x140-1 -#define U32OFFSETOF(member) offsetof(struct user32,regs.member) -#define U64OFFSETOF(member) offsetof(struct user,regs.member) -#define U6432DIFF(member) (U64OFFSETOF(member) - U32OFFSETOF(member)) -#define PT_SINGLE_STEP (PT_CR_11+8) -#define PT32_SINGLE_STEP (PT32_CR_11+4) - -#endif /* CONFIG_S390_SUPPORT */ - -int copy_user(struct task_struct *task,saddr_t useraddr, addr_t copyaddr, - int len, int tofromuser, int writingtouser) -{ - int copylen=0,copymax; - addr_t realuseraddr; - saddr_t enduseraddr; - unsigned long mask; -#ifdef CONFIG_S390_SUPPORT - int parent_31bit=current->thread.flags & S390_FLAG_31BIT; - int skip; -#endif - enduseraddr=useraddr+len; - if ((useraddr<0||useraddr&3||enduseraddr&3)|| + if ((addr & 7) || addr > sizeof(struct user) - 7) + return -EIO; + + if (addr <= (addr_t) &dummy->regs.orig_gpr2) { + /* + * psw, gprs, acrs and orig_gpr2 are stored on the stack + */ + if (addr == (addr_t) &dummy->regs.psw.mask && #ifdef CONFIG_S390_SUPPORT - (parent_31bit && enduseraddr > sizeof(struct user32)) || + (data & ~PSW_MASK_CC) != PSW_USER32_BITS && #endif - enduseraddr > sizeof(struct user)) - return (-EIO); + (data & ~PSW_MASK_CC) != PSW_USER_BITS) + /* Invalid psw mask. */ + return -EINVAL; + *(__u64 *)((addr_t) __KSTK_PTREGS(child) + addr) = data; + + } else if (addr >= (addr_t) &dummy->regs.fp_regs && + addr < (addr_t) (&dummy->regs.fp_regs + 1)) { + /* + * floating point regs. are stored in the thread structure + */ + if (addr == (addr_t) &dummy->regs.fp_regs.fpc && + ((data >> 32) & ~FPC_VALID_MASK) != 0) + /* Invalid floating pointer control. */ + return -EINVAL; + offset = addr - (addr_t) &dummy->regs.fp_regs; + *(__u64 *)((addr_t) &child->thread.fp_regs + offset) = data; + + } else if (addr >= (addr_t) &dummy->regs.per_info && + addr < (addr_t) (&dummy->regs.per_info + 1)) { + /* + * per_info is found in the thread structure + */ + offset = addr - (addr_t) &dummy->regs.per_info; + *(__u64 *)((addr_t) &child->thread.per_info + offset) = data; -#ifdef CONFIG_S390_SUPPORT - if(parent_31bit) - { - if(useraddr != PT32_PSWMASK) - { - if (useraddr == PT32_PSWADDR) - useraddr = PT_PSWADDR+4; - else if(useraddr <= PT32_GPR15) - useraddr = ((useraddr-PT32_GPR0)*2) + PT_GPR0+4; - else if(useraddr <= PT32_ACR15) - useraddr += PT_ACR0-PT32_ACR0; - else if(useraddr == PT32_ORIGGPR2) - useraddr = PT_ORIGGPR2+4; - else if(useraddr <= PT32_FPR15_LO) - useraddr += PT_FPR0-PT32_FPR0_HI; - else if(useraddr <= PT32_CR_11) - useraddr = ((useraddr-PT32_CR_9)*2) + PT_CR_9+4; - else if(useraddr == PT32_SINGLE_STEP) - useraddr = PT_SINGLE_STEP; - else if(useraddr <= U32OFFSETOF(per_info.ending_addr)) - useraddr = (((useraddr-U32OFFSETOF(per_info.starting_addr)))*2) + - U64OFFSETOF(per_info.starting_addr)+4; - else if( useraddr == U32OFFSETOF(per_info.lowcore.words.perc_atmid)) - useraddr = U64OFFSETOF(per_info.lowcore.words.perc_atmid); - else if( useraddr == U32OFFSETOF(per_info.lowcore.words.address)) - useraddr = U64OFFSETOF(per_info.lowcore.words.address)+4; - else if(useraddr == U32OFFSETOF(per_info.lowcore.words.access_id)) - useraddr = U64OFFSETOF(per_info.lowcore.words.access_id); - else if(useraddr == PT32_IEEE_IP) - useraddr = PT_IEEE_IP+4; - } } -#endif /* CONFIG_S390_SUPPORT */ - while(len>0) - { -#ifdef CONFIG_S390_SUPPORT - skip=0; -#endif - mask=PSW_ADDR_MASK; - if(useraddrthread.fp_regs)[useraddr-PT_FPC]); - } - else if(useraddrthread.per_info)[useraddr-PT_CR_9]); + ret = poke_user(child, addr, data); + if (ret) + return ret; + addr += sizeof(unsigned long); + data += sizeof(unsigned long); + copied += sizeof(unsigned long); } - else - { - copymax=sizeof(struct user); - realuseraddr=(addr_t)NULL; - } - copylen=copymax-useraddr; - copylen=(copylen>len ? len:copylen); - if(ptrace_usercopy(realuseraddr,copyaddr,copylen,tofromuser,writingtouser,mask)) - return (-EIO); - copyaddr+=copylen; - len-=copylen; - useraddr+=copylen -#if CONFIG_S390_SUPPORT - +skip -#endif - ; + return 0; } - FixPerRegisters(task); - return(0); + return -EIO; } +#ifdef CONFIG_S390_SUPPORT /* - * Called by kernel/ptrace.c when detaching.. - * - * Make sure single step bits etc are not set. + * Now the fun part starts... a 31 bit program running in the + * 31 bit emulation tracing another program. PTRACE_PEEKTEXT, + * PTRACE_PEEKDATA, PTRACE_POKETEXT and PTRACE_POKEDATA are easy + * to handle, the difference to the 64 bit versions of the requests + * is that the access is done in multiples of 4 byte instead of + * 8 bytes (sizeof(unsigned long) on 31/64 bit). + * The ugly part are PTRACE_PEEKUSR, PTRACE_PEEKUSR_AREA, + * PTRACE_POKEUSR and PTRACE_POKEUSR_AREA. If the traced program + * is a 31 bit program too, the content of struct user can be + * emulated. A 31 bit program peeking into the struct user of + * a 64 bit program is a no-no. */ -void ptrace_disable(struct task_struct *child) + +/* + * Same as peek_user but for a 31 bit program. + */ +static int peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data) { - /* make sure the single step bit is not set. */ - clear_single_step(child); + struct user32 *dummy32 = NULL; + per_struct32 *dummy_per32 = NULL; + addr_t offset; + __u32 tmp; + + if (!(child->thread.flags & S390_FLAG_31BIT) || + (addr & 3) || addr > sizeof(struct user) - 3) + return -EIO; + + if (addr <= (addr_t) &dummy32->regs.orig_gpr2) { + /* + * psw, gprs, acrs and orig_gpr2 are stored on the stack + */ + if (addr == (addr_t) &dummy32->regs.psw.mask) { + /* Fake a 31 bit psw mask. */ + tmp = (__u32)(__KSTK_PTREGS(child)->psw.mask >> 32); + tmp = (tmp & PSW32_MASK_CC) | PSW32_USER_BITS; + } else if (addr == (addr_t) &dummy32->regs.psw.addr) { + /* Fake a 31 bit psw address. */ + tmp = (__u32) __KSTK_PTREGS(child)->psw.addr | + PSW32_ADDR_AMODE31; + } else + tmp = *(__u32 *)((addr_t) __KSTK_PTREGS(child) + + addr*2 + 4); + } else if (addr >= (addr_t) &dummy32->regs.fp_regs && + addr < (addr_t) (&dummy32->regs.fp_regs + 1)) { + /* + * floating point regs. are stored in the thread structure + */ + offset = addr - (addr_t) &dummy32->regs.fp_regs; + tmp = *(__u32 *)((addr_t) &child->thread.fp_regs + offset); + + } else if (addr >= (addr_t) &dummy32->regs.per_info && + addr < (addr_t) (&dummy32->regs.per_info + 1)) { + /* + * per_info is found in the thread structure + */ + offset = addr - (addr_t) &dummy32->regs.per_info; + /* This is magic. See per_struct and per_struct32. */ + if ((offset >= (addr_t) &dummy_per32->control_regs && + offset < (addr_t) (&dummy_per32->control_regs + 1)) || + (offset >= (addr_t) &dummy_per32->starting_addr && + offset <= (addr_t) &dummy_per32->ending_addr) || + offset == (addr_t) &dummy_per32->lowcore.words.address) + offset = offset*2 + 4; + else + offset = offset*2; + tmp = *(__u32 *)((addr_t) &child->thread.per_info + offset); + + } else + tmp = 0; + + return put_user(tmp, (__u32 *) data); } -typedef struct +/* + * Same as poke_user but for a 31 bit program. + */ +static int poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data) { -__u32 len; -__u32 kernel_addr; -__u32 process_addr; -} ptrace_area_emu31; + struct user32 *dummy32 = NULL; + per_struct32 *dummy_per32 = NULL; + addr_t offset; + __u32 tmp; + int ret; + + if (!(child->thread.flags & S390_FLAG_31BIT) || + (addr & 3) || addr > sizeof(struct user32) - 3) + return -EIO; + + tmp = (__u32) data; + + if (addr <= (addr_t) &dummy32->regs.orig_gpr2) { + /* + * psw, gprs, acrs and orig_gpr2 are stored on the stack + */ + if (addr == (addr_t) &dummy32->regs.psw.mask) { + /* Build a 64 bit psw mask from 31 bit mask. */ + if ((tmp & ~PSW32_MASK_CC) != PSW32_USER_BITS) + /* Invalid psw mask. */ + return -EINVAL; + __KSTK_PTREGS(child)->psw.mask = PSW_USER_BITS | + ((tmp & PSW32_MASK_CC) << 32); + } else if (addr == (addr_t) &dummy32->regs.psw.addr) { + /* Build a 64 bit psw address from 31 bit address. */ + __KSTK_PTREGS(child)->psw.addr = + (__u64) tmp & PSW32_ADDR_INSN; + } else + *(__u32*)((addr_t) __KSTK_PTREGS(child) + addr*2 + 4) = + tmp; + } else if (addr >= (addr_t) &dummy32->regs.fp_regs && + addr < (addr_t) (&dummy32->regs.fp_regs + 1)) { + /* + * floating point regs. are stored in the thread structure + */ + if (addr == (addr_t) &dummy32->regs.fp_regs.fpc && + (tmp & ~FPC_VALID_MASK) != 0) + /* Invalid floating pointer control. */ + return -EINVAL; + offset = addr - (addr_t) &dummy32->regs.fp_regs; + *(__u32 *)((addr_t) &child->thread.fp_regs + offset) = tmp; + + } else if (addr >= (addr_t) &dummy32->regs.per_info && + addr < (addr_t) (&dummy32->regs.per_info + 1)) { + /* + * per_info is found in the thread structure. + */ + offset = addr - (addr_t) &dummy32->regs.per_info; + /* + * This is magic. See per_struct and per_struct32. + * By incident the offsets in per_struct are exactly + * twice the offsets in per_struct32 for all fields. + * The 8 byte fields need special handling though, + * because the second half (bytes 4-7) is needed and + * not the first half. + */ + if ((offset >= (addr_t) &dummy_per32->control_regs && + offset < (addr_t) (&dummy_per32->control_regs + 1)) || + (offset >= (addr_t) &dummy_per32->starting_addr && + offset <= (addr_t) &dummy_per32->ending_addr) || + offset == (addr_t) &dummy_per32->lowcore.words.address) + offset = offset*2 + 4; + else + offset = offset*2; + *(__u32 *)((addr_t) &child->thread.per_info + offset) = tmp; + } -asmlinkage int sys_ptrace(long request, long pid, long addr, long data) + FixPerRegisters(child); + return 0; +} + +static int +do_ptrace_emu31(struct task_struct *child, long request, long addr, long data) { - struct task_struct *child; - int ret = -EPERM; - int copied; -#ifdef CONFIG_S390_SUPPORT - int parent_31bit; - int sizeof_parent_long; - u8 *dataptr; -#else -#define sizeof_parent_long 8 -#define dataptr (u8 *)&data -#endif - lock_kernel(); - 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_tsk; - if (request == PTRACE_ATTACH) - { - ret = ptrace_attach(child); - goto out_tsk; - } - ret = -ESRCH; - // printk("child=%lX child->flags=%lX",child,child->flags); - /* I added child!=current line so we can get the */ - /* ieee_instruction_pointer from the user structure DJB */ - if(child!=current) - { - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) - { - if (request != PTRACE_KILL) - goto out_tsk; + unsigned int tmp; /* 4 bytes !! */ + ptrace_area_emu31 parea; + int copied, ret; + + switch (request) { + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + /* read word at location addr. */ + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + if (copied != sizeof(tmp)) + return -EIO; + return put_user(tmp, (unsigned int *) data); + + case PTRACE_PEEKUSR: + /* read the word at location addr in the USER area. */ + return peek_user_emu31(child, addr, data); + + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + /* write the word at location addr. */ + tmp = data; + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 1); + if (copied != sizeof(tmp)) + return -EIO; + return 0; + + case PTRACE_POKEUSR: + /* write the word at location addr in the USER area */ + return poke_user_emu31(child, addr, data); + + case PTRACE_PEEKUSR_AREA: + case PTRACE_POKEUSR_AREA: + if (!copy_from_user(&parea, (void *) addr, sizeof(parea))) + return -EFAULT; + addr = parea.kernel_addr; + data = parea.process_addr; + copied = 0; + while (copied < parea.len) { + if (request == PTRACE_PEEKUSR_AREA) + ret = peek_user_emu31(child, addr, data); + else + ret = poke_user_emu31(child, addr, data); + if (ret) + return ret; + addr += sizeof(unsigned int); + data += sizeof(unsigned int); + copied += sizeof(unsigned int); } - if (child->parent != current) - goto out_tsk; + return 0; } -#ifdef CONFIG_S390_SUPPORT - parent_31bit=(current->thread.flags & S390_FLAG_31BIT); - sizeof_parent_long=(parent_31bit ? 4:8); - dataptr=&(((u8 *)&data)[parent_31bit ? 4:0]); + return -EIO; +} #endif - switch (request) - { - /* If I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: - { - u8 tmp[8]; - copied = access_process_vm(child, addr, tmp, sizeof_parent_long, 0); - ret = -EIO; - if (copied != sizeof_parent_long) - break; - ret = copy_to_user((void *)data,tmp,sizeof_parent_long); - break; - + +static int +do_ptrace(struct task_struct *child, long request, long addr, long data) +{ + int ret; + + if (request == PTRACE_ATTACH) + return ptrace_attach(child); + + /* + * I added child != current line so we can get the + * ieee_instruction_pointer from the user structure DJB + */ + if (child != current) { + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + return ret; } - /* read the word at location addr in the USER area. */ - case PTRACE_PEEKUSR: - ret=copy_user(child,addr,data,sizeof_parent_long,1,0); - break; - /* If I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - ret = 0; - if (access_process_vm(child, addr,dataptr, sizeof_parent_long, 1) == sizeof_parent_long) - break; - ret = -EIO; - break; - - case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ - ret=copy_user(child,addr,(addr_t)dataptr,sizeof_parent_long,0,1); - break; - - case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: /* restart after signal. */ - ret = -EIO; + switch (request) { + /* First the common request for 31/64 bit */ + case PTRACE_SYSCALL: + /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: + /* restart after signal. */ if ((unsigned long) data >= _NSIG) - break; + return -EIO; if (request == PTRACE_SYSCALL) set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); else @@ -525,86 +507,104 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) /* make sure the single step bit is not set. */ clear_single_step(child); wake_up_process(child); - ret = 0; - break; + return 0; -/* - * 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: - ret = 0; + /* + * 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. + */ if (child->state == TASK_ZOMBIE) /* already dead */ - break; + return 0; child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ clear_single_step(child); wake_up_process(child); - /* make sure the single step bit is not set. */ - break; + return 0; - case PTRACE_SINGLESTEP: /* set the trap flag. */ - ret = -EIO; + case PTRACE_SINGLESTEP: + /* set the trap flag. */ if ((unsigned long) data >= _NSIG) - break; + return -EIO; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; set_single_step(child); /* give it a chance to run. */ wake_up_process(child); - ret = 0; - break; + return 0; - case PTRACE_DETACH: /* detach a process that was attached. */ - ret = ptrace_detach(child, data); - break; + case PTRACE_DETACH: + /* detach a process that was attached. */ + return ptrace_detach(child, data); - case PTRACE_PEEKUSR_AREA: - case PTRACE_POKEUSR_AREA: - if(parent_31bit) - { - ptrace_area_emu31 parea; - if((ret=copy_from_user(&parea,(void *)addr,sizeof(parea)))==0) - ret=copy_user(child,parea.kernel_addr,parea.process_addr, - parea.len,1,(request==PTRACE_POKEUSR_AREA)); - } - else - { - ptrace_area parea; - if((ret=copy_from_user(&parea,(void *)addr,sizeof(parea)))==0) - ret=copy_user(child,parea.kernel_addr,parea.process_addr, - parea.len,1,(request==PTRACE_POKEUSR_AREA)); - } - break; - case PTRACE_SETOPTIONS: { + case PTRACE_SETOPTIONS: if (data & PTRACE_O_TRACESYSGOOD) child->ptrace |= PT_TRACESYSGOOD; else child->ptrace &= ~PT_TRACESYSGOOD; - ret = 0; - break; - } + return 0; + /* Do requests that differ for 31/64 bit */ default: - ret = -EIO; - break; +#ifdef CONFIG_S390_SUPPORT + if (current->thread.flags & S390_FLAG_31BIT) + return do_ptrace_emu31(child, request, addr, data); +#endif + return do_ptrace_normal(child, request, addr, data); + } - out_tsk: + return -EIO; +} + +asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret; + + lock_kernel(); + + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + ret = -EPERM; + if (current->ptrace & PT_PTRACED) + goto out; + ret = security_ops->ptrace(current->parent, current); + if (ret) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + goto out; + } + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + 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 = do_ptrace(child, request, addr, data); + put_task_struct(child); - out: +out: unlock_kernel(); return ret; } - - asmlinkage void syscall_trace(void) { if (!test_thread_flag(TIF_SYSCALL_TRACE)) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); + current->exit_code = + SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0); current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); diff --git a/arch/s390x/kernel/ptrace32.h b/arch/s390x/kernel/ptrace32.h new file mode 100644 index 000000000000..f5d221acddc7 --- /dev/null +++ b/arch/s390x/kernel/ptrace32.h @@ -0,0 +1,86 @@ +#ifndef _PTRACE32_H +#define _PTRACE32_H + +#include "linux32.h" /* needed for _psw_t32 */ + +typedef struct +{ + __u32 cr[3]; +} per_cr_words32 __attribute__((packed)); + +typedef struct +{ + __u16 perc_atmid; /* 0x096 */ + __u32 address; /* 0x098 */ + __u8 access_id; /* 0x0a1 */ +} per_lowcore_words32 __attribute__((packed)); + +typedef struct +{ + union { + per_cr_words32 words; + } control_regs __attribute__((packed)); + /* + * Use these flags instead of setting em_instruction_fetch + * directly they are used so that single stepping can be + * switched on & off while not affecting other tracing + */ + unsigned single_step : 1; + unsigned instruction_fetch : 1; + unsigned : 30; + /* + * These addresses are copied into cr10 & cr11 if single + * stepping is switched off + */ + __u32 starting_addr; + __u32 ending_addr; + union { + per_lowcore_words32 words; + } lowcore; +} per_struct32 __attribute__((packed)); + +struct user_regs_struct32 +{ + _psw_t32 psw; + u32 gprs[NUM_GPRS]; + u32 acrs[NUM_ACRS]; + u32 orig_gpr2; + s390_fp_regs fp_regs; + /* + * These per registers are in here so that gdb can modify them + * itself as there is no "official" ptrace interface for hardware + * watchpoints. This is the way intel does it. + */ + per_struct32 per_info; + u32 ieee_instruction_pointer; + /* Used to give failing instruction back to user for ieee exceptions */ +}; + +struct user32 { + /* We start with the registers, to mimic the way that "memory" + is returned from the ptrace(3,...) function. */ + struct user_regs_struct32 regs; /* Where the registers are actually stored */ + /* The rest of this junk is to help gdb figure out what goes where */ + u32 u_tsize; /* Text segment size (pages). */ + u32 u_dsize; /* Data segment size (pages). */ + u32 u_ssize; /* Stack segment size (pages). */ + u32 start_code; /* Starting virtual address of text. */ + u32 start_stack; /* Starting virtual address of stack area. + This is actually the bottom of the stack, + the top of the stack is always found in the + esp register. */ + s32 signal; /* Signal that caused the core dump. */ + u32 u_ar0; /* Used by gdb to help find the values for */ + /* the registers. */ + u32 magic; /* To uniquely identify a core file */ + char u_comm[32]; /* User command that was responsible */ +}; + +typedef struct +{ + __u32 len; + __u32 kernel_addr; + __u32 process_addr; +} ptrace_area_emu31; + +#endif /* _PTRACE32_H */ diff --git a/arch/s390x/kernel/setup.c b/arch/s390x/kernel/setup.c index c082607d74a4..218e4c5cb5fb 100644 --- a/arch/s390x/kernel/setup.c +++ b/arch/s390x/kernel/setup.c @@ -304,7 +304,7 @@ void __init setup_arch(char **cmdline_p) unsigned long start_pfn, end_pfn; static unsigned int smptrap=0; unsigned long delay = 0; - struct _lowcore *lowcore; + struct _lowcore *lc; int i; if (smptrap) @@ -441,27 +441,30 @@ void __init setup_arch(char **cmdline_p) /* * Setup lowcore for boot cpu */ - lowcore = (struct _lowcore *) - __alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0); - memset(lowcore, 0, 2*PAGE_SIZE); - lowcore->restart_psw.mask = _RESTART_PSW_MASK; - lowcore->restart_psw.addr = (addr_t) &restart_int_handler; - lowcore->external_new_psw.mask = _EXT_PSW_MASK; - lowcore->external_new_psw.addr = (addr_t) &ext_int_handler; - lowcore->svc_new_psw.mask = _SVC_PSW_MASK; - lowcore->svc_new_psw.addr = (addr_t) &system_call; - lowcore->program_new_psw.mask = _PGM_PSW_MASK; - lowcore->program_new_psw.addr = (addr_t) &pgm_check_handler; - lowcore->mcck_new_psw.mask = _MCCK_PSW_MASK; - lowcore->mcck_new_psw.addr = (addr_t) &mcck_int_handler; - lowcore->io_new_psw.mask = _IO_PSW_MASK; - lowcore->io_new_psw.addr = (addr_t) &io_int_handler; - lowcore->ipl_device = S390_lowcore.ipl_device; - lowcore->kernel_stack = ((__u64) &init_thread_union) + 16384; - lowcore->async_stack = (__u64) + lc = (struct _lowcore *) __alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0); + memset(lc, 0, 2*PAGE_SIZE); + lc->restart_psw.mask = PSW_BASE_BITS; + lc->restart_psw.addr = (addr_t) &restart_int_handler; + lc->external_new_psw.mask = PSW_KERNEL_BITS; + lc->external_new_psw.addr = (addr_t) &ext_int_handler; + lc->svc_new_psw.mask = PSW_KERNEL_BITS; + lc->svc_new_psw.addr = (addr_t) &system_call; + lc->program_new_psw.mask = PSW_KERNEL_BITS; + lc->program_new_psw.addr = (addr_t) &pgm_check_handler; + lc->mcck_new_psw.mask = PSW_KERNEL_BITS; + lc->mcck_new_psw.addr = (addr_t) &mcck_int_handler; + lc->io_new_psw.mask = PSW_KERNEL_BITS; + lc->io_new_psw.addr = (addr_t) &io_int_handler; + lc->ipl_device = S390_lowcore.ipl_device; + lc->kernel_stack = ((__u64) &init_thread_union) + 16384; + lc->async_stack = (__u64) __alloc_bootmem(4*PAGE_SIZE, 4*PAGE_SIZE, 0) + 16384; - lowcore->jiffy_timer = -1LL; - set_prefix((__u32)(__u64) lowcore); + lc->jiffy_timer = -1LL; + if (MACHINE_HAS_DIAG44) + lc->diag44_opcode = 0x83000044; + else + lc->diag44_opcode = 0x07000700; + set_prefix((__u32)(__u64) lc); cpu_init(); __cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr; diff --git a/arch/s390x/kernel/signal.c b/arch/s390x/kernel/signal.c index 6d4d9fe2812f..725afe1738f8 100644 --- a/arch/s390x/kernel/signal.c +++ b/arch/s390x/kernel/signal.c @@ -162,7 +162,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs *sregs) int err; err = __copy_from_user(regs, &sregs->regs, sizeof(_s390_regs_common)); - regs->psw.mask = _USER_PSW_MASK | (regs->psw.mask & PSW_MASK_DEBUGCHANGE); + regs->psw.mask = PSW_USER_BITS | (regs->psw.mask & PSW_MASK_CC); if (err) return err; @@ -292,9 +292,9 @@ static void setup_frame(int sig, struct k_sigaction *ka, /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { - regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + regs->gprs[14] = (__u64) ka->sa.sa_restorer; } else { - regs->gprs[14] = FIX_PSW(frame->retcode); + regs->gprs[14] = (__u64) frame->retcode; if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, (u16 *)(frame->retcode))) goto give_sigsegv; @@ -305,12 +305,12 @@ static void setup_frame(int sig, struct k_sigaction *ka, goto give_sigsegv; /* Set up registers for signal handler */ - regs->gprs[15] = (addr_t)frame; - regs->psw.addr = FIX_PSW(ka->sa.sa_handler); - regs->psw.mask = _USER_PSW_MASK; + regs->gprs[15] = (__u64) frame; + regs->psw.addr = (__u64) ka->sa.sa_handler; + regs->psw.mask = PSW_USER_BITS; regs->gprs[2] = map_signal(sig); - regs->gprs[3] = (addr_t)&frame->sc; + regs->gprs[3] = (__u64) &frame->sc; /* We forgot to include these in the sigcontext. To avoid breaking binary compatibility, they are passed as args. */ @@ -350,9 +350,9 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { - regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + regs->gprs[14] = (__u64) ka->sa.sa_restorer; } else { - regs->gprs[14] = FIX_PSW(frame->retcode); + regs->gprs[14] = (__u64) frame->retcode; err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, (u16 *)(frame->retcode)); } @@ -362,13 +362,13 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, goto give_sigsegv; /* Set up registers for signal handler */ - regs->gprs[15] = (addr_t)frame; - regs->psw.addr = FIX_PSW(ka->sa.sa_handler); - regs->psw.mask = _USER_PSW_MASK; + regs->gprs[15] = (__u64) frame; + regs->psw.addr = (__u64) ka->sa.sa_handler; + regs->psw.mask = PSW_USER_BITS; regs->gprs[2] = map_signal(sig); - regs->gprs[3] = (addr_t)&frame->info; - regs->gprs[4] = (addr_t)&frame->uc; + regs->gprs[3] = (__u64) &frame->info; + regs->gprs[4] = (__u64) &frame->uc; return; give_sigsegv: diff --git a/arch/s390x/kernel/signal32.c b/arch/s390x/kernel/signal32.c index c16a04d33eea..2d846b3a7a1c 100644 --- a/arch/s390x/kernel/signal32.c +++ b/arch/s390x/kernel/signal32.c @@ -29,15 +29,10 @@ #include #include #include "linux32.h" +#include "ptrace32.h" #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -#define _ADDR_31 0x80000000 -#define _USER_PSW_MASK_EMU32 0x070DC000 -#define _USER_PSW_MASK32 0x0705C00080000000 -#define PSW_MASK_DEBUGCHANGE32 0x00003000UL -#define PSW_ADDR_DEBUGCHANGE32 0x7FFFFFFFUL - typedef struct { __u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; @@ -297,9 +292,9 @@ static int save_sigregs32(struct pt_regs *regs,_sigregs32 *sregs) _s390_regs_common32 regs32; int err, i; - regs32.psw.mask = _USER_PSW_MASK_EMU32 | - (__u32)((regs->psw.mask & PSW_MASK_DEBUGCHANGE) >> 32); - regs32.psw.addr = _ADDR_31 | (__u32) regs->psw.addr; + regs32.psw.mask = PSW32_USER_BITS | + ((__u32)(regs->psw.mask >> 32) & PSW32_MASK_CC); + regs32.psw.addr = PSW32_ADDR_AMODE31 | (__u32) regs->psw.addr; for (i = 0; i < NUM_GPRS; i++) regs32.gprs[i] = (__u32) regs->gprs[i]; memcpy(regs32.acrs, regs->acrs, sizeof(regs32.acrs)); @@ -320,9 +315,9 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 *sregs) err = __copy_from_user(®s32, &sregs->regs, sizeof(regs32)); if (err) return err; - regs->psw.mask = _USER_PSW_MASK32 | - (__u64)(regs32.psw.mask & PSW_MASK_DEBUGCHANGE32) << 32; - regs->psw.addr = (__u64)(regs32.psw.addr & PSW_ADDR_DEBUGCHANGE32); + regs->psw.mask = PSW_USER32_BITS | + (__u64)(regs32.psw.mask & PSW32_MASK_CC) << 32; + regs->psw.addr = (__u64)(regs32.psw.addr & PSW32_ADDR_INSN); for (i = 0; i < NUM_GPRS; i++) regs->gprs[i] = (__u64) regs32.gprs[i]; memcpy(regs->acrs, regs32.acrs, sizeof(regs32.acrs)); @@ -467,9 +462,9 @@ static void setup_frame32(int sig, struct k_sigaction *ka, /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { - regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + regs->gprs[14] = (__u64) ka->sa.sa_restorer; } else { - regs->gprs[14] = FIX_PSW(frame->retcode); + regs->gprs[14] = (__u64) frame->retcode; if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, (u16 *)(frame->retcode))) goto give_sigsegv; @@ -480,12 +475,12 @@ static void setup_frame32(int sig, struct k_sigaction *ka, goto give_sigsegv; /* Set up registers for signal handler */ - regs->gprs[15] = (addr_t)frame; - regs->psw.addr = FIX_PSW(ka->sa.sa_handler); - regs->psw.mask = _USER_PSW_MASK32; + regs->gprs[15] = (__u64) frame; + regs->psw.addr = (__u64) ka->sa.sa_handler; + regs->psw.mask = PSW_USER32_BITS; regs->gprs[2] = map_signal(sig); - regs->gprs[3] = (addr_t)&frame->sc; + regs->gprs[3] = (__u64) &frame->sc; /* We forgot to include these in the sigcontext. To avoid breaking binary compatibility, they are passed as args. */ @@ -525,9 +520,9 @@ static void setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info, /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa.sa_flags & SA_RESTORER) { - regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + regs->gprs[14] = (__u64) ka->sa.sa_restorer; } else { - regs->gprs[14] = FIX_PSW(frame->retcode); + regs->gprs[14] = (__u64) frame->retcode; err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, (u16 *)(frame->retcode)); } @@ -537,13 +532,13 @@ static void setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info, goto give_sigsegv; /* Set up registers for signal handler */ - regs->gprs[15] = (addr_t)frame; - regs->psw.addr = FIX_PSW(ka->sa.sa_handler); - regs->psw.mask = _USER_PSW_MASK32; + regs->gprs[15] = (__u64) frame; + regs->psw.addr = (__u64) ka->sa.sa_handler; + regs->psw.mask = PSW_USER32_BITS; regs->gprs[2] = map_signal(sig); - regs->gprs[3] = (addr_t)&frame->info; - regs->gprs[4] = (addr_t)&frame->uc; + regs->gprs[3] = (__u64) &frame->info; + regs->gprs[4] = (__u64) &frame->uc; return; give_sigsegv: diff --git a/arch/s390x/kernel/traps.c b/arch/s390x/kernel/traps.c index ce3ec8054b5b..7be25061bc0b 100644 --- a/arch/s390x/kernel/traps.c +++ b/arch/s390x/kernel/traps.c @@ -118,22 +118,22 @@ void show_trace(unsigned long * stack) stack = (unsigned long*)&stack; printk("Call Trace: "); - low_addr = ((unsigned long) stack) & PSW_ADDR_MASK; + low_addr = (unsigned long) stack; high_addr = (low_addr & (-THREAD_SIZE)) + THREAD_SIZE; /* Skip the first frame (biased stack) */ - backchain = *((unsigned long *) low_addr) & PSW_ADDR_MASK; + backchain = *(unsigned long *) low_addr; /* Print up to 8 lines */ for (i = 0; i < 8; i++) { if (backchain < low_addr || backchain >= high_addr) break; - ret_addr = *((unsigned long *) (backchain+112)) & PSW_ADDR_MASK; + ret_addr = *(unsigned long *) (backchain+112); if (!kernel_text_address(ret_addr)) break; if (i && ((i % 3) == 0)) printk("\n "); printk("[<%016lx>] ", ret_addr); low_addr = backchain; - backchain = *((unsigned long *) backchain) & PSW_ADDR_MASK; + backchain = *(unsigned long *) backchain; } printk("\n"); } @@ -186,7 +186,7 @@ void show_registers(struct pt_regs *regs) char *mode; int i; - mode = (regs->psw.mask & PSW_PROBLEM_STATE) ? "User" : "Krnl"; + mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl"; printk("%s PSW : %016lx %016lx\n", mode, (unsigned long) regs->psw.mask, (unsigned long) regs->psw.addr); @@ -212,7 +212,7 @@ void show_registers(struct pt_regs *regs) * time of the fault. */ old_fs = get_fs(); - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) set_fs(USER_DS); else set_fs(KERNEL_DS); @@ -289,10 +289,10 @@ static void inline do_trap(long interruption_code, int signr, char *str, * We got all needed information from the lowcore and can * now safely switch on interrupts. */ - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) local_irq_enable(); - if (regs->psw.mask & PSW_PROBLEM_STATE) { + if (regs->psw.mask & PSW_MASK_PSTATE) { struct task_struct *tsk = current; tsk->thread.trap_no = interruption_code & 0xffff; if (info) @@ -323,12 +323,12 @@ static void inline do_trap(long interruption_code, int signr, char *str, static inline void *get_check_address(struct pt_regs *regs) { - return (void *) ADDR_BITS_REMOVE(regs->psw.addr-S390_lowcore.pgm_ilc); + return (void *)(regs->psw.addr - S390_lowcore.pgm_ilc); } int do_debugger_trap(struct pt_regs *regs,int signal) { - if(regs->psw.mask&PSW_PROBLEM_STATE) + if(regs->psw.mask&PSW_MASK_PSTATE) { if(current->ptrace & PT_PTRACED) force_sig(signal,current); @@ -426,14 +426,14 @@ asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code) * We got all needed information from the lowcore and can * now safely switch on interrupts. */ - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) local_irq_enable(); /* WARNING don't change this check back to */ - /* int problem_state=(regs->psw.mask & PSW_PROBLEM_STATE); */ + /* int problem_state=(regs->psw.mask & PSW_MASK_PSTATE); */ /* & then doing if(problem_state) an int is too small for this */ /* check on 64 bit. */ - if(regs->psw.mask & PSW_PROBLEM_STATE) + if(regs->psw.mask & PSW_MASK_PSTATE) get_user(*((__u16 *) opcode), location); else *((__u16 *)opcode)=*((__u16 *)location); @@ -459,7 +459,7 @@ asmlinkage void data_exception(struct pt_regs * regs, long interruption_code) * We got all needed information from the lowcore and can * now safely switch on interrupts. */ - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) local_irq_enable(); __asm__ volatile ("stfpc %0\n\t" @@ -527,21 +527,19 @@ void __init trap_init(void) void handle_per_exception(struct pt_regs *regs) { - if(regs->psw.mask&PSW_PROBLEM_STATE) - { + if (regs->psw.mask&PSW_MASK_PSTATE) { per_struct *per_info=¤t->thread.per_info; per_info->lowcore.words.perc_atmid=S390_lowcore.per_perc_atmid; per_info->lowcore.words.address=S390_lowcore.per_address; per_info->lowcore.words.access_id=S390_lowcore.per_access_id; } - if(do_debugger_trap(regs,SIGTRAP)) - { + if (do_debugger_trap(regs,SIGTRAP)) { /* I've seen this possibly a task structure being reused ? */ printk("Spurious per exception detected\n"); printk("switching off per tracing for this task.\n"); show_regs(regs); /* Hopefully switching off per tracing will help us survive */ - regs->psw.mask &= ~PSW_PER_MASK; + regs->psw.mask &= ~PSW_MASK_PER; } } diff --git a/arch/s390x/mm/fault.c b/arch/s390x/mm/fault.c index fe6bcb82d920..ec169b6fc1f2 100644 --- a/arch/s390x/mm/fault.c +++ b/arch/s390x/mm/fault.c @@ -166,7 +166,7 @@ extern inline void do_exception(struct pt_regs *regs, unsigned long error_code) /* Low-address protection hit in kernel mode means NULL pointer write access in kernel mode. */ - if (!(regs->psw.mask & PSW_PROBLEM_STATE)) { + if (!(regs->psw.mask & PSW_MASK_PSTATE)) { address = 0; user_address = 0; goto no_context; @@ -258,7 +258,7 @@ bad_area: up_read(&mm->mmap_sem); /* User mode accesses just cause a SIGSEGV */ - if (regs->psw.mask & PSW_PROBLEM_STATE) { + 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); @@ -298,7 +298,7 @@ out_of_memory: goto survive; } printk("VM: killing process %s\n", tsk->comm); - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) do_exit(SIGKILL); goto no_context; @@ -314,7 +314,7 @@ do_sigbus: force_sig(SIGBUS, tsk); /* Kernel mode? Handle exceptions or die */ - if (!(regs->psw.mask & PSW_PROBLEM_STATE)) + if (!(regs->psw.mask & PSW_MASK_PSTATE)) goto no_context; } @@ -440,7 +440,7 @@ pfault_interrupt(struct pt_regs *regs, __u16 error_code) * We got all needed information from the lowcore and can * now safely switch on interrupts. */ - if (regs->psw.mask & PSW_PROBLEM_STATE) + if (regs->psw.mask & PSW_MASK_PSTATE) local_irq_enable(); if (subcode & 0x0080) { diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 7fb61539b12d..f32b0e9ac5b3 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -98,7 +98,6 @@ static inline int s390_do_sync_wait(int irq, int do_tpi) { unsigned long psw_mask; - int ccode; uint64_t time_start; uint64_t time_curr; @@ -116,31 +115,7 @@ s390_do_sync_wait(int irq, int do_tpi) * sync. interrupt arrived we reset the I/O old PSW to * its original value. */ - - ccode = iac (); - - switch (ccode) { - case 0: /* primary-space */ - psw_mask = _IO_PSW_MASK - | _PSW_PRIM_SPACE_MODE | _PSW_IO_WAIT; - break; - case 1: /* secondary-space */ - psw_mask = _IO_PSW_MASK - | _PSW_SEC_SPACE_MODE | _PSW_IO_WAIT; - break; - case 2: /* access-register */ - psw_mask = _IO_PSW_MASK - | _PSW_ACC_REG_MODE | _PSW_IO_WAIT; - break; - case 3: /* home-space */ - psw_mask = _IO_PSW_MASK - | _PSW_HOME_SPACE_MODE | _PSW_IO_WAIT; - break; - default: - panic ("start_IO() : unexpected " - "address-space-control %d\n", ccode); - break; - } + psw_mask = PSW_KERNEL_BITS | PSW_MASK_IO | PSW_MASK_WAIT; /* * Martin didn't like modifying the new PSW, now we take @@ -201,7 +176,6 @@ s390_do_sync_wait_haltclear(int irq, int halt) int io_sub; __u32 io_parm; unsigned long psw_mask; - int ccode; int ready = 0; @@ -212,32 +186,7 @@ s390_do_sync_wait_haltclear(int irq, int halt) * FIXME: Are there case where we can't rely on an interrupt * to occurr? Need to check... */ - - ccode = iac (); - - switch (ccode) { - case 0: /* primary-space */ - psw_mask = _IO_PSW_MASK - | _PSW_PRIM_SPACE_MODE | _PSW_IO_WAIT; - break; - case 1: /* secondary-space */ - psw_mask = _IO_PSW_MASK - | _PSW_SEC_SPACE_MODE | _PSW_IO_WAIT; - break; - case 2: /* access-register */ - psw_mask = _IO_PSW_MASK - | _PSW_ACC_REG_MODE | _PSW_IO_WAIT; - break; - case 3: /* home-space */ - psw_mask = _IO_PSW_MASK - | _PSW_HOME_SPACE_MODE | _PSW_IO_WAIT; - break; - default: /* FIXME: isn't ccode only 2 bits anyway? */ - panic (halt?"halt":"clear" - "_IO() : unexpected address-space-control %d\n", - ccode); - break; - } + psw_mask = PSW_KERNEL_BITS | PSW_MASK_IO | PSW_MASK_WAIT; /* * Martin didn't like modifying the new PSW, now we take @@ -960,7 +909,7 @@ do_IRQ (struct pt_regs regs) * entry condition to synchronous I/O. */ if (*(__u32 *) __LC_SYNC_IO_WORD) { - regs.psw.mask &= ~(_PSW_WAIT_MASK_BIT | _PSW_IO_MASK_BIT); + regs.psw.mask &= ~(PSW_MASK_WAIT | PSW_MASK_IO); return; } /* endif */ diff --git a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h index bf2f3bf7b5a3..d33376528713 100644 --- a/include/asm-s390/lowcore.h +++ b/include/asm-s390/lowcore.h @@ -52,41 +52,12 @@ #define __LC_PFAULT_INTPARM 0x080 -/* interrupt handler start with all io, external and mcck interrupt disabled */ - -#define _RESTART_PSW_MASK 0x00080000 -#define _EXT_PSW_MASK 0x04080000 -#define _PGM_PSW_MASK 0x04080000 -#define _SVC_PSW_MASK 0x04080000 -#define _MCCK_PSW_MASK 0x04080000 -#define _IO_PSW_MASK 0x04080000 -#define _USER_PSW_MASK 0x070DC000/* DAT, IO, EXT, Home-space */ -#define _WAIT_PSW_MASK 0x070E0000/* DAT, IO, EXT, Wait, Home-space */ -#define _DW_PSW_MASK 0x000A0000/* disabled wait PSW mask */ - -#define _PRIMARY_MASK 0x0000 /* MASK for SACF */ -#define _SECONDARY_MASK 0x0100 /* MASK for SACF */ -#define _ACCESS_MASK 0x0200 /* MASK for SACF */ -#define _HOME_MASK 0x0300 /* MASK for SACF */ - -#define _PSW_PRIM_SPACE_MODE 0x00000000 -#define _PSW_SEC_SPACE_MODE 0x00008000 -#define _PSW_ACC_REG_MODE 0x00004000 -#define _PSW_HOME_SPACE_MODE 0x0000C000 - -#define _PSW_WAIT_MASK_BIT 0x00020000 /* Wait bit */ -#define _PSW_IO_MASK_BIT 0x02000000 /* IO bit */ -#define _PSW_IO_WAIT 0x02020000 /* IO & Wait bit */ - -/* we run in 31 Bit mode */ -#define _ADDR_31 0x80000000 - #ifndef __ASSEMBLY__ #include -#include #include #include +#include #include void restart_int_handler(void); diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h index 542f7f1e3150..31bbde234ee9 100644 --- a/include/asm-s390/processor.h +++ b/include/asm-s390/processor.h @@ -101,8 +101,8 @@ typedef struct thread_struct thread_struct; /* need to define ... */ #define start_thread(regs, new_psw, new_stackp) do { \ - regs->psw.mask = _USER_PSW_MASK; \ - regs->psw.addr = new_psw | 0x80000000; \ + regs->psw.mask = PSW_USER_BITS; \ + regs->psw.addr = new_psw | PSW_ADDR_AMODE31; \ regs->gprs[15] = new_stackp ; \ } while (0) @@ -136,19 +136,6 @@ unsigned long get_wchan(struct task_struct *p); #define cpu_relax() barrier() -/* - * Set of msr bits that gdb can change on behalf of a process. - */ -/* Only let our hackers near the condition codes */ -#define PSW_MASK_DEBUGCHANGE 0x00003000UL -/* Don't let em near the addressing mode either */ -#define PSW_ADDR_DEBUGCHANGE 0x7FFFFFFFUL -#define PSW_ADDR_MASK 0x7FFFFFFFUL -/* Program event recording mask */ -#define PSW_PER_MASK 0x40000000UL -#define USER_STD_MASK 0x00000080UL -#define PSW_PROBLEM_STATE 0x00010000UL - /* * Set PSW mask to specified value, while leaving the * PSW addr pointing to the next instruction. @@ -178,7 +165,8 @@ static inline void enabled_wait(void) unsigned long reg; psw_t wait_psw; - wait_psw.mask = 0x070e0000; + wait_psw.mask = PSW_BASE_BITS | PSW_MASK_IO | PSW_MASK_EXT | + PSW_MASK_MCHECK | PSW_MASK_WAIT; asm volatile ( " basr %0,0\n" "0: la %0,1f-0b(%0)\n" @@ -200,7 +188,7 @@ static inline void disabled_wait(unsigned long code) psw_t *dw_psw = (psw_t *)(((unsigned long) &psw_buffer+sizeof(psw_t)-1) & -sizeof(psw_t)); - dw_psw->mask = 0x000a0000; + dw_psw->mask = PSW_BASE_BITS | PSW_MASK_WAIT; dw_psw->addr = code; /* * Store status and then load disabled wait psw, diff --git a/include/asm-s390/ptrace.h b/include/asm-s390/ptrace.h index edc3b5b5c1f2..5f64a65843f7 100644 --- a/include/asm-s390/ptrace.h +++ b/include/asm-s390/ptrace.h @@ -114,7 +114,6 @@ #include #include #include - #include /* this typedef defines how a Program Status Word looks like */ @@ -124,10 +123,32 @@ typedef struct __u32 addr; } __attribute__ ((aligned(8))) psw_t; -#ifdef __KERNEL__ -#define FIX_PSW(addr) ((unsigned long)(addr)|0x80000000UL) -#define ADDR_BITS_REMOVE(addr) ((addr)&0x7fffffff) -#endif +#define PSW_MASK_PER 0x40000000UL +#define PSW_MASK_DAT 0x04000000UL +#define PSW_MASK_IO 0x02000000UL +#define PSW_MASK_EXT 0x01000000UL +#define PSW_MASK_KEY 0x00F00000UL +#define PSW_MASK_MCHECK 0x00040000UL +#define PSW_MASK_WAIT 0x00020000UL +#define PSW_MASK_PSTATE 0x00010000UL +#define PSW_MASK_ASC 0x0000C000UL +#define PSW_MASK_CC 0x00003000UL +#define PSW_MASK_PM 0x00000F00UL + +#define PSW_ADDR_AMODE31 0x80000000UL +#define PSW_ADDR_INSN 0x7FFFFFFFUL + +#define PSW_BASE_BITS 0x00080000UL + +#define PSW_ASC_PRIMARY 0x00000000UL +#define PSW_ASC_ACCREG 0x00004000UL +#define PSW_ASC_SECONDARY 0x00008000UL +#define PSW_ASC_HOME 0x0000C000UL + +#define PSW_KERNEL_BITS (PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_PRIMARY) +#define PSW_USER_BITS (PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_HOME | \ + PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK | \ + PSW_MASK_PSTATE) typedef union { @@ -328,8 +349,8 @@ struct user_regs_struct }; #ifdef __KERNEL__ -#define user_mode(regs) (((regs)->psw.mask & PSW_PROBLEM_STATE) != 0) -#define instruction_pointer(regs) ((regs)->psw.addr) +#define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0) +#define instruction_pointer(regs) ((regs)->psw.addr & PSW_MASK_INSN) extern void show_regs(struct pt_regs * regs); #endif diff --git a/include/asm-s390x/lowcore.h b/include/asm-s390x/lowcore.h index fb7cdb090813..41e0c5fc07cf 100644 --- a/include/asm-s390x/lowcore.h +++ b/include/asm-s390x/lowcore.h @@ -56,32 +56,6 @@ #define __LC_PFAULT_INTPARM 0x11B8 -/* interrupt handler start with all io, external and mcck interrupt disabled */ - -#define _RESTART_PSW_MASK 0x0000000180000000 -#define _EXT_PSW_MASK 0x0400000180000000 -#define _PGM_PSW_MASK 0x0400000180000000 -#define _SVC_PSW_MASK 0x0400000180000000 -#define _MCCK_PSW_MASK 0x0400000180000000 -#define _IO_PSW_MASK 0x0400000180000000 -#define _USER_PSW_MASK 0x0705C00180000000 -#define _WAIT_PSW_MASK 0x0706000180000000 -#define _DW_PSW_MASK 0x0002000180000000 - -#define _PRIMARY_MASK 0x0000 /* MASK for SACF */ -#define _SECONDARY_MASK 0x0100 /* MASK for SACF */ -#define _ACCESS_MASK 0x0200 /* MASK for SACF */ -#define _HOME_MASK 0x0300 /* MASK for SACF */ - -#define _PSW_PRIM_SPACE_MODE 0x0000000000000000 -#define _PSW_SEC_SPACE_MODE 0x0000800000000000 -#define _PSW_ACC_REG_MODE 0x0000400000000000 -#define _PSW_HOME_SPACE_MODE 0x0000C00000000000 - -#define _PSW_WAIT_MASK_BIT 0x0002000000000000 -#define _PSW_IO_MASK_BIT 0x0200000000000000 -#define _PSW_IO_WAIT 0x0202000000000000 - #ifndef __ASSEMBLY__ #include diff --git a/include/asm-s390x/processor.h b/include/asm-s390x/processor.h index f578c9eba468..334cd6dcf6c4 100644 --- a/include/asm-s390x/processor.h +++ b/include/asm-s390x/processor.h @@ -111,13 +111,13 @@ typedef struct thread_struct thread_struct; /* need to define ... */ #define start_thread(regs, new_psw, new_stackp) do { \ - regs->psw.mask = _USER_PSW_MASK; \ + regs->psw.mask = PSW_USER_BITS; \ regs->psw.addr = new_psw; \ regs->gprs[15] = new_stackp; \ } while (0) #define start_thread31(regs, new_psw, new_stackp) do { \ - regs->psw.mask = _USER_PSW_MASK & ~(1L << 32); \ + regs->psw.mask = PSW_USER32_BITS; \ regs->psw.addr = new_psw; \ regs->gprs[15] = new_stackp; \ } while (0) @@ -153,19 +153,6 @@ unsigned long get_wchan(struct task_struct *p); #define cpu_relax() barrier() -/* - * Set of msr bits that gdb can change on behalf of a process. - */ -/* Only let our hackers near the condition codes */ -#define PSW_MASK_DEBUGCHANGE 0x0000300000000000UL -/* Don't let em near the addressing mode either */ -#define PSW_ADDR_DEBUGCHANGE 0xFFFFFFFFFFFFFFFFUL -#define PSW_ADDR_MASK 0xFFFFFFFFFFFFFFFFUL -/* Program event recording mask */ -#define PSW_PER_MASK 0x4000000000000000UL -#define USER_STD_MASK 0x0000000000000080UL -#define PSW_PROBLEM_STATE 0x0001000000000000UL - /* * Set PSW mask to specified value, while leaving the * PSW addr pointing to the next instruction. @@ -194,7 +181,8 @@ static inline void enabled_wait(void) unsigned long reg; psw_t wait_psw; - wait_psw.mask = 0x0706000180000000; + wait_psw.mask = PSW_BASE_BITS | PSW_MASK_IO | PSW_MASK_EXT | + PSW_MASK_MCHECK | PSW_MASK_WAIT; asm volatile ( " larl %0,0f\n" " stg %0,8(%1)\n" @@ -214,7 +202,7 @@ static inline void disabled_wait(addr_t code) psw_t *dw_psw = (psw_t *)(((unsigned long) &psw_buffer+sizeof(psw_t)-1) & -sizeof(psw_t)); - dw_psw->mask = 0x0002000180000000; + dw_psw->mask = PSW_BASE_BITS | PSW_MASK_WAIT; dw_psw->addr = code; /* * Store status and then load disabled wait psw, diff --git a/include/asm-s390x/ptrace.h b/include/asm-s390x/ptrace.h index 311d6624a6d0..adf7fbaf629b 100644 --- a/include/asm-s390x/ptrace.h +++ b/include/asm-s390x/ptrace.h @@ -104,10 +104,33 @@ typedef struct __u64 addr; } __attribute__ ((aligned(8))) psw_t; -#ifdef __KERNEL__ -#define FIX_PSW(addr) ((unsigned long)(addr)) -#define ADDR_BITS_REMOVE(addr) ((addr)) -#endif +#define PSW_MASK_PER 0x4000000000000000UL +#define PSW_MASK_DAT 0x0400000000000000UL +#define PSW_MASK_IO 0x0200000000000000UL +#define PSW_MASK_EXT 0x0100000000000000UL +#define PSW_MASK_KEY 0x00F0000000000000UL +#define PSW_MASK_MCHECK 0x0004000000000000UL +#define PSW_MASK_WAIT 0x0002000000000000UL +#define PSW_MASK_PSTATE 0x0001000000000000UL +#define PSW_MASK_ASC 0x0000C00000000000UL +#define PSW_MASK_CC 0x0000300000000000UL +#define PSW_MASK_PM 0x00000F0000000000UL + +#define PSW_BASE_BITS 0x0000000180000000UL +#define PSW_BASE32_BITS 0x0000000080000000UL + +#define PSW_ASC_PRIMARY 0x0000000000000000UL +#define PSW_ASC_ACCREG 0x0000400000000000UL +#define PSW_ASC_SECONDARY 0x0000800000000000UL +#define PSW_ASC_HOME 0x0000C00000000000UL + +#define PSW_KERNEL_BITS (PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_PRIMARY) +#define PSW_USER_BITS (PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_HOME | \ + PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK | \ + PSW_MASK_PSTATE) +#define PSW_USER32_BITS (PSW_BASE32_BITS | PSW_MASK_DAT | PSW_ASC_HOME | \ + PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK | \ + PSW_MASK_PSTATE) typedef union { @@ -309,7 +332,7 @@ struct user_regs_struct }; #ifdef __KERNEL__ -#define user_mode(regs) (((regs)->psw.mask & PSW_PROBLEM_STATE) != 0) +#define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0) #define instruction_pointer(regs) ((regs)->psw.addr) extern void show_regs(struct pt_regs * regs); #endif -- cgit v1.2.3 From ebbde00338de209f4c3f55939384a35c4af85ed3 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Thu, 3 Oct 2002 21:50:33 -0700 Subject: [PATCH] s390 update (24/27): boot sequence. Rework boot sequence on s390: Traditionally, device detection os s390 is done completely at a _very_ early stage during bootup (from init_irq(), i.e. before memory management or the console are there). This has always been a bad idea, but now it broke even more since the linux driver model requires devices detection to take place after the core_initcalls are done. We now do only a small amount of scanning (probably less in the future) at the early stage, the bulk of it is done from a proper subsys_initcall(). This requires some changes in related areas: - the machine check handler initialization is split in two halves, since we want to catch major machine malfunctions as early as possible, but device machine checks can only be caught after the channel subsystem is up. - some functions that are called from the css initialization made some assumptions of when to use kmalloc or bootmem_alloc, which were broken anyway. We fix this here and hopefully can get rid of bootmem_alloc for the css completely in the future. - the debug logging feature for s390 was not used for functions in the initialization before, since it requires the memory management to be working. Now that we can be sure that it works, some special cases can be removed. Now that these changes are done, a partial implementation of the device model for the channel subsystem is possible, but at this point, none of the device drivers make use of that yet. --- arch/s390/kernel/debug.c | 44 ++-- arch/s390/kernel/setup.c | 2 +- arch/s390/kernel/smp.c | 4 +- arch/s390x/kernel/debug.c | 44 ++-- arch/s390x/kernel/setup.c | 2 +- arch/s390x/kernel/smp.c | 4 +- drivers/s390/Makefile | 3 +- drivers/s390/char/con3215.c | 22 +- drivers/s390/cio/cio_debug.c | 4 +- drivers/s390/cio/requestirq.c | 32 ++- drivers/s390/cio/s390io.c | 478 ++++++++++++++++++++++-------------------- drivers/s390/s390mach.c | 33 ++- include/asm-s390/irq.h | 28 +-- include/asm-s390/s390io.h | 33 ++- include/asm-s390x/irq.h | 28 +-- include/asm-s390x/s390io.h | 33 ++- 16 files changed, 433 insertions(+), 361 deletions(-) (limited to 'include') diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index 4fd8eef5aa77..166bd1db09b1 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c @@ -21,6 +21,7 @@ #include #include +#include #include @@ -146,7 +147,7 @@ static debug_info_t *debug_area_first = NULL; static debug_info_t *debug_area_last = NULL; DECLARE_MUTEX(debug_lock); -static int initialized = 0; +static int initialized; static struct file_operations debug_file_ops = { read: debug_output, @@ -591,7 +592,7 @@ debug_info_t *debug_register MOD_INC_USE_COUNT; if (!initialized) - debug_init(); + BUG(); down(&debug_lock); /* create new debug_info */ @@ -828,18 +829,16 @@ debug_entry_t *debug_sprintf_exception(debug_info_t* id, * - is called exactly once to initialize the debug feature */ -int debug_init(void) +static int __init debug_init(void) { int rc = 0; down(&debug_lock); - if (!initialized) { #ifdef CONFIG_PROC_FS - debug_proc_root_entry = proc_mkdir(DEBUG_DIR_ROOT, NULL); + debug_proc_root_entry = proc_mkdir(DEBUG_DIR_ROOT, NULL); #endif /* CONFIG_PROC_FS */ - printk(KERN_INFO "debug: Initialization complete\n"); - initialized = 1; - } + printk(KERN_INFO "debug: Initialization complete\n"); + initialized = 1; up(&debug_lock); return rc; @@ -1173,27 +1172,9 @@ out: } /* - * init_module: - */ - -#ifdef MODULE -int init_module(void) -{ - int rc = 0; -#ifdef DEBUG - printk("debug_module_init: \n"); -#endif - rc = debug_init(); - if (rc) - printk(KERN_INFO "debug: an error occurred with debug_init\n"); - return rc; -} - -/* - * cleanup_module: + * clean up module */ - -void cleanup_module(void) +void __exit debug_exit(void) { #ifdef DEBUG printk("debug_cleanup_module: \n"); @@ -1204,7 +1185,12 @@ void cleanup_module(void) return; } -#endif /* MODULE */ +/* + * module definitions + */ +core_initcall(debug_init); +module_exit(debug_exit); +MODULE_LICENSE("GPL"); EXPORT_SYMBOL(debug_register); EXPORT_SYMBOL(debug_unregister); diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 181a6fce21a2..cea36338e916 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -78,7 +78,7 @@ static struct resource data_resource = { "Kernel data", 0, 0 }; /* * cpu_init() initializes state that is per-CPU. */ -void __init cpu_init (void) +void __devinit cpu_init (void) { int nr = smp_processor_id(); int addr = hard_smp_processor_id(); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index b5ca80658f19..ce29d1f6f31a 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -454,7 +454,7 @@ extern void init_cpu_timer(void); extern int pfault_init(void); extern int pfault_token(void); -int __init start_secondary(void *cpuvoid) +int __devinit start_secondary(void *cpuvoid) { /* Setup the cpu */ cpu_init(); @@ -474,7 +474,7 @@ int __init start_secondary(void *cpuvoid) return cpu_idle(NULL); } -static struct task_struct *__init fork_by_hand(void) +static struct task_struct *__devinit fork_by_hand(void) { struct pt_regs regs; /* don't care about the psw and regs settings since we'll never diff --git a/arch/s390x/kernel/debug.c b/arch/s390x/kernel/debug.c index 4fd8eef5aa77..166bd1db09b1 100644 --- a/arch/s390x/kernel/debug.c +++ b/arch/s390x/kernel/debug.c @@ -21,6 +21,7 @@ #include #include +#include #include @@ -146,7 +147,7 @@ static debug_info_t *debug_area_first = NULL; static debug_info_t *debug_area_last = NULL; DECLARE_MUTEX(debug_lock); -static int initialized = 0; +static int initialized; static struct file_operations debug_file_ops = { read: debug_output, @@ -591,7 +592,7 @@ debug_info_t *debug_register MOD_INC_USE_COUNT; if (!initialized) - debug_init(); + BUG(); down(&debug_lock); /* create new debug_info */ @@ -828,18 +829,16 @@ debug_entry_t *debug_sprintf_exception(debug_info_t* id, * - is called exactly once to initialize the debug feature */ -int debug_init(void) +static int __init debug_init(void) { int rc = 0; down(&debug_lock); - if (!initialized) { #ifdef CONFIG_PROC_FS - debug_proc_root_entry = proc_mkdir(DEBUG_DIR_ROOT, NULL); + debug_proc_root_entry = proc_mkdir(DEBUG_DIR_ROOT, NULL); #endif /* CONFIG_PROC_FS */ - printk(KERN_INFO "debug: Initialization complete\n"); - initialized = 1; - } + printk(KERN_INFO "debug: Initialization complete\n"); + initialized = 1; up(&debug_lock); return rc; @@ -1173,27 +1172,9 @@ out: } /* - * init_module: - */ - -#ifdef MODULE -int init_module(void) -{ - int rc = 0; -#ifdef DEBUG - printk("debug_module_init: \n"); -#endif - rc = debug_init(); - if (rc) - printk(KERN_INFO "debug: an error occurred with debug_init\n"); - return rc; -} - -/* - * cleanup_module: + * clean up module */ - -void cleanup_module(void) +void __exit debug_exit(void) { #ifdef DEBUG printk("debug_cleanup_module: \n"); @@ -1204,7 +1185,12 @@ void cleanup_module(void) return; } -#endif /* MODULE */ +/* + * module definitions + */ +core_initcall(debug_init); +module_exit(debug_exit); +MODULE_LICENSE("GPL"); EXPORT_SYMBOL(debug_register); EXPORT_SYMBOL(debug_unregister); diff --git a/arch/s390x/kernel/setup.c b/arch/s390x/kernel/setup.c index 218e4c5cb5fb..2c2106efb1f9 100644 --- a/arch/s390x/kernel/setup.c +++ b/arch/s390x/kernel/setup.c @@ -78,7 +78,7 @@ static struct resource data_resource = { "Kernel data", 0, 0 }; /* * cpu_init() initializes state that is per-CPU. */ -void __init cpu_init (void) +void __devinit cpu_init (void) { int nr = smp_processor_id(); int addr = hard_smp_processor_id(); diff --git a/arch/s390x/kernel/smp.c b/arch/s390x/kernel/smp.c index ff2b6007d23e..844b710f3763 100644 --- a/arch/s390x/kernel/smp.c +++ b/arch/s390x/kernel/smp.c @@ -435,7 +435,7 @@ void __init smp_check_cpus(unsigned int max_cpus) extern void init_cpu_timer(void); extern int pfault_init(void); -int __init start_secondary(void *cpuvoid) +int __devinit start_secondary(void *cpuvoid) { /* Setup the cpu */ cpu_init(); @@ -455,7 +455,7 @@ int __init start_secondary(void *cpuvoid) return cpu_idle(NULL); } -static struct task_struct * __init fork_by_hand(void) +static struct task_struct * __devinit fork_by_hand(void) { struct pt_regs regs; /* don't care about the psw and regs settings since we'll never diff --git a/drivers/s390/Makefile b/drivers/s390/Makefile index 16d7657b1243..8274a2681a1c 100644 --- a/drivers/s390/Makefile +++ b/drivers/s390/Makefile @@ -7,8 +7,9 @@ export-objs := s390dyn.o qdio.o obj-$(CONFIG_QDIO) += qdio.o obj-y += s390mach.o s390dyn.o sysinfo.o -obj-y += block/ char/ misc/ net/ cio/ +obj-y += cio/ block/ char/ misc/ net/ drivers-y += drivers/s390/built-in.o include $(TOPDIR)/Rules.make + diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index c2ec4c4443bd..18a66f4503b1 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -693,13 +693,12 @@ static int raw3215_startup(raw3215_info *raw) if (raw->flags & RAW3215_ACTIVE) return 0; - if (request_irq(raw->irq, raw3215_irq, SA_INTERRUPT, - "3215 terminal driver", &raw->devstat) != 0) + if (s390_request_console_irq(raw->irq, raw3215_irq, SA_INTERRUPT, + "3215 terminal driver", &raw->devstat) != 0) return -1; raw->line_pos = 0; raw->flags |= RAW3215_ACTIVE; s390irq_spin_lock_irqsave(raw->irq, flags); - set_cons_dev(raw->irq); raw3215_try_io(raw); s390irq_spin_unlock_irqrestore(raw->irq, flags); @@ -1064,7 +1063,6 @@ void __init con3215_init(void) /* Check if 3215 is to be the console */ if (!CONSOLE_IS_3215) return; - irq = raw3215_find_dev(0); /* Set the console mode for VM */ if (MACHINE_IS_VM) { @@ -1072,6 +1070,22 @@ void __init con3215_init(void) cpcmd("TERM AUTOCR OFF", NULL, 0); } + if (console_device != -1) { + /* use the device number that was specified on the + * command line */ + irq = raw3215_find_dev(0); + } else if (MACHINE_IS_VM) { + /* under VM, the console is at device number 0009 + * by default, so try that */ + irq = get_irq_by_devno(0x0009); + } else { + /* unlike in 2.4, we cannot autoprobe here, since + * the channel subsystem is not fully initialized. + * With some luck, the HWC console can take over */ + printk(KERN_WARNING "3215 console not found\n"); + return; + } + /* allocate 3215 request structures */ raw3215_freelist = NULL; spin_lock_init(&raw3215_freelist_lock); diff --git a/drivers/s390/cio/cio_debug.c b/drivers/s390/cio/cio_debug.c index 7e5ebc461ba4..571122876fdb 100644 --- a/drivers/s390/cio/cio_debug.c +++ b/drivers/s390/cio/cio_debug.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/cio_debug.c * S/390 common I/O routines -- message ids for debugging - * $Revision: 1.4 $ + * $Revision: 1.5 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -69,4 +69,4 @@ cio_debug_init (void) return ret; } -__initcall (cio_debug_init); +arch_initcall (cio_debug_init); diff --git a/drivers/s390/cio/requestirq.c b/drivers/s390/cio/requestirq.c index 5648b69c7e88..15472666a98b 100644 --- a/drivers/s390/cio/requestirq.c +++ b/drivers/s390/cio/requestirq.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/requestirq.c * S/390 common I/O routines -- - * $Revision: 1.7 $ + * $Revision: 1.12 $ * * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * IBM Corporation @@ -122,6 +122,34 @@ s390_request_irq (unsigned int irq, NULL, irqflags, devname, dev_id); } +int +s390_request_console_irq (int irq, + void (*handler) (int, void *, struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id) +{ + int ret; + unsigned long flags; + + s390_device_recognition_irq (irq); + + ret = s390_request_irq_special (irq, (io_handler_func_t) handler, + NULL, irqflags, devname, dev_id); + + if (ret) + goto out; + + s390irq_spin_lock_irqsave(irq, flags); + ret = set_cons_dev(irq); + s390irq_spin_unlock_irqrestore(irq, flags); + + if (ret) + free_irq(irq, dev_id); +out: + return ret; +} + /* * request_irq wrapper */ @@ -151,7 +179,7 @@ s390_free_irq (unsigned int irq, void *dev_id) s390irq_spin_lock_irqsave (irq, flags); - CIO_DEBUG_NOCONS(irq,KERN_DEBUG, printk, 2, + CIO_DEBUG_NOCONS(irq,KERN_DEBUG, DBG, 2, "Trying to free IRQ %d\n", irq); diff --git a/drivers/s390/cio/s390io.c b/drivers/s390/cio/s390io.c index 62115ec8b98c..fd559f82443a 100644 --- a/drivers/s390/cio/s390io.c +++ b/drivers/s390/cio/s390io.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/s390io.c * S/390 common I/O routines - * $Revision: 1.11 $ + * $Revision: 1.33 $ * * S390 version * Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH, @@ -75,9 +75,8 @@ static schib_t p_init_schib; static __u64 irq_IPL_TOD; // FIXME: can be on stack static void s390_process_subchannels (void); -static void s390_device_recognition_all (void); static int s390_SenseID (int irq, senseid_t * sid, __u8 lpm); -static int s390_SetPGID (int irq, __u8 lpm, pgid_t * pgid); +static int s390_SetPGID (int irq, __u8 lpm); static int s390_SensePGID (int irq, __u8 lpm, pgid_t * pgid); /* FIXME: intermixed with proc.c */ @@ -86,7 +85,6 @@ unsigned long s390_irq_count[NR_CPUS]; /* trace how many irqs have occured per c int cio_show_msg; static int cio_notoper_msg = 1; -static int cio_sid_with_pgid; /* if we need a PGID for SenseID, switch this on */ static int __init cio_setup (char *parm) @@ -125,25 +123,6 @@ cio_notoper_setup (char *parm) __setup ("cio_notoper_msg=", cio_notoper_setup); -static int __init -cio_pgid_setup (char *parm) -{ - if (!strcmp (parm, "yes")) { - cio_sid_with_pgid = 1; - } else if (!strcmp (parm, "no")) { - cio_sid_with_pgid = 0; - } else { - printk (KERN_ERR - "cio_pgid_setup : invalid cio_msg parameter '%s'", - parm); - - } - - return 1; -} - -__setup ("cio_sid_with_pgid=", cio_pgid_setup); - /* This function is replacing the init_IRQ function in * arch/s390(x)/kernel/irq.c and is called early during * bootup. Anything called from here must be careful @@ -199,8 +178,6 @@ s390_init_IRQ (void) cr6 = 0x10000000; __ctl_load (cr6, 6, 6); - s390_device_recognition_all (); - init_IRQ_complete = 1; local_irq_restore (flags); @@ -226,6 +203,51 @@ init_IRQ_handler (int irq, void *dev_id, struct pt_regs *regs) /* this is a dummy handler only ... */ } +/* + * diag210 is used under VM to get information about a virtual device + */ +#ifdef CONFIG_ARCH_S390X +int diag210(diag210_t * addr) +{ + /* + * diag 210 needs its data below the 2GB border, so we + * use a static data area to be sure + */ + static diag210_t diag210_tmp; + static spinlock_t diag210_lock = SPIN_LOCK_UNLOCKED; + unsigned long flags; + int ccode; + + spin_lock_irqsave(&diag210_lock, flags); + diag210_tmp = *addr; + + asm volatile ( + " sam31\n" + " diag %1,0,0x210\n" + " sam64\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory" ); + + *addr = diag210_tmp; + spin_unlock_irqrestore(&diag210_lock, flags); + + return ccode; +} +#else +int diag210(diag210_t * addr) +{ + int ccode; + + asm volatile ( + " diag %1,0,0x210\n" + " ipm %0\n" + " srl %0,28" + : "=d" (ccode) : "a" (__pa(addr)) : "cc", "memory" ); + + return ccode; +} +#endif /* * Input : @@ -234,31 +256,28 @@ init_IRQ_handler (int irq, void *dev_id, struct pt_regs *regs) * Output : none */ static inline void -_VM_virtual_device_info (__u16 devno, senseid_t * ps) +VM_virtual_device_info (__u16 devno, senseid_t * ps) { - diag210_t *p_diag_data; + diag210_t diag_data; int ccode; int error = 0; CIO_TRACE_EVENT (4, "VMvdinf"); - if (init_IRQ_complete) { - p_diag_data = kmalloc (sizeof (diag210_t), GFP_DMA); - } else { - p_diag_data = alloc_bootmem_low (sizeof (diag210_t)); + diag_data = (diag210_t) { + .vrdcdvno = devno, + .vrdclen = sizeof (diag_data), + }; - } - - p_diag_data->vrdcdvno = devno; - p_diag_data->vrdclen = sizeof (diag210_t); - ccode = diag210 ((diag210_t *) virt_to_phys (p_diag_data)); + ccode = diag210 (&diag_data); ps->reserved = 0xff; - switch (p_diag_data->vrdcvcla) { + /* FIXME: this would be much nicer if it were table driven */ + switch (diag_data.vrdcvcla) { case 0x80: - switch (p_diag_data->vrdcvtyp) { + switch (diag_data.vrdcvtyp) { case 00: ps->cu_type = 0x3215; @@ -277,7 +296,7 @@ _VM_virtual_device_info (__u16 devno, senseid_t * ps) case 0x40: - switch (p_diag_data->vrdcvtyp) { + switch (diag_data.vrdcvtyp) { case 0xC0: ps->cu_type = 0x5080; @@ -314,7 +333,7 @@ _VM_virtual_device_info (__u16 devno, senseid_t * ps) case 0x20: - switch (p_diag_data->vrdcvtyp) { + switch (diag_data.vrdcvtyp) { case 0x84: ps->cu_type = 0x3505; @@ -345,7 +364,7 @@ _VM_virtual_device_info (__u16 devno, senseid_t * ps) case 0x10: - switch (p_diag_data->vrdcvtyp) { + switch (diag_data.vrdcvtyp) { case 0x84: ps->cu_type = 0x3525; @@ -422,7 +441,7 @@ _VM_virtual_device_info (__u16 devno, senseid_t * ps) case 0x08: - switch (p_diag_data->vrdcvtyp) { + switch (diag_data.vrdcvtyp) { case 0x82: ps->cu_type = 0x3422; @@ -477,7 +496,7 @@ _VM_virtual_device_info (__u16 devno, senseid_t * ps) case 02: /* special device class ... */ - switch (p_diag_data->vrdcvtyp) { + switch (diag_data.vrdcvtyp) { case 0x20: /* OSA */ ps->cu_type = 0x3088; @@ -502,13 +521,6 @@ _VM_virtual_device_info (__u16 devno, senseid_t * ps) } - if (init_IRQ_complete) { - kfree (p_diag_data); - } else { - free_bootmem ((unsigned long) p_diag_data, sizeof (diag210_t)); - - } - if (error) CIO_DEBUG_ALWAYS(KERN_ERR, 0, "DIAG X'210' for " @@ -519,12 +531,11 @@ _VM_virtual_device_info (__u16 devno, senseid_t * ps) "rdev model: %02X\n", devno, ccode, - p_diag_data->vrdcvcla, - p_diag_data->vrdcvtyp, - p_diag_data->vrdcrccl, - p_diag_data->vrdccrty, - p_diag_data->vrdccrmd); - + diag_data.vrdcvcla, + diag_data.vrdcvtyp, + diag_data.vrdcrccl, + diag_data.vrdccrty, + diag_data.vrdccrmd); } /* @@ -553,7 +564,7 @@ _VM_virtual_device_info (__u16 devno, senseid_t * ps) int read_dev_chars (int irq, void **buffer, int length) { - unsigned int flags; + unsigned long flags; ccw1_t *rdc_ccw; devstat_t devstat; char *rdc_buf; @@ -661,9 +672,11 @@ read_dev_chars (int irq, void **buffer, int length) } + } else { + local_irq_restore(flags); } - return (ret); + return ret; } /* @@ -698,7 +711,7 @@ read_conf_data (int irq, void **buffer, int *length, __u8 lpm) * scan for RCD command in extended SenseID data */ - for (ciw_cnt = 0; (found == 0) && (ciw_cnt < 62); ciw_cnt++) { + for (ciw_cnt = 0; (found == 0) && (ciw_cnt < MAX_CIWS); ciw_cnt++) { if (ioinfo[irq]->senseid.ciw[ciw_cnt].ct == CIW_TYPE_RCD) { /* * paranoia check ... @@ -817,11 +830,10 @@ read_conf_data (int irq, void **buffer, int *length, __u8 lpm) } while (retry); } - - local_irq_restore (flags); - } + local_irq_restore(flags); + /* * on success we update the user input parms */ @@ -867,8 +879,8 @@ read_conf_data (int irq, void **buffer, int *length, __u8 lpm) void s390_device_recognition_irq (int irq) { - int ret; char dbf_txt[15]; + devstat_t devstat; sprintf (dbf_txt, "devrec%x", irq); CIO_TRACE_EVENT (4, dbf_txt); @@ -877,52 +889,78 @@ s390_device_recognition_irq (int irq) * We issue the SenseID command on I/O subchannels we think are * operational only. */ - if ((ioinfo[irq] != INVALID_STORAGE_AREA) - && (!ioinfo[irq]->st) - && (ioinfo[irq]->schib.pmcw.st == 0) - && (ioinfo[irq]->ui.flags.oper == 1)) { - int irq_ret; - devstat_t devstat; + if ((ioinfo[irq]->st) || (!ioinfo[irq]->ui.flags.oper)) + goto out; - irq_ret = request_irq (irq, - init_IRQ_handler, - SA_PROBE, "INIT", &devstat); + if (request_irq (irq, init_IRQ_handler, SA_PROBE, "INIT", &devstat)) + goto out; - if (!irq_ret) { - ret = enable_cpu_sync_isc (irq); + if (enable_cpu_sync_isc(irq)) + goto out_freeirq; - if (!ret) { - ioinfo[irq]->ui.flags.unknown = 0; + ioinfo[irq]->ui.flags.unknown = 0; - memset (&ioinfo[irq]->senseid, '\0', - sizeof (senseid_t)); + memset (&ioinfo[irq]->senseid, '\0', sizeof (senseid_t)); - if (cio_sid_with_pgid) { - - ret = s390_DevicePathVerification(irq,0); - - if (ret == -EOPNOTSUPP) - /* - * Doesn't prevent us from proceeding - */ - ret = 0; - } + s390_SenseID (irq, &ioinfo[irq]->senseid, 0xff); - /* - * we'll fallthrough here if we don't want - * to do SPID before SID - */ - if (!ret) { - s390_SenseID (irq, &ioinfo[irq]->senseid, 0xff); - } - disable_cpu_sync_isc (irq); + disable_cpu_sync_isc(irq); +out_freeirq: + free_irq (irq, &devstat); +out: + return; +} - } +static int +css_bus_match (struct device *dev, struct device_driver *drv) +{ + struct subchannel *ioinfo; + struct subchannel_driver *sdrv; - free_irq (irq, &devstat); + ioinfo = list_entry (dev, struct subchannel, dev); + sdrv = list_entry (drv, struct subchannel_driver, drv); - } - } + return (ioinfo->st == sdrv->st); +} + +struct bus_type css_bus_type = { + .name = "css", + .match = &css_bus_match, +}; + +static struct device css_bus_device = { + .name = "Channel Subsystem", + .bus_id = "css0", +}; + +/* + * s390_register_subchannel + * + * initializes the struct device member of a subchannel and gives it to + * the device driver core + */ +static __devinit void +s390_register_subchannel (struct subchannel *ioinfo) +{ + static const char *subchannel_types[] = { + "I/O Subchannel", + "CHSC Subchannel", + "Message Subchannel", + "ADM Subchannel", + "undefined subchannel type 4", + "undefined subchannel type 5", + "undefined subchannel type 6", + "undefined subchannel type 7", + }; + + strncpy (ioinfo->dev.name, subchannel_types[ioinfo->st], DEVICE_NAME_SIZE); + snprintf (ioinfo->dev.bus_id, DEVICE_ID_SIZE, "0:%04x", ioinfo->irq); + + ioinfo->dev.bus = &css_bus_type; + ioinfo->dev.parent = &css_bus_device; + + if (device_register(&ioinfo->dev)) + printk(KERN_WARNING "failed to register subchannel %04x\n", ioinfo->irq); } /* @@ -931,19 +969,43 @@ s390_device_recognition_irq (int irq) * Used for system wide device recognition. * */ -static void __init -s390_device_recognition_all (void) +static int __init +s390_probe_css (void) { - int irq = 0; /* let's start with subchannel 0 ... */ + int ret; + int irq; - do { - s390_device_recognition_irq (irq); + printk (KERN_INFO "probing %d subchannels...\n", highest_subchannel+1); + + if ((ret = bus_register(&css_bus_type))) + goto out; + + if ((ret = device_register(&css_bus_device))) + goto out_unregister; - irq++; + for (irq = 0; irq <= highest_subchannel; irq++) { + if (ioinfo[irq] == INVALID_STORAGE_AREA) + continue; + + printk(KERN_INFO "subchannel %04x: devno %04x, ", + irq, ioinfo[irq]->devno); + + if (irq != cons_dev) /* console has already been probed */ + s390_device_recognition_irq (irq); + + s390_register_subchannel(ioinfo[irq]); + printk("control unit type %04x\n", ioinfo[irq]->senseid.cu_type); + } - } while (irq <= highest_subchannel); +out_unregister: + if (ret) + put_bus(&css_bus_type); +out: + return ret; } +subsys_initcall(s390_probe_css); + /* * s390_process_subchannels * @@ -1301,7 +1363,13 @@ s390_validate_subchannel (int irq, int enable) static int s390_SenseID (int irq, senseid_t * sid, __u8 lpm) { - ccw1_t *sense_ccw; /* ccw area for SenseID command */ + /* SenseID may be called during console initialization, + * before we have a working kmalloc, using a static + * ccw area is the least evil workaround */ + static ccw1_t sense_ccw[2]; /* ccw area for SenseID command */ + static spinlock_t sid_lock /* lock to protect sense_ccw */ + = SPIN_LOCK_UNLOCKED; + senseid_t isid; /* internal sid */ devstat_t devstat; /* required by request_irq() */ __u8 pathmask; /* calulate path mask */ @@ -1318,6 +1386,7 @@ s390_SenseID (int irq, senseid_t * sid, __u8 lpm) char dbf_txt[15]; int i; int failure = 0; /* nothing went wrong yet */ + unsigned long flags; if (ioinfo[irq]->ui.flags.oper == 0) { return -ENODEV; @@ -1327,44 +1396,27 @@ s390_SenseID (int irq, senseid_t * sid, __u8 lpm) sprintf (dbf_txt, "snsID%x", irq); CIO_TRACE_EVENT (4, dbf_txt); - inlreq = 0; /* to make the compiler quiet... */ - if (!ioinfo[irq]->ui.flags.ready) { - - pdevstat = &devstat; - + inlreq = 1; /* * Perform SENSE ID command processing. We have to request device * ownership and provide a dummy I/O handler. We issue sync. I/O * requests and evaluate the devstat area on return therefore * we don't need a real I/O handler in place. */ - irq_ret = - request_irq (irq, init_IRQ_handler, SA_PROBE, "SID", - &devstat); + if ((irq_ret = request_irq (irq, init_IRQ_handler, + SA_PROBE, "SID", &devstat))) + return irq_ret; - if (irq_ret == 0) - inlreq = 1; } else { inlreq = 0; - irq_ret = 0; - pdevstat = ioinfo[irq]->irq_desc.dev_id; - } - if (irq_ret) { - return irq_ret; - } + pdevstat = ioinfo[irq]->irq_desc.dev_id; + spin_lock_irqsave(&sid_lock, flags); s390irq_spin_lock (irq); - if (init_IRQ_complete) { - sense_ccw = kmalloc (2 * sizeof (ccw1_t), GFP_DMA); - } else { - sense_ccw = alloc_bootmem_low (2 * sizeof (ccw1_t)); - - } - /* more than one path installed ? */ if (ioinfo[irq]->schib.pmcw.pim != 0x80) { sense_ccw[0].cmd_code = CCW_CMD_SUSPEND_RECONN; @@ -1584,14 +1636,8 @@ s390_SenseID (int irq, senseid_t * sid, __u8 lpm) } - if (init_IRQ_complete) { - kfree (sense_ccw); - } else { - free_bootmem ((unsigned long) sense_ccw, 2 * sizeof (ccw1_t)); - - } - s390irq_spin_unlock (irq); + spin_unlock_irqrestore(&sid_lock, flags); /* * If we installed the irq action handler we have to @@ -1606,7 +1652,7 @@ s390_SenseID (int irq, senseid_t * sid, __u8 lpm) */ if ((sid->cu_type == 0xFFFF) && (MACHINE_IS_VM)) { - _VM_virtual_device_info (ioinfo[irq]->schib.pmcw.dev, sid); + VM_virtual_device_info (ioinfo[irq]->schib.pmcw.dev, sid); } if (sid->cu_type == 0xFFFF) { @@ -1622,43 +1668,18 @@ s390_SenseID (int irq, senseid_t * sid, __u8 lpm) ioinfo[irq]->schib.pmcw.dev, irq); ioinfo[irq]->ui.flags.unknown = 1; - + return -ENODEV; } /* * Issue device info message if unit was operational . */ - if (!ioinfo[irq]->ui.flags.unknown) { - if (sid->dev_type != 0) { - - CIO_DEBUG_IFMSG(KERN_INFO, 2, - "SenseID : device %04X reports: " - "CU Type/Mod = %04X/%02X," - " Dev Type/Mod = %04X/%02X\n", - ioinfo[irq]->schib.pmcw.dev, - sid->cu_type, - sid->cu_model, - sid->dev_type, - sid->dev_model); - - } else { + CIO_DEBUG_IFMSG(KERN_INFO, 2, "SenseID : device %04X reports: " + "CU Type/Mod = %04X/%02X, Dev Type/Mod = %04X/%02X\n", + ioinfo[irq]->schib.pmcw.dev, sid->cu_type, sid->cu_model, + sid->dev_type, sid->dev_model); - CIO_DEBUG_IFMSG(KERN_INFO, 2, - "SenseID : device %04X reports:" - " Dev Type/Mod = %04X/%02X\n", - ioinfo[irq]->schib.pmcw.dev, - sid->cu_type, - sid->cu_model); - } - - } - - if (!ioinfo[irq]->ui.flags.unknown) - irq_ret = 0; - else - irq_ret = -ENODEV; - - return (irq_ret); + return 0; } /* @@ -1683,7 +1704,6 @@ s390_DevicePathVerification (int irq, __u8 usermask) int i; pgid_t pgid; __u8 dev_path; - int first = 1; char dbf_txt[15]; @@ -1758,7 +1778,7 @@ s390_DevicePathVerification (int irq, __u8 usermask) & ioinfo[irq]->schib.pmcw.pam & ioinfo[irq]->schib.pmcw.pom; chsc_validate_chpids(irq); - + if ((ioinfo[irq]->opm == 0) && (old_opm)) { not_oper_handler_func_t nopfunc=ioinfo[irq]->nopfunc; int was_oper = ioinfo[irq]->ui.flags.ready; @@ -1803,7 +1823,6 @@ s390_DevicePathVerification (int irq, __u8 usermask) memcpy (&ioinfo[irq]->pgid, &global_pgid, sizeof (pgid_t)); ioinfo[irq]->ui.flags.pgid = 1; } - memcpy (&pgid, &ioinfo[irq]->pgid, sizeof (pgid_t)); for (i = 0; i < 8 && !ret; i++) { pathmask = 0x80 >> i; @@ -1811,46 +1830,38 @@ s390_DevicePathVerification (int irq, __u8 usermask) domask = dev_path & pathmask; if (domask) { - ret = s390_SetPGID (irq, domask, &pgid); - - /* - * For the *first* path we are prepared - * for recovery - * - * - If we fail setting the PGID we assume its - * using a different PGID already (VM) we - * try to sense. - */ - if (ret == -EOPNOTSUPP && first) { + if (MACHINE_IS_VM) { + /* + * If we are running under VM, try to obtain + * VM's PGID by SensePGID + */ *(int *) &pgid = 0; - - ret = s390_SensePGID (irq, domask, &pgid); - first = 0; - - if (ret == 0) { + + if (!s390_SensePGID (irq, domask, &pgid)) /* * Check whether we retrieved * a reasonable PGID ... */ if (pgid.inf.ps.state1 == - SNID_STATE1_GROUPED) { + SNID_STATE1_GROUPED) memcpy (&ioinfo[irq]->pgid, &pgid, sizeof (pgid_t)); - } else { /* ungrouped or garbage ... */ - ret = -EOPNOTSUPP; - } - } else { - ioinfo[irq]->ui.flags.pgid_supp = 0; + } + + ret = s390_SetPGID (irq, domask); - CIO_DEBUG(KERN_WARNING, 2, - "PathVerification(%04X) " - "- Device %04X doesn't " - " support path grouping\n", - irq, - ioinfo[irq]->schib.pmcw.dev); - - } + if (ret == -EOPNOTSUPP) { + + ioinfo[irq]->ui.flags.pgid_supp = 0; + + CIO_DEBUG(KERN_WARNING, 2, + "PathVerification(%04X) " + "- Device %04X doesn't " + " support path grouping\n", + irq, + ioinfo[irq]->schib.pmcw.dev); + } else if (ret == -EIO) { CIO_DEBUG(KERN_ERR, 2, @@ -1873,12 +1884,19 @@ s390_DevicePathVerification (int irq, __u8 usermask) ret = 0; - } else { + } else if (ret == -ENODEV) { CIO_DEBUG(KERN_ERR, 2, - "PathVerification(%04X) - " - "Unexpected error on device %04X\n", + "PathVerification(%04X) " + "- Device %04X is no longer there?!?\n", irq, ioinfo[irq]->schib.pmcw.dev); + + } else if (ret) { + + CIO_DEBUG(KERN_ERR, 2, + "PathVerification(%04X) - " + "Unexpected error %d on device %04X\n", + irq, ret, ioinfo[irq]->schib.pmcw.dev); ioinfo[irq]->ui.flags.pgid_supp = 0; } @@ -1895,7 +1913,7 @@ s390_DevicePathVerification (int irq, __u8 usermask) * */ static int -s390_SetPGID (int irq, __u8 lpm, pgid_t * pgid) +s390_SetPGID (int irq, __u8 lpm) { ccw1_t *spid_ccw; /* ccw area for SPID command */ devstat_t devstat; /* required by request_irq() */ @@ -1953,11 +1971,11 @@ s390_SetPGID (int irq, __u8 lpm, pgid_t * pgid) spid_ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC; spid_ccw[1].cmd_code = CCW_CMD_SET_PGID; - spid_ccw[1].cda = (__u32) virt_to_phys (pgid); + spid_ccw[1].cda = (__u32) virt_to_phys (&ioinfo[irq]->pgid); spid_ccw[1].count = sizeof (pgid_t); spid_ccw[1].flags = CCW_FLAG_SLI; - pgid->inf.fc = SPID_FUNC_MULTI_PATH | SPID_FUNC_ESTABLISH; + ioinfo[irq]->pgid.inf.fc = SPID_FUNC_MULTI_PATH | SPID_FUNC_ESTABLISH; /* * We now issue a SetPGID request. In case of BUSY @@ -2011,13 +2029,13 @@ s390_SetPGID (int irq, __u8 lpm, pgid_t * pgid) spid_ccw[0].cmd_code = CCW_CMD_SET_PGID; spid_ccw[0].cda = (__u32) - virt_to_phys (pgid); + virt_to_phys (&ioinfo[irq]->pgid); spid_ccw[0].count = sizeof (pgid_t); spid_ccw[0].flags = CCW_FLAG_SLI; - pgid->inf.fc = + ioinfo[irq]->pgid.inf.fc = SPID_FUNC_SINGLE_PATH | SPID_FUNC_ESTABLISH; mpath = 0; @@ -2071,6 +2089,7 @@ s390_SetPGID (int irq, __u8 lpm, pgid_t * pgid) retry = 0; ioinfo[irq]->opm &= ~lpm; + switch_off_chpids(irq, lpm); irq_ret = -EAGAIN; } @@ -2133,6 +2152,7 @@ s390_SensePGID (int irq, __u8 lpm, pgid_t * pgid) devstat_t devstat; /* required by request_irq() */ devstat_t *pdevstat = &devstat; char dbf_txt[15]; + pgid_t * tmp_pgid; int irq_ret = 0; /* return code */ int retry = 5; /* retry count */ @@ -2174,13 +2194,14 @@ s390_SensePGID (int irq, __u8 lpm, pgid_t * pgid) if (init_IRQ_complete) { snid_ccw = kmalloc (sizeof (ccw1_t), GFP_DMA); + tmp_pgid = kmalloc (sizeof (pgid_t), GFP_DMA); } else { snid_ccw = alloc_bootmem_low (sizeof (ccw1_t)); - + tmp_pgid = alloc_bootmem_low (sizeof (pgid_t)); } snid_ccw->cmd_code = CCW_CMD_SENSE_PGID; - snid_ccw->cda = (__u32) virt_to_phys (pgid); + snid_ccw->cda = (__u32) virt_to_phys (tmp_pgid); snid_ccw->count = sizeof (pgid_t); snid_ccw->flags = CCW_FLAG_SLI; @@ -2256,6 +2277,7 @@ s390_SensePGID (int irq, __u8 lpm, pgid_t * pgid) } else { retry = 0; /* success ... */ irq_ret = 0; + memcpy(pgid, tmp_pgid, sizeof(pgid_t)); } } else if (irq_ret != -ENODEV) { /* -EIO, or -EBUSY */ @@ -2290,9 +2312,10 @@ s390_SensePGID (int irq, __u8 lpm, pgid_t * pgid) if (init_IRQ_complete) { kfree (snid_ccw); + kfree (tmp_pgid); } else { free_bootmem ((unsigned long) snid_ccw, sizeof (ccw1_t)); - + free_bootmem ((unsigned long) tmp_pgid, sizeof (tmp_pgid)); } s390irq_spin_unlock_irqrestore (irq, flags); @@ -2311,13 +2334,11 @@ s390_SensePGID (int irq, __u8 lpm, pgid_t * pgid) * Function: s390_send_nop * * sends a nop CCW to the specified subchannel down the given path(s) - * FIXME: why not put nop_ccw on the stack, it's only 64 bits? */ int s390_send_nop(int irq, __u8 lpm) { char dbf_txt[15]; - ccw1_t *nop_ccw; devstat_t devstat; devstat_t *pdevstat = &devstat; unsigned long flags; @@ -2325,6 +2346,14 @@ s390_send_nop(int irq, __u8 lpm) int irq_ret = 0; int inlreq = 0; + /* static allocation is necessary because nop_ccw has to be below + * the 2GB border. locking is not required since it nop_ccw is + * never modified */ + static ccw1_t nop_ccw = { + .cmd_code = CCW_CMD_NOOP, + .flags = CCW_FLAG_SLI, + }; + if (!ioinfo[irq]->ui.flags.oper) /* no sense in trying */ return -ENODEV; @@ -2352,19 +2381,9 @@ s390_send_nop(int irq, __u8 lpm) s390irq_spin_lock_irqsave (irq, flags); - if (init_IRQ_complete) - nop_ccw = kmalloc (sizeof (ccw1_t), GFP_DMA); - else - nop_ccw = alloc_bootmem_low (sizeof (ccw1_t)); - - nop_ccw->cmd_code = CCW_CMD_NOOP; - nop_ccw->cda = 0; - nop_ccw->count = 0; - nop_ccw->flags = CCW_FLAG_SLI; - memset (pdevstat, '\0', sizeof (devstat_t)); - irq_ret = s390_start_IO (irq, nop_ccw, 0xE2D5D6D7, lpm, + irq_ret = s390_start_IO (irq, &nop_ccw, 0xE2D5D6D7, lpm, DOIO_WAIT_FOR_INTERRUPT | DOIO_TIMEOUT | DOIO_DONT_CALL_INTHDLR @@ -2376,11 +2395,6 @@ s390_send_nop(int irq, __u8 lpm) cancel_IO(irq); } - if (init_IRQ_complete) - kfree (nop_ccw); - else - free_bootmem ((unsigned long) nop_ccw, sizeof (ccw1_t)); - s390irq_spin_unlock_irqrestore (irq, flags); if (inlreq) diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index df578c454da2..2e055e58703b 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef CONFIG_SMP #include #endif @@ -51,6 +52,7 @@ static spinlock_t mchchk_queue_lock = SPIN_LOCK_UNLOCKED; static spinlock_t crw_queue_lock = SPIN_LOCK_UNLOCKED; static struct semaphore s_sem; +static DECLARE_COMPLETION(mchchk_thread_active); #ifdef CONFIG_MACHCHK_WARNING static int mchchk_wng_posted = 0; @@ -103,14 +105,15 @@ s390_init_machine_check(void) kernel_thread(s390_machine_check_handler, &s_sem, CLONE_FS | CLONE_FILES); + wait_for_completion(&mchchk_thread_active); + ctl_clear_bit(14, 25); /* disable damage MCH */ ctl_set_bit(14, 26); /* enable degradation MCH */ ctl_set_bit(14, 27); /* enable system recovery MCH */ - ctl_set_bit(14, 28); /* enable channel report MCH */ -#ifdef CONFIG_MACHCK_WARNING +#ifdef CONFIG_MACHCHK_WARNING ctl_set_bit(14, 24); /* enable warning MCH */ #endif @@ -127,6 +130,30 @@ s390_init_machine_check(void) return; } +/* + * initialize the machine check handler really early to be able to + * catch all machine checks that happen during boot + */ +static int __init +machine_check_init (void) +{ + s390_init_machine_check(); + return 0; +} +arch_initcall(machine_check_init); + +/* + * Machine checks for the channel subsystem must be enabled + * after the channel subsystem is initialized + */ +static int __init +machine_check_crw_init (void) +{ + ctl_set_bit(14, 28); /* enable channel report MCH */ + return 0; +} +device_initcall (machine_check_crw_init); + static void s390_handle_damage(char *msg) { @@ -244,6 +271,8 @@ s390_machine_check_handler(void *parm) DBG(KERN_NOTICE "mach_handler : ready\n"); + complete(&mchchk_thread_active); + do { DBG(KERN_NOTICE "mach_handler : waiting for wakeup\n"); diff --git a/include/asm-s390/irq.h b/include/asm-s390/irq.h index 5ccf5013a518..bcc71eccbc63 100644 --- a/include/asm-s390/irq.h +++ b/include/asm-s390/irq.h @@ -637,6 +637,12 @@ int s390_request_irq_special( int irq, const char *devname, void *dev_id); +extern int s390_request_console_irq (int irq, + void (*handler) (int, void *, struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id); + extern int set_cons_dev(int irq); extern int wait_cons_dev(int irq); extern schib_t *s390_get_schib( int irq ); @@ -860,28 +866,8 @@ typedef struct { __u32 vrdccrft : 8; /* real device feature (output) */ } __attribute__ ((packed,aligned(4))) diag210_t; -void VM_virtual_device_info( __u16 devno, /* device number */ - senseid_t *ps ); /* ptr to senseID data */ - -extern __inline__ int diag210( diag210_t * addr) -{ - int ccode; +extern int diag210( diag210_t * addr); - __asm__ __volatile__( -#ifdef CONFIG_ARCH_S390X - " sam31\n" - " diag %1,0,0x210\n" - " sam64\n" -#else - " diag %1,0,0x210\n" -#endif - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "a" (addr) - : "cc" ); - return ccode; -} extern __inline__ int chsc( chsc_area_t * chsc_area) { int cc; diff --git a/include/asm-s390/s390io.h b/include/asm-s390/s390io.h index 95f9c5b61973..8424a470e887 100644 --- a/include/asm-s390/s390io.h +++ b/include/asm-s390/s390io.h @@ -9,22 +9,25 @@ #ifndef __s390io_h #define __s390io_h +#include + /* * IRQ data structure used by I/O subroutines * * Note : If bit flags are added, the "unused" value must be * decremented accordingly ! */ -typedef struct _ioinfo { +typedef struct subchannel { unsigned int irq; /* aka. subchannel number */ spinlock_t irq_lock; /* irq lock */ - void *private_data; /* pointer to private data */ - - struct _ioinfo *prev; - struct _ioinfo *next; __u8 st; /* subchannel type */ + void *private_data; /* pointer to private data */ + + struct subchannel *prev; + struct subchannel *next; + union { unsigned int info; struct { @@ -78,8 +81,26 @@ typedef struct _ioinfo { unsigned long qflag; /* queued flags */ __u8 qlpm; /* queued logical path mask */ ssd_info_t ssd_info; /* subchannel description */ + struct device dev; /* entry in device tree */ +} __attribute__ ((aligned(8))) ioinfo_t; + - } __attribute__ ((aligned(8))) ioinfo_t; +/* + * There are four different subchannel types, but we are currently + * only interested in I/O subchannels. This means there is only + * one subchannel_driver, other subchannels belonging to css_bus_type + * are simply ignored. + */ +struct subchannel_driver { + enum { + SUBCHANNEL_TYPE_IO = 0, + SUBCHANNEL_TYPE_CHSC = 1, + SUBCHANNEL_TYPE_MESSAGE = 2, + SUBCHANNEL_TYPE_ADM = 3, + } st; /* subchannel type */ + struct device_driver drv; /* entry in driver tree */ +}; +extern struct bus_type css_bus_type; #define IOINFO_FLAGS_BUSY 0x80000000 #define IOINFO_FLAGS_OPER 0x40000000 diff --git a/include/asm-s390x/irq.h b/include/asm-s390x/irq.h index 5ccf5013a518..bcc71eccbc63 100644 --- a/include/asm-s390x/irq.h +++ b/include/asm-s390x/irq.h @@ -637,6 +637,12 @@ int s390_request_irq_special( int irq, const char *devname, void *dev_id); +extern int s390_request_console_irq (int irq, + void (*handler) (int, void *, struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id); + extern int set_cons_dev(int irq); extern int wait_cons_dev(int irq); extern schib_t *s390_get_schib( int irq ); @@ -860,28 +866,8 @@ typedef struct { __u32 vrdccrft : 8; /* real device feature (output) */ } __attribute__ ((packed,aligned(4))) diag210_t; -void VM_virtual_device_info( __u16 devno, /* device number */ - senseid_t *ps ); /* ptr to senseID data */ - -extern __inline__ int diag210( diag210_t * addr) -{ - int ccode; +extern int diag210( diag210_t * addr); - __asm__ __volatile__( -#ifdef CONFIG_ARCH_S390X - " sam31\n" - " diag %1,0,0x210\n" - " sam64\n" -#else - " diag %1,0,0x210\n" -#endif - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) - : "a" (addr) - : "cc" ); - return ccode; -} extern __inline__ int chsc( chsc_area_t * chsc_area) { int cc; diff --git a/include/asm-s390x/s390io.h b/include/asm-s390x/s390io.h index 9a984f96dcfa..8424a470e887 100644 --- a/include/asm-s390x/s390io.h +++ b/include/asm-s390x/s390io.h @@ -9,21 +9,24 @@ #ifndef __s390io_h #define __s390io_h +#include + /* * IRQ data structure used by I/O subroutines * * Note : If bit flags are added, the "unused" value must be * decremented accordingly ! */ -typedef struct _ioinfo { +typedef struct subchannel { unsigned int irq; /* aka. subchannel number */ spinlock_t irq_lock; /* irq lock */ - void *private_data; /* pointer to private data */ - struct _ioinfo *prev; - struct _ioinfo *next; + __u8 st; /* subchannel type */ + + void *private_data; /* pointer to private data */ - __u8 st; /* subchannel type */ + struct subchannel *prev; + struct subchannel *next; union { unsigned int info; @@ -78,8 +81,26 @@ typedef struct _ioinfo { unsigned long qflag; /* queued flags */ __u8 qlpm; /* queued logical path mask */ ssd_info_t ssd_info; /* subchannel description */ + struct device dev; /* entry in device tree */ +} __attribute__ ((aligned(8))) ioinfo_t; - } __attribute__ ((aligned(8))) ioinfo_t; + +/* + * There are four different subchannel types, but we are currently + * only interested in I/O subchannels. This means there is only + * one subchannel_driver, other subchannels belonging to css_bus_type + * are simply ignored. + */ +struct subchannel_driver { + enum { + SUBCHANNEL_TYPE_IO = 0, + SUBCHANNEL_TYPE_CHSC = 1, + SUBCHANNEL_TYPE_MESSAGE = 2, + SUBCHANNEL_TYPE_ADM = 3, + } st; /* subchannel type */ + struct device_driver drv; /* entry in driver tree */ +}; +extern struct bus_type css_bus_type; #define IOINFO_FLAGS_BUSY 0x80000000 #define IOINFO_FLAGS_OPER 0x40000000 -- cgit v1.2.3 From c5e6d236c3f8f9e0ae4a224c8d8cd467d57d4fa5 Mon Sep 17 00:00:00 2001 From: Maksim Krasnyanskiy Date: Fri, 4 Oct 2002 03:38:30 -0700 Subject: RFCOMM protocol support. RFCOMM socket and TTY emulation APIs. --- include/net/bluetooth/rfcomm.h | 346 ++++++++ net/bluetooth/Config.help | 15 + net/bluetooth/Config.in | 1 + net/bluetooth/Makefile | 6 + net/bluetooth/rfcomm/Config.in | 5 + net/bluetooth/rfcomm/Makefile | 11 + net/bluetooth/rfcomm/core.c | 1711 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/rfcomm/crc.c | 71 ++ net/bluetooth/rfcomm/sock.c | 869 ++++++++++++++++++++ net/bluetooth/rfcomm/tty.c | 884 +++++++++++++++++++++ 10 files changed, 3919 insertions(+) create mode 100644 include/net/bluetooth/rfcomm.h create mode 100644 net/bluetooth/rfcomm/Config.in create mode 100644 net/bluetooth/rfcomm/Makefile create mode 100644 net/bluetooth/rfcomm/core.c create mode 100644 net/bluetooth/rfcomm/crc.c create mode 100644 net/bluetooth/rfcomm/sock.c create mode 100644 net/bluetooth/rfcomm/tty.c (limited to 'include') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h new file mode 100644 index 000000000000..2efca665decf --- /dev/null +++ b/include/net/bluetooth/rfcomm.h @@ -0,0 +1,346 @@ +/* + RFCOMM implementation for Linux Bluetooth stack (BlueZ). + Copyright (C) 2002 Maxim Krasnyansky + Copyright (C) 2002 Marcel Holtmann + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +/* + RPN support - Dirk Husemann +*/ + +/* + * $Id: rfcomm.h,v 1.29 2002/10/02 20:26:17 maxk Exp $ + */ + +#ifndef __RFCOMM_H +#define __RFCOMM_H + +#define RFCOMM_PSM 3 + +#define RFCOMM_CONN_TIMEOUT (HZ * 30) +#define RFCOMM_DISC_TIMEOUT (HZ * 20) + +#define RFCOMM_DEFAULT_MTU 127 +#define RFCOMM_DEFAULT_CREDITS 7 + +#define RFCOMM_MAX_L2CAP_MTU 1024 +#define RFCOMM_MAX_CREDITS 40 + +#define RFCOMM_SKB_HEAD_RESERVE 8 +#define RFCOMM_SKB_TAIL_RESERVE 2 +#define RFCOMM_SKB_RESERVE (RFCOMM_SKB_HEAD_RESERVE + RFCOMM_SKB_TAIL_RESERVE) + +#define RFCOMM_SABM 0x2f +#define RFCOMM_DISC 0x43 +#define RFCOMM_UA 0x63 +#define RFCOMM_DM 0x0f +#define RFCOMM_UIH 0xef + +#define RFCOMM_TEST 0x08 +#define RFCOMM_FCON 0x28 +#define RFCOMM_FCOFF 0x18 +#define RFCOMM_MSC 0x38 +#define RFCOMM_RPN 0x24 +#define RFCOMM_RLS 0x14 +#define RFCOMM_PN 0x20 +#define RFCOMM_NSC 0x04 + +#define RFCOMM_V24_FC 0x02 +#define RFCOMM_V24_RTC 0x04 +#define RFCOMM_V24_RTR 0x08 +#define RFCOMM_V24_IC 0x40 +#define RFCOMM_V24_DV 0x80 + +#define RFCOMM_RPN_BR_2400 0x0 +#define RFCOMM_RPN_BR_4800 0x1 +#define RFCOMM_RPN_BR_7200 0x2 +#define RFCOMM_RPN_BR_9600 0x3 +#define RFCOMM_RPN_BR_19200 0x4 +#define RFCOMM_RPN_BR_38400 0x5 +#define RFCOMM_RPN_BR_57600 0x6 +#define RFCOMM_RPN_BR_115200 0x7 +#define RFCOMM_RPN_BR_230400 0x8 + +#define RFCOMM_RPN_DATA_5 0x0 +#define RFCOMM_RPN_DATA_6 0x1 +#define RFCOMM_RPN_DATA_7 0x2 +#define RFCOMM_RPN_DATA_8 0x3 + +#define RFCOMM_RPN_STOP_1 0 +#define RFCOMM_RPN_STOP_15 1 + +#define RFCOMM_RPN_PARITY_NONE 0x0 +#define RFCOMM_RPN_PARITY_ODD 0x4 +#define RFCOMM_RPN_PARITY_EVEN 0x5 +#define RFCOMM_RPN_PARITY_MARK 0x6 +#define RFCOMM_RPN_PARITY_SPACE 0x7 + +#define RFCOMM_RPN_FLOW_NONE 0x00 + +#define RFCOMM_RPN_XON_CHAR 0x11 +#define RFCOMM_RPN_XOFF_CHAR 0x13 + +#define RFCOMM_RPN_PM_BITRATE 0x0001 +#define RFCOMM_RPN_PM_DATA 0x0002 +#define RFCOMM_RPN_PM_STOP 0x0004 +#define RFCOMM_RPN_PM_PARITY 0x0008 +#define RFCOMM_RPN_PM_PARITY_TYPE 0x0010 +#define RFCOMM_RPN_PM_XON 0x0020 +#define RFCOMM_RPN_PM_XOFF 0x0040 +#define RFCOMM_RPN_PM_FLOW 0x3F00 + +#define RFCOMM_RPN_PM_ALL 0x3F7F + +struct rfcomm_hdr { + u8 addr; + u8 ctrl; + u8 len; // Actual size can be 2 bytes +} __attribute__ ((packed)); + +struct rfcomm_cmd { + u8 addr; + u8 ctrl; + u8 len; + u8 fcs; +} __attribute__ ((packed)); + +struct rfcomm_mcc { + u8 type; + u8 len; +} __attribute__ ((packed)); + +struct rfcomm_pn { + u8 dlci; + u8 flow_ctrl; + u8 priority; + u8 ack_timer; + u16 mtu; + u8 max_retrans; + u8 credits; +} __attribute__ ((packed)); + +struct rfcomm_rpn { + u8 dlci; + u8 bit_rate; + u8 line_settings; + u8 flow_ctrl; + u8 xon_char; + u8 xoff_char; + u16 param_mask; +} __attribute__ ((packed)); + +struct rfcomm_msc { + u8 dlci; + u8 v24_sig; +} __attribute__ ((packed)); + +/* ---- Core structures, flags etc ---- */ + +struct rfcomm_session { + struct list_head list; + struct socket *sock; + unsigned long state; + unsigned long flags; + atomic_t refcnt; + int initiator; + + /* Default DLC parameters */ + uint mtu; + uint credits; + + struct list_head dlcs; +}; + +struct rfcomm_dlc { + struct list_head list; + struct rfcomm_session *session; + struct sk_buff_head tx_queue; + struct timer_list timer; + + spinlock_t lock; + unsigned long state; + unsigned long flags; + atomic_t refcnt; + u8 dlci; + u8 addr; + + uint mtu; + u8 v24_sig; + + uint credits; + uint rx_credits; + uint tx_credits; + + void *owner; + + void (*data_ready)(struct rfcomm_dlc *d, struct sk_buff *skb); + void (*state_change)(struct rfcomm_dlc *d, int err); + void (*modem_status)(struct rfcomm_dlc *d, int v24_sig); +}; + +/* DLC and session flags */ +#define RFCOMM_RX_THROTTLED 0 +#define RFCOMM_TX_THROTTLED 1 +#define RFCOMM_MSC_PENDING 2 +#define RFCOMM_TIMED_OUT 3 + +/* Scheduling flags and events */ +#define RFCOMM_SCHED_STATE 0 +#define RFCOMM_SCHED_RX 1 +#define RFCOMM_SCHED_TX 2 +#define RFCOMM_SCHED_TIMEO 3 +#define RFCOMM_SCHED_WAKEUP 31 + +extern struct task_struct *rfcomm_thread; +extern unsigned long rfcomm_event; + +static inline void rfcomm_schedule(uint event) +{ + if (!rfcomm_thread) + return; + + //set_bit(event, &rfcomm_event); + if (!test_and_set_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) + wake_up_process(rfcomm_thread); +} + +extern struct semaphore rfcomm_sem; +#define rfcomm_lock() down(&rfcomm_sem); +#define rfcomm_unlock() up(&rfcomm_sem); + +/* ---- RFCOMM DLCs (channels) ---- */ +struct rfcomm_dlc *rfcomm_dlc_alloc(int prio); +void rfcomm_dlc_free(struct rfcomm_dlc *d); +int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel); +int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason); +int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb); +int rfcomm_dlc_modem_status(struct rfcomm_dlc *d, int v24_sig); + +#define rfcomm_dlc_lock(d) spin_lock(&d->lock) +#define rfcomm_dlc_unlock(d) spin_unlock(&d->lock) + +static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d) +{ + atomic_inc(&d->refcnt); +} + +static inline void rfcomm_dlc_put(struct rfcomm_dlc *d) +{ + if (atomic_dec_and_test(&d->refcnt)) + rfcomm_dlc_free(d); +} + +extern void FASTCALL(__rfcomm_dlc_throttle(struct rfcomm_dlc *d)); +extern void FASTCALL(__rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)); + +static inline void rfcomm_dlc_throttle(struct rfcomm_dlc *d) +{ + if (!test_and_set_bit(RFCOMM_RX_THROTTLED, &d->flags)) + __rfcomm_dlc_throttle(d); +} + +static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) +{ + if (test_and_clear_bit(RFCOMM_RX_THROTTLED, &d->flags)) + __rfcomm_dlc_unthrottle(d); +} + +/* ---- RFCOMM sessions ---- */ +struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state); +struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst); +struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err); +void rfcomm_session_del(struct rfcomm_session *s); +void rfcomm_session_close(struct rfcomm_session *s, int err); + +static inline void rfcomm_session_hold(struct rfcomm_session *s) +{ + atomic_inc(&s->refcnt); +} + +static inline void rfcomm_session_put(struct rfcomm_session *s) +{ + if (atomic_dec_and_test(&s->refcnt)) + rfcomm_session_del(s); +} + +/* ---- RFCOMM chechsum ---- */ +extern u8 rfcomm_crc_table[]; + +/* ---- RFCOMM sockets ---- */ +struct sockaddr_rc { + sa_family_t rc_family; + bdaddr_t rc_bdaddr; + u8 rc_channel; +}; + +#define rfcomm_pi(sk) ((struct rfcomm_pinfo *) &sk->protinfo) + +struct rfcomm_pinfo { + struct rfcomm_dlc *dlc; + u8 channel; +}; + +int rfcomm_init_sockets(void); +void rfcomm_cleanup_sockets(void); + +int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d); + +/* ---- RFCOMM TTY ---- */ +#define RFCOMM_MAX_DEV 256 + +#define RFCOMMCREATEDEV _IOW('R', 200, int) +#define RFCOMMRELEASEDEV _IOW('R', 201, int) +#define RFCOMMGETDEVLIST _IOR('R', 210, int) +#define RFCOMMGETDEVINFO _IOR('R', 211, int) +#define RFCOMMSTEALDLC _IOW('R', 220, int) + +#define RFCOMM_REUSE_DLC 0 +#define RFCOMM_RELEASE_ONHUP 1 +#define RFCOMM_HANGUP_NOW 2 +#define RFCOMM_TTY_ATTACHED 3 + +struct rfcomm_dev_req { + s16 dev_id; + u32 flags; + bdaddr_t src; + bdaddr_t dst; + u8 channel; + +}; + +struct rfcomm_dev_info { + s16 id; + u32 flags; + u16 state; + bdaddr_t src; + bdaddr_t dst; + u8 channel; +}; + +struct rfcomm_dev_list_req { + u16 dev_num; + struct rfcomm_dev_info dev_info[0]; +}; + +int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg); +int rfcomm_init_ttys(void); +void rfcomm_cleanup_ttys(void); + +#endif /* __RFCOMM_H */ diff --git a/net/bluetooth/Config.help b/net/bluetooth/Config.help index a3aa13f2c958..9bb18e720a7a 100644 --- a/net/bluetooth/Config.help +++ b/net/bluetooth/Config.help @@ -10,7 +10,9 @@ CONFIG_BLUEZ BlueZ Core (HCI device and connection manager, scheduler) HCI Device drivers (interface to the hardware) L2CAP Module (L2CAP protocol) + RFCOMM Module (RFCOMM protocol) SCO Module (SCO links) + BNEP Module (BNEP protocol) Say Y here to enable Linux Bluetooth support and to build BlueZ Core layer. @@ -31,6 +33,19 @@ CONFIG_BLUEZ_L2CAP Say Y here to compile L2CAP support into the kernel or say M to compile it as module (l2cap.o). +RFCOMM protocol support +CONFIG_BLUEZ_RFCOMM + RFCOMM provides connection oriented stream transport. RFCOMM + support is required for Dialup Networking, OBEX and other Bluetooth + applications. + + Say Y here to compile RFCOMM support into the kernel or say M to + compile it as module (rfcomm.o). + +RFCOMM TTY emulation support +CONFIG_RFCOMM_TTY + This options enables TTY emulation support for RFCOMM channels. + SCO links support CONFIG_BLUEZ_SCO SCO link provides voice transport over Bluetooth. SCO support is diff --git a/net/bluetooth/Config.in b/net/bluetooth/Config.in index 5a306aa1bb6b..ddf740202ca0 100644 --- a/net/bluetooth/Config.in +++ b/net/bluetooth/Config.in @@ -10,6 +10,7 @@ if [ "$CONFIG_NET" != "n" ]; then if [ "$CONFIG_BLUEZ" != "n" ]; then dep_tristate 'L2CAP protocol support' CONFIG_BLUEZ_L2CAP $CONFIG_BLUEZ dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ + source net/bluetooth/rfcomm/Config.in source net/bluetooth/bnep/Config.in source drivers/bluetooth/Config.in fi diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 04396a732f75..f769086251ca 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -14,6 +14,12 @@ ifeq ($(CONFIG_BLUEZ_BNEP),y) obj-y += bnep/bnep.o endif +subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm + +ifeq ($(CONFIG_BLUEZ_RFCOMM),y) +obj-y += rfcomm/rfcomm.o +endif + bluez-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o include $(TOPDIR)/Rules.make diff --git a/net/bluetooth/rfcomm/Config.in b/net/bluetooth/rfcomm/Config.in new file mode 100644 index 000000000000..680f9693d33f --- /dev/null +++ b/net/bluetooth/rfcomm/Config.in @@ -0,0 +1,5 @@ + +dep_tristate 'RFCOMM protocol support' CONFIG_BLUEZ_RFCOMM $CONFIG_BLUEZ_L2CAP +if [ "$CONFIG_BLUEZ_RFCOMM" != "n" ]; then + bool ' RFCOMM TTY support' CONFIG_RFCOMM_TTY +fi diff --git a/net/bluetooth/rfcomm/Makefile b/net/bluetooth/rfcomm/Makefile new file mode 100644 index 000000000000..b0e245508970 --- /dev/null +++ b/net/bluetooth/rfcomm/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for BNEP protocol +# + +O_TARGET := rfcomm.o + +obj-y := core.o sock.o crc.o +obj-$(CONFIG_RFCOMM_TTY) += tty.o +obj-m += $(O_TARGET) + +include $(TOPDIR)/Rules.make diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c new file mode 100644 index 000000000000..de3dccc504c8 --- /dev/null +++ b/net/bluetooth/rfcomm/core.c @@ -0,0 +1,1711 @@ +/* + RFCOMM implementation for Linux Bluetooth stack (BlueZ). + Copyright (C) 2002 Maxim Krasnyansky + Copyright (C) 2002 Marcel Holtmann + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +/* + RPN support - Dirk Husemann +*/ + +/* + * RFCOMM core. + * + * $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $ + */ + +#define __KERNEL_SYSCALLS__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define VERSION "0.3" + +#ifndef CONFIG_RFCOMM_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +struct task_struct *rfcomm_thread; +DECLARE_MUTEX(rfcomm_sem); +unsigned long rfcomm_event; + +static LIST_HEAD(session_list); +static atomic_t terminate, running; + +static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len); +static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci); +static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci); +static int rfcomm_queue_disc(struct rfcomm_dlc *d); +static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type); +static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d); +static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig); +static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len); +static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits); +static void rfcomm_make_uih(struct sk_buff *skb, u8 addr); + +static void rfcomm_process_connect(struct rfcomm_session *s); + +/* ---- RFCOMM frame parsing macros ---- */ +#define __get_dlci(b) ((b & 0xfc) >> 2) +#define __get_channel(b) ((b & 0xf8) >> 3) +#define __get_dir(b) ((b & 0x04) >> 2) +#define __get_type(b) ((b & 0xef)) + +#define __test_ea(b) ((b & 0x01)) +#define __test_cr(b) ((b & 0x02)) +#define __test_pf(b) ((b & 0x10)) + +#define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01) +#define __ctrl(type, pf) (((type & 0xef) | (pf << 4))) +#define __dlci(dir, chn) (((chn & 0x1f) << 1) | dir) +#define __srv_channel(dlci) (dlci >> 1) +#define __dir(dlci) (dlci & 0x01) + +#define __len8(len) (((len) << 1) | 1) +#define __len16(len) ((len) << 1) + +/* MCC macros */ +#define __mcc_type(cr, type) (((type << 2) | (cr << 1) | 0x01)) +#define __get_mcc_type(b) ((b & 0xfc) >> 2) +#define __get_mcc_len(b) ((b & 0xfe) >> 1) + +/* RPN macros */ +#define __rpn_line_settings(data, stop, parity) ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x3) << 3)) +#define __get_rpn_data_bits(line) ((line) & 0x3) +#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1) +#define __get_rpn_parity(line) (((line) >> 3) & 0x3) + +/* ---- RFCOMM FCS computation ---- */ + +/* CRC on 2 bytes */ +#define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]]) + +/* FCS on 2 bytes */ +static inline u8 __fcs(u8 *data) +{ + return (0xff - __crc(data)); +} + +/* FCS on 3 bytes */ +static inline u8 __fcs2(u8 *data) +{ + return (0xff - rfcomm_crc_table[__crc(data) ^ data[2]]); +} + +/* Check FCS */ +static inline int __check_fcs(u8 *data, int type, u8 fcs) +{ + u8 f = __crc(data); + + if (type != RFCOMM_UIH) + f = rfcomm_crc_table[f ^ data[2]]; + + return rfcomm_crc_table[f ^ fcs] != 0xcf; +} + +/* ---- L2CAP callbacks ---- */ +static void rfcomm_l2state_change(struct sock *sk) +{ + BT_DBG("%p state %d", sk, sk->state); + rfcomm_schedule(RFCOMM_SCHED_STATE); +} + +static void rfcomm_l2data_ready(struct sock *sk, int bytes) +{ + BT_DBG("%p bytes %d", sk, bytes); + rfcomm_schedule(RFCOMM_SCHED_RX); +} + +static int rfcomm_l2sock_create(struct socket **sock) +{ + int err; + + BT_DBG(""); + + err = sock_create(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock); + if (!err) { + struct sock *sk = (*sock)->sk; + sk->data_ready = rfcomm_l2data_ready; + sk->state_change = rfcomm_l2state_change; + } + return err; +} + +/* ---- RFCOMM DLCs ---- */ +static void rfcomm_dlc_timeout(unsigned long arg) +{ + struct rfcomm_dlc *d = (void *) arg; + + BT_DBG("dlc %p state %ld", d, d->state); + + set_bit(RFCOMM_TIMED_OUT, &d->flags); + rfcomm_dlc_put(d); + rfcomm_schedule(RFCOMM_SCHED_TIMEO); +} + +static void rfcomm_dlc_set_timer(struct rfcomm_dlc *d, long timeout) +{ + BT_DBG("dlc %p state %ld timeout %ld", d, d->state, timeout); + + if (!mod_timer(&d->timer, jiffies + timeout)) + rfcomm_dlc_hold(d); +} + +static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d) +{ + BT_DBG("dlc %p state %ld", d, d->state); + + if (timer_pending(&d->timer) && del_timer(&d->timer)) + rfcomm_dlc_put(d); +} + +static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d) +{ + BT_DBG("%p", d); + + d->state = BT_OPEN; + d->flags = 0; + d->mtu = RFCOMM_DEFAULT_MTU; + d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV; + + d->credits = RFCOMM_MAX_CREDITS; + d->rx_credits = RFCOMM_DEFAULT_CREDITS; +} + +struct rfcomm_dlc *rfcomm_dlc_alloc(int prio) +{ + struct rfcomm_dlc *d = kmalloc(sizeof(*d), prio); + if (!d) + return NULL; + memset(d, 0, sizeof(*d)); + + init_timer(&d->timer); + d->timer.function = rfcomm_dlc_timeout; + d->timer.data = (unsigned long) d; + + skb_queue_head_init(&d->tx_queue); + spin_lock_init(&d->lock); + atomic_set(&d->refcnt, 1); + + rfcomm_dlc_clear_state(d); + + BT_DBG("%p", d); + return d; +} + +void rfcomm_dlc_free(struct rfcomm_dlc *d) +{ + BT_DBG("%p", d); + + skb_queue_purge(&d->tx_queue); + kfree(d); +} + +static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d) +{ + BT_DBG("dlc %p session %p", d, s); + + rfcomm_session_hold(s); + + rfcomm_dlc_hold(d); + list_add(&d->list, &s->dlcs); + d->session = s; +} + +static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) +{ + struct rfcomm_session *s = d->session; + + BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s); + + list_del(&d->list); + d->session = NULL; + rfcomm_dlc_put(d); + + rfcomm_session_put(s); +} + +static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, int dlci) +{ + struct rfcomm_dlc *d; + struct list_head *p; + + list_for_each(p, &s->dlcs) { + d = list_entry(p, struct rfcomm_dlc, list); + if (d->dlci == dlci) + return d; + } + return NULL; +} + +static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) +{ + struct rfcomm_session *s; + int err = 0, dlci = __dlci(0, channel); + + BT_DBG("dlc %p state %ld %s %s channel %d dlci %d", + d, d->state, batostr(src), batostr(dst), channel, dlci); + + if (dlci < 1 || dlci > 62) + return -EINVAL; + + if (d->state != BT_OPEN && d->state != BT_CLOSED) + return 0; + + s = rfcomm_session_get(src, dst); + if (!s) { + s = rfcomm_session_create(src, dst, &err); + if (!s) + return err; + } + + /* Check if DLCI already exists */ + if (rfcomm_dlc_get(s, dlci)) + return -EBUSY; + + rfcomm_dlc_clear_state(d); + + d->dlci = dlci; + d->addr = __addr(s->initiator, dlci); + + d->state = BT_CONFIG; + rfcomm_dlc_link(s, d); + + d->mtu = s->mtu; + d->credits = s->credits; + + if (s->state == BT_CONNECTED) + rfcomm_send_pn(s, 1, d); + rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); + return 0; +} + +int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) +{ + mm_segment_t fs; + int r; + + rfcomm_lock(); + + fs = get_fs(); set_fs(KERNEL_DS); + r = __rfcomm_dlc_open(d, src, dst, channel); + set_fs(fs); + + rfcomm_unlock(); + return r; +} + +static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) +{ + struct rfcomm_session *s = d->session; + if (!s) + return 0; + + BT_DBG("dlc %p state %ld dlci %d err %d session %p", + d, d->state, d->dlci, err, s); + + switch (d->state) { + case BT_CONNECTED: + case BT_CONFIG: + case BT_CONNECT: + d->state = BT_DISCONN; + if (skb_queue_empty(&d->tx_queue)) { + rfcomm_send_disc(s, d->dlci); + rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT); + } else { + rfcomm_queue_disc(d); + rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2); + } + break; + + default: + rfcomm_dlc_clear_timer(d); + + rfcomm_dlc_lock(d); + d->state = BT_CLOSED; + d->state_change(d, err); + rfcomm_dlc_unlock(d); + + skb_queue_purge(&d->tx_queue); + rfcomm_dlc_unlink(d); + } + + return 0; +} + +int rfcomm_dlc_close(struct rfcomm_dlc *d, int err) +{ + mm_segment_t fs; + int r; + + rfcomm_lock(); + + fs = get_fs(); set_fs(KERNEL_DS); + r = __rfcomm_dlc_close(d, err); + set_fs(fs); + + rfcomm_unlock(); + return r; +} + +int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb) +{ + int len = skb->len; + + if (d->state != BT_CONNECTED) + return -ENOTCONN; + + BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len); + + if (len > d->mtu) + return -EINVAL; + + rfcomm_make_uih(skb, d->addr); + skb_queue_tail(&d->tx_queue, skb); + + if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags)) + rfcomm_schedule(RFCOMM_SCHED_TX); + return len; +} + +void __rfcomm_dlc_throttle(struct rfcomm_dlc *d) +{ + BT_DBG("dlc %p state %ld", d, d->state); + + if (!d->credits) { + d->v24_sig |= RFCOMM_V24_FC; + set_bit(RFCOMM_MSC_PENDING, &d->flags); + } + rfcomm_schedule(RFCOMM_SCHED_TX); +} + +void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) +{ + BT_DBG("dlc %p state %ld", d, d->state); + + if (!d->credits) { + d->v24_sig &= ~RFCOMM_V24_FC; + set_bit(RFCOMM_MSC_PENDING, &d->flags); + } + rfcomm_schedule(RFCOMM_SCHED_TX); +} + +int rfcomm_dlc_modem_status(struct rfcomm_dlc *d, int v24_sig) +{ + BT_DBG("dlc %p state %ld v24_sig 0x%x", + d, d->state, v24_sig); + + if (test_bit(RFCOMM_RX_THROTTLED, &d->flags)) + v24_sig |= RFCOMM_V24_FC; + else + v24_sig &= ~RFCOMM_V24_FC; + + d->v24_sig = v24_sig; + + if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags)) + rfcomm_schedule(RFCOMM_SCHED_TX); + + return 0; +} + +/* ---- RFCOMM sessions ---- */ +struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) +{ + struct rfcomm_session *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return NULL; + memset(s, 0, sizeof(*s)); + + BT_DBG("session %p sock %p", s, sock); + + INIT_LIST_HEAD(&s->dlcs); + s->state = state; + s->sock = sock; + + s->mtu = RFCOMM_DEFAULT_MTU; + s->credits = RFCOMM_MAX_CREDITS; + + list_add(&s->list, &session_list); + + /* Do not increment module usage count for listeting sessions. + * Otherwise we won't be able to unload the module. */ + if (state != BT_LISTEN) + MOD_INC_USE_COUNT; + return s; +} + +void rfcomm_session_del(struct rfcomm_session *s) +{ + int state = s->state; + + BT_DBG("session %p state %ld", s, s->state); + + list_del(&s->list); + + if (state == BT_CONNECTED) + rfcomm_send_disc(s, 0); + + sock_release(s->sock); + kfree(s); + + if (state != BT_LISTEN) + MOD_DEC_USE_COUNT; +} + +struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) +{ + struct rfcomm_session *s; + struct list_head *p, *n; + struct bluez_sock *sk; + list_for_each_safe(p, n, &session_list) { + s = list_entry(p, struct rfcomm_session, list); + sk = bluez_sk(s->sock->sk); + + if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) && + !bacmp(&sk->dst, dst)) + return s; + } + return NULL; +} + +void rfcomm_session_close(struct rfcomm_session *s, int err) +{ + struct rfcomm_dlc *d; + struct list_head *p, *n; + + BT_DBG("session %p state %ld err %d", s, s->state, err); + + rfcomm_session_hold(s); + + s->state = BT_CLOSED; + + /* Close all dlcs */ + list_for_each_safe(p, n, &s->dlcs) { + d = list_entry(p, struct rfcomm_dlc, list); + d->state = BT_CLOSED; + __rfcomm_dlc_close(d, err); + } + + rfcomm_session_put(s); +} + +struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err) +{ + struct rfcomm_session *s = NULL; + struct sockaddr_l2 addr; + struct l2cap_options opts; + struct socket *sock; + int size; + + BT_DBG("%s %s", batostr(src), batostr(dst)); + + *err = rfcomm_l2sock_create(&sock); + if (*err < 0) + return NULL; + + bacpy(&addr.l2_bdaddr, src); + addr.l2_family = AF_BLUETOOTH; + addr.l2_psm = 0; + *err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr)); + if (*err < 0) + goto failed; + + /* Set L2CAP options */ + size = sizeof(opts); + sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size); + + opts.imtu = RFCOMM_MAX_L2CAP_MTU; + sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size); + + s = rfcomm_session_add(sock, BT_BOUND); + if (!s) { + *err = -ENOMEM; + goto failed; + } + + s->initiator = 1; + + bacpy(&addr.l2_bdaddr, dst); + addr.l2_family = AF_BLUETOOTH; + addr.l2_psm = htobs(RFCOMM_PSM); + *err = sock->ops->connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK); + if (*err == 0 || *err == -EAGAIN) + return s; + + rfcomm_session_del(s); + return NULL; + +failed: + sock_release(sock); + return NULL; +} + +/* ---- RFCOMM frame sending ---- */ +static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len) +{ + struct socket *sock = s->sock; + struct iovec iv = { data, len }; + struct msghdr msg; + int err; + + BT_DBG("session %p len %d", s, len); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iovlen = 1; + msg.msg_iov = &iv; + + err = sock->ops->sendmsg(sock, &msg, len, 0); + return err; +} + +static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci) +{ + struct rfcomm_cmd cmd; + + BT_DBG("%p dlci %d", s, dlci); + + cmd.addr = __addr(s->initiator, dlci); + cmd.ctrl = __ctrl(RFCOMM_SABM, 1); + cmd.len = __len8(0); + cmd.fcs = __fcs2((u8 *) &cmd); + + return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); +} + +static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci) +{ + struct rfcomm_cmd cmd; + + BT_DBG("%p dlci %d", s, dlci); + + cmd.addr = __addr(!s->initiator, dlci); + cmd.ctrl = __ctrl(RFCOMM_UA, 1); + cmd.len = __len8(0); + cmd.fcs = __fcs2((u8 *) &cmd); + + return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); +} + +static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci) +{ + struct rfcomm_cmd cmd; + + BT_DBG("%p dlci %d", s, dlci); + + cmd.addr = __addr(s->initiator, dlci); + cmd.ctrl = __ctrl(RFCOMM_DISC, 1); + cmd.len = __len8(0); + cmd.fcs = __fcs2((u8 *) &cmd); + + return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); +} + +static int rfcomm_queue_disc(struct rfcomm_dlc *d) +{ + struct rfcomm_cmd *cmd; + struct sk_buff *skb; + + BT_DBG("dlc %p dlci %d", d, d->dlci); + + skb = alloc_skb(sizeof(*cmd), GFP_KERNEL); + if (!skb) + return -ENOMEM; + + cmd = (void *) __skb_put(skb, sizeof(*cmd)); + cmd->addr = d->addr; + cmd->ctrl = __ctrl(RFCOMM_DISC, 1); + cmd->len = __len8(0); + cmd->fcs = __fcs2((u8 *) cmd); + + skb_queue_tail(&d->tx_queue, skb); + rfcomm_schedule(RFCOMM_SCHED_TX); + return 0; +} + +static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci) +{ + struct rfcomm_cmd cmd; + + BT_DBG("%p dlci %d", s, dlci); + + cmd.addr = __addr(!s->initiator, dlci); + cmd.ctrl = __ctrl(RFCOMM_DM, 1); + cmd.len = __len8(0); + cmd.fcs = __fcs2((u8 *) &cmd); + + return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); +} + +static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) +{ + struct rfcomm_hdr *hdr; + struct rfcomm_mcc *mcc; + u8 buf[16], *ptr = buf; + + BT_DBG("%p cr %d type %d", s, cr, type); + + hdr = (void *) ptr; ptr += sizeof(*hdr); + hdr->addr = __addr(s->initiator, 0); + hdr->ctrl = __ctrl(RFCOMM_UIH, 0); + hdr->len = __len8(sizeof(*mcc) + 1); + + mcc = (void *) ptr; ptr += sizeof(*mcc); + mcc->type = __mcc_type(s->initiator, RFCOMM_NSC); + mcc->len = __len8(1); + + /* Type that we didn't like */ + *ptr = __mcc_type(cr, type); ptr++; + + *ptr = __fcs(buf); ptr++; + + return rfcomm_send_frame(s, buf, ptr - buf); +} + +static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d) +{ + struct rfcomm_hdr *hdr; + struct rfcomm_mcc *mcc; + struct rfcomm_pn *pn; + u8 buf[16], *ptr = buf; + + BT_DBG("%p cr %d dlci %d mtu %d", s, cr, d->dlci, d->mtu); + + hdr = (void *) ptr; ptr += sizeof(*hdr); + hdr->addr = __addr(s->initiator, 0); + hdr->ctrl = __ctrl(RFCOMM_UIH, 0); + hdr->len = __len8(sizeof(*mcc) + sizeof(*pn)); + + mcc = (void *) ptr; ptr += sizeof(*mcc); + mcc->type = __mcc_type(s->initiator, RFCOMM_PN); + mcc->len = __len8(sizeof(*pn)); + + pn = (void *) ptr; ptr += sizeof(*pn); + pn->dlci = d->dlci; + pn->priority = 0; + pn->ack_timer = 0; + pn->max_retrans = 0; + + if (d->credits) { + pn->flow_ctrl = cr ? 0xf0 : 0xe0; + pn->credits = RFCOMM_DEFAULT_CREDITS; + } else { + pn->flow_ctrl = 0; + pn->credits = 0; + } + + pn->mtu = htobs(d->mtu); + + *ptr = __fcs(buf); ptr++; + + return rfcomm_send_frame(s, buf, ptr - buf); +} + +static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, + u8 bit_rate, u8 data_bits, u8 stop_bits, + u8 parity, u8 flow_ctrl_settings, + u8 xon_char, u8 xoff_char, u16 param_mask) +{ + struct rfcomm_hdr *hdr; + struct rfcomm_mcc *mcc; + struct rfcomm_rpn *rpn; + u8 buf[16], *ptr = buf; + + BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x" + "flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x", + s, cr, dlci, bit_rate, data_bits, stop_bits, parity, + flow_ctrl_settings, xon_char, xoff_char, param_mask); + + hdr = (void *) ptr; ptr += sizeof(*hdr); + hdr->addr = __addr(s->initiator, 0); + hdr->ctrl = __ctrl(RFCOMM_UIH, 0); + hdr->len = __len8(sizeof(*mcc) + sizeof(*rpn)); + + mcc = (void *) ptr; ptr += sizeof(*mcc); + mcc->type = __mcc_type(cr, RFCOMM_RPN); + mcc->len = __len8(sizeof(*rpn)); + + rpn = (void *) ptr; ptr += sizeof(*rpn); + rpn->dlci = __addr(1, dlci); + rpn->bit_rate = bit_rate; + rpn->line_settings = __rpn_line_settings(data_bits, stop_bits, parity); + rpn->flow_ctrl = flow_ctrl_settings; + rpn->xon_char = xon_char; + rpn->xoff_char = xoff_char; + rpn->param_mask = param_mask; + + *ptr = __fcs(buf); ptr++; + + return rfcomm_send_frame(s, buf, ptr - buf); +} + +static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig) +{ + struct rfcomm_hdr *hdr; + struct rfcomm_mcc *mcc; + struct rfcomm_msc *msc; + u8 buf[16], *ptr = buf; + + BT_DBG("%p cr %d v24 0x%x", s, cr, v24_sig); + + hdr = (void *) ptr; ptr += sizeof(*hdr); + hdr->addr = __addr(s->initiator, 0); + hdr->ctrl = __ctrl(RFCOMM_UIH, 0); + hdr->len = __len8(sizeof(*mcc) + sizeof(*msc)); + + mcc = (void *) ptr; ptr += sizeof(*mcc); + mcc->type = __mcc_type(cr, RFCOMM_MSC); + mcc->len = __len8(sizeof(*msc)); + + msc = (void *) ptr; ptr += sizeof(*msc); + msc->dlci = __addr(1, dlci); + msc->v24_sig = v24_sig; + + *ptr = __fcs(buf); ptr++; + + return rfcomm_send_frame(s, buf, ptr - buf); +} + +static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len) +{ + struct socket *sock = s->sock; + struct iovec iv[3]; + struct msghdr msg; + unsigned char hdr[5], crc[1]; + + if (len > 125) + return -EINVAL; + + BT_DBG("%p cr %d", s, cr); + + hdr[0] = __addr(s->initiator, 0); + hdr[1] = __ctrl(RFCOMM_UIH, 0); + hdr[2] = 0x01 | ((len + 2) << 1); + hdr[3] = 0x01 | ((cr & 0x01) << 1) | (RFCOMM_TEST << 2); + hdr[4] = 0x01 | (len << 1); + + crc[0] = __fcs(hdr); + + iv[0].iov_base = hdr; + iv[0].iov_len = 5; + iv[1].iov_base = pattern; + iv[1].iov_len = len; + iv[2].iov_base = crc; + iv[2].iov_len = 1; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iovlen = 3; + msg.msg_iov = iv; + return sock->ops->sendmsg(sock, &msg, 6 + len, 0); +} + +static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits) +{ + struct rfcomm_hdr *hdr; + u8 buf[16], *ptr = buf; + + BT_DBG("%p addr %d credits %d", s, addr, credits); + + hdr = (void *) ptr; ptr += sizeof(*hdr); + hdr->addr = addr; + hdr->ctrl = __ctrl(RFCOMM_UIH, 1); + hdr->len = __len8(0); + + *ptr = credits; ptr++; + + *ptr = __fcs(buf); ptr++; + + return rfcomm_send_frame(s, buf, ptr - buf); +} + +static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) +{ + struct rfcomm_hdr *hdr; + int len = skb->len; + u8 *crc; + + if (len > 127) { + hdr = (void *) skb_push(skb, 4); + put_unaligned(htobs(__len16(len)), (u16 *) &hdr->len); + } else { + hdr = (void *) skb_push(skb, 3); + hdr->len = __len8(len); + } + hdr->addr = addr; + hdr->ctrl = __ctrl(RFCOMM_UIH, 0); + + crc = skb_put(skb, 1); + *crc = __fcs((void *) hdr); +} + +/* ---- RFCOMM frame reception ---- */ +static int rfcomm_recv_ua(struct rfcomm_session *s, int dlci) +{ + BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); + + if (dlci) { + /* Data channel */ + struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); + if (!d) { + rfcomm_send_dm(s, dlci); + return 0; + } + + switch (d->state) { + case BT_CONNECT: + rfcomm_dlc_clear_timer(d); + + rfcomm_dlc_lock(d); + d->state = BT_CONNECTED; + d->state_change(d, 0); + rfcomm_dlc_unlock(d); + + rfcomm_send_msc(s, 1, dlci, d->v24_sig); + break; + + case BT_DISCONN: + d->state = BT_CLOSED; + __rfcomm_dlc_close(d, 0); + break; + } + } else { + /* Control channel */ + switch (s->state) { + case BT_CONNECT: + s->state = BT_CONNECTED; + rfcomm_process_connect(s); + break; + } + } + return 0; +} + +static int rfcomm_recv_dm(struct rfcomm_session *s, int dlci) +{ + int err = 0; + + BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); + + if (dlci) { + /* Data DLC */ + struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); + if (d) { + if (d->state == BT_CONNECT || d->state == BT_CONFIG) + err = ECONNREFUSED; + else + err = ECONNRESET; + + d->state = BT_CLOSED; + __rfcomm_dlc_close(d, err); + } + } else { + if (s->state == BT_CONNECT) + err = ECONNREFUSED; + else + err = ECONNRESET; + + s->state = BT_CLOSED; + rfcomm_session_close(s, err); + } + return 0; +} + +static int rfcomm_recv_disc(struct rfcomm_session *s, int dlci) +{ + int err = 0; + + BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); + + if (dlci) { + struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); + if (d) { + rfcomm_send_ua(s, dlci); + + if (d->state == BT_CONNECT || d->state == BT_CONFIG) + err = ECONNREFUSED; + else + err = ECONNRESET; + + d->state = BT_CLOSED; + __rfcomm_dlc_close(d, err); + } else + rfcomm_send_dm(s, dlci); + + } else { + rfcomm_send_ua(s, 0); + + if (s->state == BT_CONNECT) + err = ECONNREFUSED; + else + err = ECONNRESET; + + s->state = BT_CLOSED; + rfcomm_session_close(s, err); + } + + return 0; +} + +static int rfcomm_recv_sabm(struct rfcomm_session *s, int dlci) +{ + struct rfcomm_dlc *d; + int channel; + + BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); + + if (!dlci) { + rfcomm_send_ua(s, 0); + + if (s->state == BT_OPEN) { + s->state = BT_CONNECTED; + rfcomm_process_connect(s); + } + return 0; + } + + /* Check if DLC exists */ + d = rfcomm_dlc_get(s, dlci); + if (d) { + if (d->state == BT_OPEN) { + /* DLC was previously opened by PN request */ + rfcomm_send_ua(s, dlci); + + rfcomm_dlc_lock(d); + d->state = BT_CONNECTED; + d->state_change(d, 0); + rfcomm_dlc_unlock(d); + + rfcomm_send_msc(s, 1, dlci, d->v24_sig); + } + return 0; + } + + /* Notify socket layer about incomming connection */ + channel = __srv_channel(dlci); + if (rfcomm_connect_ind(s, channel, &d)) { + d->dlci = dlci; + d->addr = __addr(s->initiator, dlci); + rfcomm_dlc_link(s, d); + + rfcomm_send_ua(s, dlci); + + rfcomm_dlc_lock(d); + d->state = BT_CONNECTED; + d->state_change(d, 0); + rfcomm_dlc_unlock(d); + } else { + rfcomm_send_dm(s, dlci); + } + + return 0; +} + +static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn) +{ + BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d", + d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits); + + if (cr) { + if (pn->flow_ctrl == 0xf0) { + d->tx_credits = pn->credits; + } else { + set_bit(RFCOMM_TX_THROTTLED, &d->flags); + d->credits = 0; + } + + d->mtu = btohs(pn->mtu); + } else { + if (pn->flow_ctrl == 0xe0) { + d->tx_credits = pn->credits; + } else { + set_bit(RFCOMM_TX_THROTTLED, &d->flags); + d->credits = 0; + } + + d->mtu = btohs(pn->mtu); + } + + return 0; +} + +static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb) +{ + struct rfcomm_pn *pn = (void *) skb->data; + struct rfcomm_dlc *d; + int dlci = pn->dlci; + + BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); + + if (!dlci) + return 0; + + d = rfcomm_dlc_get(s, dlci); + if (d) { + if (cr) { + /* PN request */ + rfcomm_apply_pn(d, cr, pn); + rfcomm_send_pn(s, 0, d); + } else { + /* PN response */ + switch (d->state) { + case BT_CONFIG: + rfcomm_apply_pn(d, cr, pn); + + d->state = BT_CONNECT; + rfcomm_send_sabm(s, d->dlci); + break; + } + } + } else { + int channel = __srv_channel(dlci); + + if (!cr) + return 0; + + /* PN request for non existing DLC. + * Assume incomming connection. */ + if (rfcomm_connect_ind(s, channel, &d)) { + d->dlci = dlci; + d->addr = __addr(s->initiator, dlci); + rfcomm_dlc_link(s, d); + + rfcomm_apply_pn(d, cr, pn); + + d->state = BT_OPEN; + rfcomm_send_pn(s, 0, d); + } else { + rfcomm_send_dm(s, dlci); + } + } + return 0; +} + +static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb) +{ + struct rfcomm_rpn *rpn = (void *) skb->data; + int dlci = __get_dlci(rpn->dlci); + + u8 bit_rate = 0; + u8 data_bits = 0; + u8 stop_bits = 0; + u8 parity = 0; + u8 flow_ctrl = 0; + u8 xon_char = 0; + u8 xoff_char = 0; + u16 rpn_mask = RFCOMM_RPN_PM_ALL; + + BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", + dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl, + rpn->xon_char, rpn->xoff_char, rpn->param_mask); + + if (!cr) + return 0; + + if (len == 1) { + /* request: return default setting */ + bit_rate = RFCOMM_RPN_BR_115200; + data_bits = RFCOMM_RPN_DATA_8; + stop_bits = RFCOMM_RPN_STOP_1; + parity = RFCOMM_RPN_PARITY_NONE; + flow_ctrl = RFCOMM_RPN_FLOW_NONE; + xon_char = RFCOMM_RPN_XON_CHAR; + xoff_char = RFCOMM_RPN_XOFF_CHAR; + + goto rpn_out; + } + /* check for sane values: ignore/accept bit_rate, 8 bits, 1 stop bit, no parity, + no flow control lines, normal XON/XOFF chars */ + if (rpn->param_mask & RFCOMM_RPN_PM_DATA) { + data_bits = __get_rpn_data_bits(rpn->line_settings); + if (data_bits != RFCOMM_RPN_DATA_8) { + BT_DBG("RPN data bits mismatch 0x%x", data_bits); + data_bits = RFCOMM_RPN_DATA_8; + rpn_mask ^= RFCOMM_RPN_PM_DATA; + } + } + if (rpn->param_mask & RFCOMM_RPN_PM_STOP) { + stop_bits = __get_rpn_stop_bits(rpn->line_settings); + if (stop_bits != RFCOMM_RPN_STOP_1) { + BT_DBG("RPN stop bits mismatch 0x%x", stop_bits); + stop_bits = RFCOMM_RPN_STOP_1; + rpn_mask ^= RFCOMM_RPN_PM_STOP; + } + } + if (rpn->param_mask & RFCOMM_RPN_PM_PARITY) { + parity = __get_rpn_parity(rpn->line_settings); + if (parity != RFCOMM_RPN_PARITY_NONE) { + BT_DBG("RPN parity mismatch 0x%x", parity); + parity = RFCOMM_RPN_PARITY_NONE; + rpn_mask ^= RFCOMM_RPN_PM_PARITY; + } + } + if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) { + if (rpn->flow_ctrl != RFCOMM_RPN_FLOW_NONE) { + BT_DBG("RPN flow ctrl mismatch 0x%x", rpn->flow_ctrl); + rpn->flow_ctrl = RFCOMM_RPN_FLOW_NONE; + rpn_mask ^= RFCOMM_RPN_PM_FLOW; + } + } + if (rpn->param_mask & RFCOMM_RPN_PM_XON) { + if (rpn->xon_char != RFCOMM_RPN_XON_CHAR) { + BT_DBG("RPN XON char mismatch 0x%x", rpn->xon_char); + rpn->xon_char = RFCOMM_RPN_XON_CHAR; + rpn_mask ^= RFCOMM_RPN_PM_XON; + } + } + if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) { + if (rpn->xoff_char != RFCOMM_RPN_XOFF_CHAR) { + BT_DBG("RPN XOFF char mismatch 0x%x", rpn->xoff_char); + rpn->xoff_char = RFCOMM_RPN_XOFF_CHAR; + rpn_mask ^= RFCOMM_RPN_PM_XOFF; + } + } + +rpn_out: + rfcomm_send_rpn(s, 0, dlci, + bit_rate, data_bits, stop_bits, parity, flow_ctrl, + xon_char, xoff_char, rpn_mask); + + return 0; +} + +static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb) +{ + struct rfcomm_msc *msc = (void *) skb->data; + struct rfcomm_dlc *d; + int dlci = __get_dlci(msc->dlci); + + BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig); + + if (!cr) + return 0; + + d = rfcomm_dlc_get(s, dlci); + if (d) { + if (msc->v24_sig & RFCOMM_V24_FC && !d->credits) + set_bit(RFCOMM_TX_THROTTLED, &d->flags); + else + clear_bit(RFCOMM_TX_THROTTLED, &d->flags); + + rfcomm_dlc_lock(d); + if (d->modem_status) + d->modem_status(d, msc->v24_sig); + rfcomm_dlc_unlock(d); + + rfcomm_send_msc(s, 0, dlci, msc->v24_sig); + } + return 0; +} + +static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb) +{ + struct rfcomm_mcc *mcc = (void *) skb->data; + u8 type, cr, len; + + cr = __test_cr(mcc->type); + type = __get_mcc_type(mcc->type); + len = __get_mcc_len(mcc->len); + + BT_DBG("%p type 0x%x cr %d", s, type, cr); + + skb_pull(skb, 2); + + switch (type) { + case RFCOMM_PN: + rfcomm_recv_pn(s, cr, skb); + break; + + case RFCOMM_RPN: + rfcomm_recv_rpn(s, cr, len, skb); + break; + + case RFCOMM_MSC: + rfcomm_recv_msc(s, cr, skb); + break; + + case RFCOMM_TEST: + if (cr) + rfcomm_send_test(s, 0, skb->data, skb->len); + break; + + default: + BT_ERR("Unknown control type 0x%02x", type); + rfcomm_send_nsc(s, cr, type); + break; + } + return 0; +} + +static int rfcomm_recv_data(struct rfcomm_session *s, int dlci, int pf, struct sk_buff *skb) +{ + struct rfcomm_dlc *d; + + BT_DBG("session %p state %ld dlci %d pf %d", s, s->state, dlci, pf); + + d = rfcomm_dlc_get(s, dlci); + if (!d) { + rfcomm_send_dm(s, dlci); + goto drop; + } + + if (pf && d->credits) { + u8 credits = *(u8 *) skb->data; skb_pull(skb, 1); + + d->tx_credits += credits; + if (d->tx_credits) + clear_bit(RFCOMM_TX_THROTTLED, &d->flags); + } + + if (skb->len && d->state == BT_CONNECTED) { + rfcomm_dlc_lock(d); + d->rx_credits--; + d->data_ready(d, skb); + rfcomm_dlc_unlock(d); + return 0; + } + +drop: + kfree_skb(skb); + return 0; +} + +static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) +{ + struct rfcomm_hdr *hdr = (void *) skb->data; + u8 type, dlci, fcs; + + dlci = __get_dlci(hdr->addr); + type = __get_type(hdr->ctrl); + + /* Trim FCS */ + skb->len--; skb->tail--; + fcs = *(u8 *) skb->tail; + + if (__check_fcs(skb->data, type, fcs)) { + BT_ERR("bad checksum in packet"); + kfree_skb(skb); + return -EILSEQ; + } + + if (__test_ea(hdr->len)) + skb_pull(skb, 3); + else + skb_pull(skb, 4); + + switch (type) { + case RFCOMM_SABM: + if (__test_pf(hdr->ctrl)) + rfcomm_recv_sabm(s, dlci); + break; + + case RFCOMM_DISC: + if (__test_pf(hdr->ctrl)) + rfcomm_recv_disc(s, dlci); + break; + + case RFCOMM_UA: + if (__test_pf(hdr->ctrl)) + rfcomm_recv_ua(s, dlci); + break; + + case RFCOMM_DM: + rfcomm_recv_dm(s, dlci); + break; + + case RFCOMM_UIH: + if (dlci) + return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb); + + rfcomm_recv_mcc(s, skb); + break; + + default: + BT_ERR("Unknown packet type 0x%02x\n", type); + break; + } + kfree_skb(skb); + return 0; +} + +/* ---- Connection and data processing ---- */ + +static void rfcomm_process_connect(struct rfcomm_session *s) +{ + struct rfcomm_dlc *d; + struct list_head *p, *n; + + BT_DBG("session %p state %ld", s, s->state); + + list_for_each_safe(p, n, &s->dlcs) { + d = list_entry(p, struct rfcomm_dlc, list); + if (d->state == BT_CONFIG) { + d->mtu = s->mtu; + rfcomm_send_pn(s, 1, d); + } + } +} + +/* Send data queued for the DLC. + * Return number of frames left in the queue. + */ +static inline int rfcomm_process_tx(struct rfcomm_dlc *d) +{ + struct sk_buff *skb; + int err; + + BT_DBG("dlc %p state %ld credits %d rx_credits %d tx_credits %d", + d, d->state, d->credits, d->rx_credits, d->tx_credits); + + /* Send pending MSC */ + if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags)) + rfcomm_send_msc(d->session, d->dlci, 1, d->v24_sig); + + if (d->credits) { + /* CFC enabled. + * Give them some credits */ + if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) && + d->rx_credits <= (d->credits >> 2)) { + rfcomm_send_credits(d->session, d->addr, d->credits - d->rx_credits); + d->rx_credits = d->credits; + } + } else { + /* CFC disabled. + * Give ourselves some credits */ + d->tx_credits = RFCOMM_MAX_CREDITS; + } + + if (test_bit(RFCOMM_TX_THROTTLED, &d->flags)) + return skb_queue_len(&d->tx_queue); + + while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) { + err = rfcomm_send_frame(d->session, skb->data, skb->len); + if (err < 0) { + skb_queue_head(&d->tx_queue, skb); + break; + } + kfree_skb(skb); + d->tx_credits--; + } + + if (d->credits && !d->tx_credits) { + /* We're out of TX credits. + * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */ + set_bit(RFCOMM_TX_THROTTLED, &d->flags); + } + + return skb_queue_len(&d->tx_queue); +} + +static inline void rfcomm_process_dlcs(struct rfcomm_session *s) +{ + struct rfcomm_dlc *d; + struct list_head *p, *n; + + BT_DBG("session %p state %ld", s, s->state); + + list_for_each_safe(p, n, &s->dlcs) { + d = list_entry(p, struct rfcomm_dlc, list); + if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) { + __rfcomm_dlc_close(d, ETIMEDOUT); + continue; + } + + if (d->state == BT_CONNECTED || d->state == BT_DISCONN) + rfcomm_process_tx(d); + } +} + +static inline void rfcomm_process_rx(struct rfcomm_session *s) +{ + struct socket *sock = s->sock; + struct sock *sk = sock->sk; + struct sk_buff *skb; + + BT_DBG("session %p state %ld qlen %d", s, s->state, skb_queue_len(&sk->receive_queue)); + + /* Get data directly from socket receive queue without copying it. */ + while ((skb = skb_dequeue(&sk->receive_queue))) { + skb_orphan(skb); + rfcomm_recv_frame(s, skb); + } + + if (sk->state == BT_CLOSED) { + if (!s->initiator) + rfcomm_session_put(s); + + rfcomm_session_close(s, sk->err); + } +} + +static inline void rfcomm_accept_connection(struct rfcomm_session *s) +{ + struct socket *sock = s->sock, *nsock; + int err; + + /* Fast check for a new connection. + * Avoids unnesesary socket allocations. */ + if (list_empty(&bluez_sk(sock->sk)->accept_q)) + return; + + BT_DBG("session %p", s); + + nsock = sock_alloc(); + if (!nsock) + return; + + nsock->type = sock->type; + nsock->ops = sock->ops; + + err = sock->ops->accept(sock, nsock, O_NONBLOCK); + if (err < 0) { + sock_release(nsock); + return; + } + + /* Set our callbacks */ + nsock->sk->data_ready = rfcomm_l2data_ready; + nsock->sk->state_change = rfcomm_l2state_change; + + s = rfcomm_session_add(nsock, BT_OPEN); + if (s) + rfcomm_session_hold(s); + else + sock_release(nsock); +} + +static inline void rfcomm_check_connection(struct rfcomm_session *s) +{ + struct sock *sk = s->sock->sk; + + BT_DBG("%p state %ld", s, s->state); + + switch(sk->state) { + case BT_CONNECTED: + s->state = BT_CONNECT; + + /* We can adjust MTU on outgoing sessions. + * L2CAP MTU minus UIH header and FCS. */ + s->mtu = min(l2cap_pi(sk)->omtu, l2cap_pi(sk)->imtu) - 5; + + rfcomm_send_sabm(s, 0); + break; + + case BT_CLOSED: + s->state = BT_CLOSED; + rfcomm_session_close(s, sk->err); + break; + } +} + +static inline void rfcomm_process_sessions(void) +{ + struct list_head *p, *n; + + rfcomm_lock(); + + list_for_each_safe(p, n, &session_list) { + struct rfcomm_session *s; + s = list_entry(p, struct rfcomm_session, list); + + if (s->state == BT_LISTEN) { + rfcomm_accept_connection(s); + continue; + } + + rfcomm_session_hold(s); + + switch (s->state) { + case BT_BOUND: + rfcomm_check_connection(s); + break; + + default: + rfcomm_process_rx(s); + break; + } + + rfcomm_process_dlcs(s); + + rfcomm_session_put(s); + } + + rfcomm_unlock(); +} + +static void rfcomm_worker(void) +{ + BT_DBG(""); + + while (!atomic_read(&terminate)) { + if (!test_and_clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) { + /* No pending events. Let's sleep. + * Incomming connections and data will wake us up. */ + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + + /* Process stuff */ + rfcomm_process_sessions(); + } + set_current_state(TASK_RUNNING); + return; +} + +static int rfcomm_add_listener(bdaddr_t *ba) +{ + struct sockaddr_l2 addr; + struct l2cap_options opts; + struct socket *sock; + struct rfcomm_session *s; + int size, err = 0; + + /* Create socket */ + err = rfcomm_l2sock_create(&sock); + if (err < 0) { + BT_ERR("Create socket failed %d", err); + return err; + } + + /* Bind socket */ + bacpy(&addr.l2_bdaddr, ba); + addr.l2_family = AF_BLUETOOTH; + addr.l2_psm = htobs(RFCOMM_PSM); + err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr)); + if (err < 0) { + BT_ERR("Bind failed %d", err); + goto failed; + } + + /* Set L2CAP options */ + size = sizeof(opts); + sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size); + + opts.imtu = RFCOMM_MAX_L2CAP_MTU; + sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size); + + /* Start listening on the socket */ + err = sock->ops->listen(sock, 10); + if (err) { + BT_ERR("Listen failed %d", err); + goto failed; + } + + /* Add listening session */ + s = rfcomm_session_add(sock, BT_LISTEN); + if (!s) + goto failed; + + rfcomm_session_hold(s); + return 0; +failed: + sock_release(sock); + return err; +} + +static void rfcomm_kill_listener(void) +{ + struct rfcomm_session *s; + struct list_head *p, *n; + + BT_DBG(""); + + list_for_each_safe(p, n, &session_list) { + s = list_entry(p, struct rfcomm_session, list); + rfcomm_session_del(s); + } +} + +static int rfcomm_run(void *unused) +{ + rfcomm_thread = current; + + atomic_inc(&running); + + daemonize(); + set_user_nice(current, -10); + current->flags |= PF_IOTHREAD; + sigfillset(¤t->blocked); + flush_signals(current); + + sprintf(current->comm, "krfcommd"); + + set_fs(KERNEL_DS); + + BT_DBG(""); + + rfcomm_add_listener(BDADDR_ANY); + + rfcomm_worker(); + + rfcomm_kill_listener(); + + atomic_dec(&running); + return 0; +} + +int __init rfcomm_init(void) +{ + kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + + rfcomm_init_sockets(); + rfcomm_init_ttys(); + + BT_INFO("BlueZ RFCOMM ver %s", VERSION); + BT_INFO("Copyright (C) 2002 Maxim Krasnyansky "); + BT_INFO("Copyright (C) 2002 Marcel Holtmann "); + return 0; +} + +void rfcomm_cleanup(void) +{ + /* Terminate working thread. + * ie. Set terminate flag and wake it up */ + atomic_inc(&terminate); + rfcomm_schedule(RFCOMM_SCHED_STATE); + + /* Wait until thread is running */ + while (atomic_read(&running)) + schedule(); + + rfcomm_cleanup_ttys(); + rfcomm_cleanup_sockets(); + return; +} + +module_init(rfcomm_init); +module_exit(rfcomm_cleanup); + +MODULE_AUTHOR("Maxim Krasnyansky , Marcel Holtmann "); +MODULE_DESCRIPTION("BlueZ RFCOMM ver " VERSION); +MODULE_LICENSE("GPL"); diff --git a/net/bluetooth/rfcomm/crc.c b/net/bluetooth/rfcomm/crc.c new file mode 100644 index 000000000000..1011bc4a8692 --- /dev/null +++ b/net/bluetooth/rfcomm/crc.c @@ -0,0 +1,71 @@ +/* + RFCOMM implementation for Linux Bluetooth stack (BlueZ). + Copyright (C) 2002 Maxim Krasnyansky + Copyright (C) 2002 Marcel Holtmann + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +/* + * RFCOMM FCS calculation. + * + * $Id: crc.c,v 1.2 2002/09/21 09:54:32 holtmann Exp $ + */ + +/* reversed, 8-bit, poly=0x07 */ +unsigned char rfcomm_crc_table[256] = { + 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, + 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, + 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, + 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, + + 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, + 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, + 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, + 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, + + 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, + 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, + 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, + 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, + + 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, + 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, + 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, + 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, + + 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, + 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, + 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, + 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, + + 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, + 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, + 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, + 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, + + 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, + 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, + 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, + 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, + + 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, + 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, + 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, + 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf +}; diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c new file mode 100644 index 000000000000..a7cbb0ae0d01 --- /dev/null +++ b/net/bluetooth/rfcomm/sock.c @@ -0,0 +1,869 @@ +/* + RFCOMM implementation for Linux Bluetooth stack (BlueZ). + Copyright (C) 2002 Maxim Krasnyansky + Copyright (C) 2002 Marcel Holtmann + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +/* + * RFCOMM sockets. + * + * $Id: sock.c,v 1.24 2002/10/03 01:00:34 maxk Exp $ + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifndef CONFIG_RFCOMM_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +static struct proto_ops rfcomm_sock_ops; + +static struct bluez_sock_list rfcomm_sk_list = { + lock: RW_LOCK_UNLOCKED +}; + +static void rfcomm_sock_close(struct sock *sk); +static void rfcomm_sock_kill(struct sock *sk); + +/* ---- DLC callbacks ---- + * + * called under rfcomm_dlc_lock() + */ +static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb) +{ + struct sock *sk = d->owner; + if (!sk) + return; + + atomic_add(skb->len, &sk->rmem_alloc); + skb_queue_tail(&sk->receive_queue, skb); + sk->data_ready(sk, skb->len); + + if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) + rfcomm_dlc_throttle(d); + return; +} + +static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err) +{ + struct sock *sk = d->owner, *parent; + if (!sk) + return; + + BT_DBG("dlc %p state %ld err %d", d, d->state, err); + + if (err) + sk->err = err; + sk->state = d->state; + + parent = bluez_sk(sk)->parent; + if (!parent) + sk->state_change(sk); + else + parent->data_ready(parent, 0); + return; +} + +static void rfcomm_sk_modem_status(struct rfcomm_dlc *d, int v24_sig) +{ + BT_DBG("dlc %p v24_sig 0x%02x", d, v24_sig); + + return; +} + +/* ---- Socket functions ---- */ +static struct sock *__rfcomm_get_sock_by_addr(int channel, bdaddr_t *src) +{ + struct sock *sk; + + for (sk = rfcomm_sk_list.head; sk; sk = sk->next) { + if (rfcomm_pi(sk)->channel == channel && + !bacmp(&bluez_sk(sk)->src, src)) + break; + } + + return sk; +} + +/* Find socket with channel and source bdaddr. + * Returns closest match. + */ +static struct sock *__rfcomm_get_sock_by_channel(int state, __u16 channel, bdaddr_t *src) +{ + struct sock *sk, *sk1 = NULL; + + for (sk = rfcomm_sk_list.head; sk; sk = sk->next) { + if (state && sk->state != state) + continue; + + if (rfcomm_pi(sk)->channel == channel) { + /* Exact match. */ + if (!bacmp(&bluez_sk(sk)->src, src)) + break; + + /* Closest match */ + if (!bacmp(&bluez_sk(sk)->src, BDADDR_ANY)) + sk1 = sk; + } + } + return sk ? sk : sk1; +} + +/* Find socket with given address (channel, src). + * Returns locked socket */ +static inline struct sock *rfcomm_get_sock_by_channel(int state, __u16 channel, bdaddr_t *src) +{ + struct sock *s; + read_lock(&rfcomm_sk_list.lock); + s = __rfcomm_get_sock_by_channel(state, channel, src); + if (s) bh_lock_sock(s); + read_unlock(&rfcomm_sk_list.lock); + return s; +} + +static void rfcomm_sock_destruct(struct sock *sk) +{ + struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; + + BT_DBG("sk %p dlc %p", sk, d); + + skb_queue_purge(&sk->receive_queue); + skb_queue_purge(&sk->write_queue); + + rfcomm_dlc_lock(d); + rfcomm_pi(sk)->dlc = NULL; + + /* Detach DLC if it's owned by this socket */ + if (d->owner == sk) + d->owner = NULL; + rfcomm_dlc_unlock(d); + + rfcomm_dlc_put(d); + + if (sk->protinfo) + kfree(sk->protinfo); + + MOD_DEC_USE_COUNT; +} + +static void rfcomm_sock_cleanup_listen(struct sock *parent) +{ + struct sock *sk; + + BT_DBG("parent %p", parent); + + /* Close not yet accepted dlcs */ + while ((sk = bluez_accept_dequeue(parent, NULL))) + rfcomm_sock_close(sk); + + parent->state = BT_CLOSED; + parent->zapped = 1; +} + +/* Kill socket (only if zapped and orphan) + * Must be called on unlocked socket. + */ +static void rfcomm_sock_kill(struct sock *sk) +{ + if (!sk->zapped || sk->socket) + return; + + BT_DBG("sk %p state %d refcnt %d", sk, sk->state, atomic_read(&sk->refcnt)); + + /* Kill poor orphan */ + bluez_sock_unlink(&rfcomm_sk_list, sk); + sk->dead = 1; + sock_put(sk); +} + +/* Close socket. + * Must be called on unlocked socket. + */ +static void rfcomm_sock_close(struct sock *sk) +{ + struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; + + lock_sock(sk); + + BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket); + + switch (sk->state) { + case BT_LISTEN: + rfcomm_sock_cleanup_listen(sk); + break; + + case BT_CONNECT: + case BT_CONNECT2: + case BT_CONFIG: + case BT_CONNECTED: + rfcomm_dlc_close(d, 0); + + default: + sk->zapped = 1; + break; + }; + + release_sock(sk); + + rfcomm_sock_kill(sk); +} + +static void rfcomm_sock_init(struct sock *sk, struct sock *parent) +{ + BT_DBG("sk %p", sk); + + if (parent) + sk->type = parent->type; +} + +static struct sock *rfcomm_sock_alloc(struct socket *sock, int proto, int prio) +{ + struct rfcomm_dlc *d; + struct sock *sk; + + sk = bluez_sock_alloc(sock, BTPROTO_RFCOMM, sizeof(struct rfcomm_pinfo), prio); + if (!sk) + return NULL; + + d = rfcomm_dlc_alloc(prio); + if (!d) { + sk_free(sk); + return NULL; + } + d->data_ready = rfcomm_sk_data_ready; + d->state_change = rfcomm_sk_state_change; + d->modem_status = rfcomm_sk_modem_status; + + rfcomm_pi(sk)->dlc = d; + d->owner = sk; + + sk->destruct = rfcomm_sock_destruct; + sk->sndtimeo = RFCOMM_CONN_TIMEOUT; + + sk->sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; + sk->rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; + + sk->protocol = proto; + sk->state = BT_OPEN; + + bluez_sock_link(&rfcomm_sk_list, sk); + + BT_DBG("sk %p", sk); + + MOD_INC_USE_COUNT; + return sk; +} + +static int rfcomm_sock_create(struct socket *sock, int protocol) +{ + struct sock *sk; + + BT_DBG("sock %p", sock); + + sock->state = SS_UNCONNECTED; + + if (sock->type != SOCK_STREAM && sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + + sock->ops = &rfcomm_sock_ops; + + if (!(sk = rfcomm_sock_alloc(sock, protocol, GFP_KERNEL))) + return -ENOMEM; + + rfcomm_sock_init(sk, NULL); + return 0; +} + +static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) +{ + struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; + struct sock *sk = sock->sk; + int err = 0; + + BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr)); + + if (!addr || addr->sa_family != AF_BLUETOOTH) + return -EINVAL; + + lock_sock(sk); + + if (sk->state != BT_OPEN) { + err = -EBADFD; + goto done; + } + + write_lock_bh(&rfcomm_sk_list.lock); + + if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) { + err = -EADDRINUSE; + } else { + /* Save source address */ + bacpy(&bluez_sk(sk)->src, &sa->rc_bdaddr); + rfcomm_pi(sk)->channel = sa->rc_channel; + sk->state = BT_BOUND; + } + + write_unlock_bh(&rfcomm_sk_list.lock); + +done: + release_sock(sk); + return err; +} + +static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) +{ + struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; + struct sock *sk = sock->sk; + struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; + int err = 0; + + BT_DBG("sk %p", sk); + + if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_rc)) + return -EINVAL; + + if (sk->state != BT_OPEN && sk->state != BT_BOUND) + return -EBADFD; + + if (sk->type != SOCK_STREAM) + return -EINVAL; + + lock_sock(sk); + + sk->state = BT_CONNECT; + bacpy(&bluez_sk(sk)->dst, &sa->rc_bdaddr); + + err = rfcomm_dlc_open(d, &bluez_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel); + if (!err) + err = bluez_sock_w4_connect(sk, flags); + + release_sock(sk); + return err; +} + +int rfcomm_sock_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + int err = 0; + + BT_DBG("sk %p backlog %d", sk, backlog); + + lock_sock(sk); + + if (sk->state != BT_BOUND) { + err = -EBADFD; + goto done; + } + + sk->max_ack_backlog = backlog; + sk->ack_backlog = 0; + sk->state = BT_LISTEN; + +done: + release_sock(sk); + return err; +} + +int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags) +{ + DECLARE_WAITQUEUE(wait, current); + struct sock *sk = sock->sk, *nsk; + long timeo; + int err = 0; + + lock_sock(sk); + + if (sk->state != BT_LISTEN) { + err = -EBADFD; + goto done; + } + + timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); + + BT_DBG("sk %p timeo %ld", sk, timeo); + + /* Wait for an incoming connection. (wake-one). */ + add_wait_queue_exclusive(sk->sleep, &wait); + while (!(nsk = bluez_accept_dequeue(sk, newsock))) { + set_current_state(TASK_INTERRUPTIBLE); + if (!timeo) { + err = -EAGAIN; + break; + } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + + if (sk->state != BT_LISTEN) { + err = -EBADFD; + break; + } + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(sk->sleep, &wait); + + if (err) + goto done; + + newsock->state = SS_CONNECTED; + + BT_DBG("new socket %p", nsk); + +done: + release_sock(sk); + return err; +} + +static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) +{ + struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; + struct sock *sk = sock->sk; + + BT_DBG("sock %p, sk %p", sock, sk); + + sa->rc_family = AF_BLUETOOTH; + sa->rc_channel = rfcomm_pi(sk)->channel; + if (peer) + bacpy(&sa->rc_bdaddr, &bluez_sk(sk)->dst); + else + bacpy(&sa->rc_bdaddr, &bluez_sk(sk)->src); + + *len = sizeof(struct sockaddr_rc); + return 0; +} + +static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, + struct scm_cookie *scm) +{ + struct sock *sk = sock->sk; + struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; + struct sk_buff *skb; + int err, size; + int sent = 0; + + if (msg->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + if (sk->shutdown & SEND_SHUTDOWN) + return -EPIPE; + + BT_DBG("sock %p, sk %p", sock, sk); + + lock_sock(sk); + + while (len) { + size = min_t(uint, len, d->mtu); + + skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + break; + skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); + + err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + if (err) { + kfree_skb(skb); + sent = err; + break; + } + + err = rfcomm_dlc_send(d, skb); + if (err < 0) { + kfree_skb(skb); + break; + } + + sent += size; + len -= size; + } + + release_sock(sk); + + return sent ? sent : err; +} + +static long rfcomm_sock_data_wait(struct sock *sk, long timeo) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(sk->sleep, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + + if (skb_queue_len(&sk->receive_queue) || sk->err || (sk->shutdown & RCV_SHUTDOWN) || + signal_pending(current) || !timeo) + break; + + set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags); + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags); + } + + __set_current_state(TASK_RUNNING); + remove_wait_queue(sk->sleep, &wait); + return timeo; +} + +static int rfcomm_sock_recvmsg(struct socket *sock, struct msghdr *msg, int size, + int flags, struct scm_cookie *scm) +{ + struct sock *sk = sock->sk; + int target, err = 0, copied = 0; + long timeo; + + if (sk->state != BT_CONNECTED) + return -EINVAL; + + if (flags & MSG_OOB) + return -EOPNOTSUPP; + + msg->msg_namelen = 0; + + BT_DBG("sk %p size %d", sk, size); + + lock_sock(sk); + + target = sock_rcvlowat(sk, flags & MSG_WAITALL, size); + timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); + + do { + struct sk_buff *skb; + int chunk; + + skb = skb_dequeue(&sk->receive_queue); + if (!skb) { + if (copied >= target) + break; + + if ((err = sock_error(sk)) != 0) + break; + if (sk->shutdown & RCV_SHUTDOWN) + break; + + err = -EAGAIN; + if (!timeo) + break; + + timeo = rfcomm_sock_data_wait(sk, timeo); + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + goto out; + } + continue; + } + + chunk = min_t(unsigned int, skb->len, size); + if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { + skb_queue_head(&sk->receive_queue, skb); + if (!copied) + copied = -EFAULT; + break; + } + copied += chunk; + size -= chunk; + + if (!(flags & MSG_PEEK)) { + atomic_sub(chunk, &sk->rmem_alloc); + + skb_pull(skb, chunk); + if (skb->len) { + skb_queue_head(&sk->receive_queue, skb); + break; + } + kfree_skb(skb); + + } else { + /* put message back and return */ + skb_queue_head(&sk->receive_queue, skb); + break; + } + } while (size); + +out: + if (atomic_read(&sk->rmem_alloc) <= (sk->rcvbuf >> 2)) + rfcomm_dlc_unthrottle(rfcomm_pi(sk)->dlc); + + release_sock(sk); + return copied ? : err; +} + +static int rfcomm_sock_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + + BT_DBG("sock %p, sk %p", sock, sk); + + if (!sk) return 0; + + lock_sock(sk); + sk->shutdown = SHUTDOWN_MASK; + if (sk->state == BT_CONNECTED) + rfcomm_dlc_close(rfcomm_pi(sk)->dlc, 0); + release_sock(sk); + + return 0; +} + + +static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) +{ + struct sock *sk = sock->sk; + int err = 0; + + BT_DBG("sk %p", sk); + + lock_sock(sk); + + switch (optname) { + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) +{ + struct sock *sk = sock->sk; + int len, err = 0; + + BT_DBG("sk %p", sk); + + if (get_user(len, optlen)) + return -EFAULT; + + lock_sock(sk); + + switch (optname) { + default: + err = -ENOPROTOOPT; + break; + }; + + release_sock(sk); + return err; +} + +static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct sock *sk = sock->sk; + int err; + + lock_sock(sk); + +#ifdef CONFIG_RFCOMM_TTY + err = rfcomm_dev_ioctl(sk, cmd, arg); +#else + err = -EOPNOTSUPP; +#endif + + release_sock(sk); + return err; +} + +static int rfcomm_sock_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + BT_DBG("sock %p, sk %p", sock, sk); + + if (!sk) + return 0; + + sock_orphan(sk); + rfcomm_sock_close(sk); + + return 0; +} + +/* ---- RFCOMM core layer callbacks ---- + * + * called under rfcomm_lock() + */ +int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d) +{ + struct bluez_sock *bsk = bluez_sk(s->sock->sk); + struct sock *sk, *parent; + int result = 0; + + BT_DBG("session %p channel %d", s, channel); + + /* Check if we have socket listening on channel */ + parent = rfcomm_get_sock_by_channel(BT_LISTEN, channel, &bsk->src); + if (!parent) + return 0; + + /* Check for backlog size */ + if (parent->ack_backlog > parent->max_ack_backlog) { + BT_DBG("backlog full %d", parent->ack_backlog); + goto done; + } + + sk = rfcomm_sock_alloc(NULL, BTPROTO_RFCOMM, GFP_ATOMIC); + if (!sk) + goto done; + + rfcomm_sock_init(sk, parent); + bacpy(&bluez_sk(sk)->src, &bsk->src); + bacpy(&bluez_sk(sk)->dst, &bsk->dst); + rfcomm_pi(sk)->channel = channel; + + sk->state = BT_CONFIG; + bluez_accept_enqueue(parent, sk); + + /* Accept connection and return socket DLC */ + *d = rfcomm_pi(sk)->dlc; + result = 1; + +done: + bh_unlock_sock(parent); + return result; +} + +/* ---- Proc fs support ---- */ +static int rfcomm_sock_dump(char *buf, struct bluez_sock_list *list) +{ + struct rfcomm_pinfo *pi; + struct sock *sk; + char *ptr = buf; + + write_lock_bh(&list->lock); + + for (sk = list->head; sk; sk = sk->next) { + pi = rfcomm_pi(sk); + ptr += sprintf(ptr, "%s %s %d %d\n", + batostr(&bluez_sk(sk)->src), batostr(&bluez_sk(sk)->dst), + sk->state, rfcomm_pi(sk)->channel); + } + + write_unlock_bh(&list->lock); + + ptr += sprintf(ptr, "\n"); + + return ptr - buf; +} + +static int rfcomm_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv) +{ + char *ptr = buf; + int len; + + BT_DBG("count %d, offset %ld", count, offset); + + ptr += rfcomm_sock_dump(ptr, &rfcomm_sk_list); + len = ptr - buf; + + if (len <= count + offset) + *eof = 1; + + *start = buf + offset; + len -= offset; + + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} + +static struct proto_ops rfcomm_sock_ops = { + .family = PF_BLUETOOTH, + .release = rfcomm_sock_release, + .bind = rfcomm_sock_bind, + .connect = rfcomm_sock_connect, + .listen = rfcomm_sock_listen, + .accept = rfcomm_sock_accept, + .getname = rfcomm_sock_getname, + .sendmsg = rfcomm_sock_sendmsg, + .recvmsg = rfcomm_sock_recvmsg, + .shutdown = rfcomm_sock_shutdown, + .setsockopt = rfcomm_sock_setsockopt, + .getsockopt = rfcomm_sock_getsockopt, + .ioctl = rfcomm_sock_ioctl, + .poll = bluez_sock_poll, + .socketpair = sock_no_socketpair, + .mmap = sock_no_mmap +}; + +static struct net_proto_family rfcomm_sock_family_ops = { + .family = PF_BLUETOOTH, + .create = rfcomm_sock_create +}; + +int rfcomm_init_sockets(void) +{ + int err; + + if ((err = bluez_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops))) { + BT_ERR("Can't register RFCOMM socket layer"); + return err; + } + + create_proc_read_entry("bluetooth/rfcomm", 0, 0, rfcomm_read_proc, NULL); + return 0; +} + +void rfcomm_cleanup_sockets(void) +{ + int err; + + remove_proc_entry("bluetooth/rfcomm", NULL); + + /* Unregister socket, protocol and notifier */ + if ((err = bluez_sock_unregister(BTPROTO_RFCOMM))) + BT_ERR("Can't unregister RFCOMM socket layer %d", err); +} diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c new file mode 100644 index 000000000000..8c1c491d0e07 --- /dev/null +++ b/net/bluetooth/rfcomm/tty.c @@ -0,0 +1,884 @@ +/* + RFCOMM implementation for Linux Bluetooth stack (BlueZ). + Copyright (C) 2002 Maxim Krasnyansky + Copyright (C) 2002 Marcel Holtmann + + 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; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +/* + * RFCOMM TTY. + * + * $Id: tty.c,v 1.24 2002/10/03 01:54:38 holtmann Exp $ + */ + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#ifndef CONFIG_RFCOMM_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +#define RFCOMM_TTY_MAGIC 0x6d02 /* magic number for rfcomm struct */ +#define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV /* whole lotta rfcomm devices */ +#define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */ +#define RFCOMM_TTY_MINOR 0 + +struct rfcomm_dev { + struct list_head list; + atomic_t refcnt; + + char name[12]; + int id; + unsigned long flags; + int opened; + int err; + + bdaddr_t src; + bdaddr_t dst; + u8 channel; + + struct rfcomm_dlc *dlc; + struct tty_struct *tty; + wait_queue_head_t wait; + struct tasklet_struct wakeup_task; + + atomic_t wmem_alloc; + unsigned int sndbuf; +}; + +static LIST_HEAD(rfcomm_dev_list); +static rwlock_t rfcomm_dev_lock = RW_LOCK_UNLOCKED; + +static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb); +static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err); +static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, int v24_sig); + +static void rfcomm_tty_wakeup(unsigned long arg); + +/* ---- Device functions ---- */ +static void rfcomm_dev_destruct(struct rfcomm_dev *dev) +{ + struct rfcomm_dlc *dlc = dev->dlc; + + BT_DBG("dev %p dlc %p", dev, dlc); + + rfcomm_dlc_lock(dlc); + /* Detach DLC if it's owned by this dev */ + if (dlc->owner == dev) + dlc->owner = NULL; + rfcomm_dlc_unlock(dlc); + + rfcomm_dlc_put(dlc); + kfree(dev); + + MOD_DEC_USE_COUNT; +} + +static inline void rfcomm_dev_hold(struct rfcomm_dev *dev) +{ + atomic_inc(&dev->refcnt); +} + +static inline void rfcomm_dev_put(struct rfcomm_dev *dev) +{ + if (atomic_dec_and_test(&dev->refcnt)) + rfcomm_dev_destruct(dev); +} + +static struct rfcomm_dev *__rfcomm_dev_get(int id) +{ + struct rfcomm_dev *dev; + struct list_head *p; + + list_for_each(p, &rfcomm_dev_list) { + dev = list_entry(p, struct rfcomm_dev, list); + if (dev->id == id) + return dev; + } + + return NULL; +} + +static inline struct rfcomm_dev *rfcomm_dev_get(int id) +{ + struct rfcomm_dev *dev; + + read_lock(&rfcomm_dev_lock); + dev = __rfcomm_dev_get(id); + read_unlock(&rfcomm_dev_lock); + + if (dev) rfcomm_dev_hold(dev); + return dev; +} + +static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) +{ + struct rfcomm_dev *dev; + struct list_head *head = &rfcomm_dev_list, *p; + int err = 0; + + BT_DBG("id %d channel %d", req->dev_id, req->channel); + + dev = kmalloc(sizeof(struct rfcomm_dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + memset(dev, 0, sizeof(struct rfcomm_dev)); + + write_lock_bh(&rfcomm_dev_lock); + + if (req->dev_id < 0) { + dev->id = 0; + + list_for_each(p, &rfcomm_dev_list) { + if (list_entry(p, struct rfcomm_dev, list)->id != dev->id) + break; + + dev->id++; + head = p; + } + } else { + dev->id = req->dev_id; + + list_for_each(p, &rfcomm_dev_list) { + struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list); + + if (entry->id == dev->id) { + err = -EADDRINUSE; + goto out; + } + + if (entry->id > dev->id - 1) + break; + + head = p; + } + } + + if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) { + err = -ENFILE; + goto out; + } + + sprintf(dev->name, "rfcomm%d", dev->id); + + list_add(&dev->list, head); + atomic_set(&dev->refcnt, 1); + + bacpy(&dev->src, &req->src); + bacpy(&dev->dst, &req->dst); + dev->channel = req->channel; + + dev->flags = req->flags & + ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC)); + + dev->sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; + + init_waitqueue_head(&dev->wait); + tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev); + + rfcomm_dlc_lock(dlc); + dlc->data_ready = rfcomm_dev_data_ready; + dlc->state_change = rfcomm_dev_state_change; + dlc->modem_status = rfcomm_dev_modem_status; + + dlc->owner = dev; + dev->dlc = dlc; + rfcomm_dlc_unlock(dlc); + + MOD_INC_USE_COUNT; + +out: + write_unlock_bh(&rfcomm_dev_lock); + + if (err) { + kfree(dev); + return err; + } else + return dev->id; +} + +static void rfcomm_dev_del(struct rfcomm_dev *dev) +{ + BT_DBG("dev %p", dev); + + write_lock_bh(&rfcomm_dev_lock); + list_del_init(&dev->list); + write_unlock_bh(&rfcomm_dev_lock); + + rfcomm_dev_put(dev); +} + +/* ---- Send buffer ---- */ +static void rfcomm_wfree(struct sk_buff *skb) +{ + struct rfcomm_dev *dev = (void *) skb->sk; + atomic_sub(skb->truesize, &dev->wmem_alloc); + if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) + tasklet_schedule(&dev->wakeup_task); + rfcomm_dev_put(dev); +} + +static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev) +{ + rfcomm_dev_hold(dev); + atomic_add(skb->truesize, &dev->wmem_alloc); + skb->sk = (void *) dev; + skb->destructor = rfcomm_wfree; +} + +static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int priority) +{ + if (size || atomic_read(&dev->wmem_alloc) < dev->sndbuf) { + struct sk_buff *skb = alloc_skb(size, priority); + if (skb) { + rfcomm_set_owner_w(skb, dev); + return skb; + } + } + return NULL; +} + +/* ---- Device IOCTLs ---- */ +static int rfcomm_create_dev(struct sock *sk, unsigned long arg) +{ + struct rfcomm_dev_req req; + struct rfcomm_dlc *dlc; + int id; + + if (copy_from_user(&req, (void *) arg, sizeof(req))) + return -EFAULT; + + BT_DBG("sk %p dev_id %id flags 0x%x", sk, req.dev_id, req.flags); + + if (req.flags & (1 << RFCOMM_REUSE_DLC)) { + dlc = rfcomm_pi(sk)->dlc; + rfcomm_dlc_hold(dlc); + } else { + dlc = rfcomm_dlc_alloc(GFP_KERNEL); + if (!dlc) + return -ENOMEM; + } + + id = rfcomm_dev_add(&req, dlc); + if (id < 0) { + rfcomm_dlc_put(dlc); + return id; + } + + if (req.flags & (1 << RFCOMM_REUSE_DLC)) { + /* DLC is now used by device. + * Socket must be disconnected */ + sk->state = BT_CLOSED; + } + + return id; +} + +static int rfcomm_release_dev(unsigned long arg) +{ + struct rfcomm_dev_req req; + struct rfcomm_dev *dev; + + if (copy_from_user(&req, (void *) arg, sizeof(req))) + return -EFAULT; + + BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags); + + if (!(dev = rfcomm_dev_get(req.dev_id))) + return -ENODEV; + + if (req.flags & (1 << RFCOMM_HANGUP_NOW)) + rfcomm_dlc_close(dev->dlc, 0); + + rfcomm_dev_del(dev); + rfcomm_dev_put(dev); + return 0; +} + +static int rfcomm_get_dev_list(unsigned long arg) +{ + struct rfcomm_dev_list_req *dl; + struct rfcomm_dev_info *di; + struct list_head *p; + int n = 0, size; + u16 dev_num; + + BT_DBG(""); + + if (get_user(dev_num, (u16 *) arg)) + return -EFAULT; + + if (!dev_num) + return -EINVAL; + + size = sizeof(*dl) + dev_num * sizeof(*di); + + if (verify_area(VERIFY_WRITE, (void *)arg, size)) + return -EFAULT; + + if (!(dl = kmalloc(size, GFP_KERNEL))) + return -ENOMEM; + + di = dl->dev_info; + + read_lock_bh(&rfcomm_dev_lock); + + list_for_each(p, &rfcomm_dev_list) { + struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list); + (di + n)->id = dev->id; + (di + n)->flags = dev->flags; + (di + n)->state = dev->dlc->state; + (di + n)->channel = dev->channel; + bacpy(&(di + n)->src, &dev->src); + bacpy(&(di + n)->dst, &dev->dst); + if (++n >= dev_num) + break; + } + + read_unlock_bh(&rfcomm_dev_lock); + + dl->dev_num = n; + size = sizeof(*dl) + n * sizeof(*di); + + copy_to_user((void *) arg, dl, size); + kfree(dl); + return 0; +} + +static int rfcomm_get_dev_info(unsigned long arg) +{ + struct rfcomm_dev *dev; + struct rfcomm_dev_info di; + int err = 0; + + BT_DBG(""); + + if (copy_from_user(&di, (void *)arg, sizeof(di))) + return -EFAULT; + + if (!(dev = rfcomm_dev_get(di.id))) + return -ENODEV; + + di.flags = dev->flags; + di.channel = dev->channel; + di.state = dev->dlc->state; + bacpy(&di.src, &dev->src); + bacpy(&di.dst, &dev->dst); + + if (copy_to_user((void *)arg, &di, sizeof(di))) + err = -EFAULT; + + rfcomm_dev_put(dev); + return err; +} + +int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) +{ + BT_DBG("cmd %d arg %ld", cmd, arg); + + switch (cmd) { + case RFCOMMCREATEDEV: + return rfcomm_create_dev(sk, arg); + + case RFCOMMRELEASEDEV: + return rfcomm_release_dev(arg); + + case RFCOMMGETDEVLIST: + return rfcomm_get_dev_list(arg); + + case RFCOMMGETDEVINFO: + return rfcomm_get_dev_info(arg); + } + + return -EINVAL; +} + +/* ---- DLC callbacks ---- */ +static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb) +{ + struct rfcomm_dev *dev = dlc->owner; + struct tty_struct *tty; + + if (!dev || !(tty = dev->tty)) { + kfree(skb); + return; + } + + BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len); + + if (test_bit(TTY_DONT_FLIP, &tty->flags)) { + register int i; + for (i = 0; i < skb->len; i++) { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + tty_flip_buffer_push(tty); + + tty_insert_flip_char(tty, skb->data[i], 0); + } + tty_flip_buffer_push(tty); + } else + tty->ldisc.receive_buf(tty, skb->data, NULL, skb->len); + + kfree_skb(skb); +} + +static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) +{ + struct rfcomm_dev *dev = dlc->owner; + if (!dev) + return; + + BT_DBG("dlc %p dev %p err %d", dlc, dev, err); + + dev->err = err; + wake_up_interruptible(&dev->wait); + + if (dlc->state == BT_CLOSED) { + if (!dev->tty) { + if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { + rfcomm_dev_hold(dev); + rfcomm_dev_del(dev); + + /* We have to drop DLC lock here, otherwise + * rfcomm_dev_put() will dead lock if it's the last refference */ + rfcomm_dlc_unlock(dlc); + rfcomm_dev_put(dev); + rfcomm_dlc_lock(dlc); + } + } else + tty_hangup(dev->tty); + } +} + +static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, int v24_sig) +{ + BT_DBG("dlc %p v24_sig 0x%02x", dlc, v24_sig); +} + + +/* ---- TTY functions ---- */ +static void rfcomm_tty_wakeup(unsigned long arg) +{ + struct rfcomm_dev *dev = (void *) arg; + struct tty_struct *tty = dev->tty; + if (!tty) + return; + + BT_DBG("dev %p tty %p", dev, tty); + + if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && 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 +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#define __minor MINOR +#else +#define __minor minor +#endif + +static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) +{ + DECLARE_WAITQUEUE(wait, current); + struct rfcomm_dev *dev; + struct rfcomm_dlc *dlc; + int err, id; + + id = __minor(tty->device) - tty->driver.minor_start; + + BT_DBG("tty %p id %d", tty, id); + + dev = rfcomm_dev_get(id); + if (!dev) + return -ENODEV; + + BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened); + + if (dev->opened++ != 0) + return 0; + + dlc = dev->dlc; + + /* Attach TTY and open DLC */ + + rfcomm_dlc_lock(dlc); + tty->driver_data = dev; + dev->tty = tty; + rfcomm_dlc_unlock(dlc); + set_bit(RFCOMM_TTY_ATTACHED, &dev->flags); + + err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel); + if (err < 0) + return err; + + /* Wait for DLC to connect */ + add_wait_queue(&dev->wait, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + + if (dlc->state == BT_CLOSED) { + err = -dev->err; + break; + } + + if (dlc->state == BT_CONNECTED) + break; + + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dev->wait, &wait); + + return err; +} + +static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) +{ + struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + if (!dev) + return; + + BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened); + + if (--dev->opened == 0) { + /* Close DLC and dettach TTY */ + rfcomm_dlc_close(dev->dlc, 0); + + clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); + tasklet_kill(&dev->wakeup_task); + + rfcomm_dlc_lock(dev->dlc); + tty->driver_data = NULL; + dev->tty = NULL; + rfcomm_dlc_unlock(dev->dlc); + } + + rfcomm_dev_put(dev); +} + +static int rfcomm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) +{ + struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + struct rfcomm_dlc *dlc = dev->dlc; + struct sk_buff *skb; + int err = 0, sent = 0, size; + + BT_DBG("tty %p from_user %d count %d", tty, from_user, count); + + while (count) { + size = min_t(uint, count, dlc->mtu); + + if (from_user) + skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_KERNEL); + else + skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC); + + if (!skb) + break; + + skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); + + if (from_user) + copy_from_user(skb_put(skb, size), buf + sent, size); + else + memcpy(skb_put(skb, size), buf + sent, size); + + if ((err = rfcomm_dlc_send(dlc, skb)) < 0) { + kfree_skb(skb); + break; + } + + sent += size; + count -= size; + } + + return sent ? sent : err; +} + +static int rfcomm_tty_write_room(struct tty_struct *tty) +{ + struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + struct rfcomm_dlc *dlc = dev->dlc; + + BT_DBG("tty %p", tty); + + return dlc->mtu * (dlc->tx_credits ? : 10); +} + +static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + struct rfcomm_dlc *dlc = dev->dlc; + unsigned int modeminfo; + int err; + + BT_DBG("tty %p cmd 0x%02x", tty, cmd); + + switch (cmd) { + case TCGETS: + BT_DBG("TCGETS is not supported"); + return -ENOIOCTLCMD; + + case TCSETS: + BT_DBG("TCSETS is not supported"); + return -ENOIOCTLCMD; + + case TIOCMGET: + BT_DBG("TIOCMGET"); + + modeminfo = ((dlc->v24_sig & RFCOMM_V24_RTC) ? TIOCM_DSR | TIOCM_DTR : 0) + | ((dlc->v24_sig & RFCOMM_V24_RTR) ? TIOCM_RTS | TIOCM_CTS : 0) + | ((dlc->v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0) + | ((dlc->v24_sig & RFCOMM_V24_DV) ? TIOCM_CD : 0); + + return put_user(modeminfo, (unsigned int *)arg); + + case TIOCMBIS: + BT_DBG("TIOCMBIS"); + + if ((err = get_user(modeminfo, (unsigned int *)arg))) + return err; + + dlc->v24_sig |= (modeminfo & TIOCM_DSR) ? RFCOMM_V24_RTC : 0; + dlc->v24_sig |= (modeminfo & TIOCM_DTR) ? RFCOMM_V24_RTC : 0; + dlc->v24_sig |= (modeminfo & TIOCM_RTS) ? RFCOMM_V24_RTR : 0; + dlc->v24_sig |= (modeminfo & TIOCM_CTS) ? RFCOMM_V24_RTR : 0; + dlc->v24_sig |= (modeminfo & TIOCM_RI) ? RFCOMM_V24_IC : 0; + dlc->v24_sig |= (modeminfo & TIOCM_CD) ? RFCOMM_V24_DV : 0; + + // rfcomm_send_msc(dlc->session, dlc->dlci, 1, dlc->v24_sig); + + return 0; + + case TIOCMBIC: + case TIOCMSET: + BT_DBG("set modem info"); + break; + + case TIOCMIWAIT: + BT_DBG("TIOCMIWAIT"); + break; + + case TIOCGICOUNT: + BT_DBG("TIOCGICOUNT"); + break; + + case TIOCGSERIAL: + BT_ERR("TIOCGSERIAL is not supported"); + return -ENOIOCTLCMD; + + case TIOCSSERIAL: + BT_ERR("TIOCSSERIAL is not supported"); + return -ENOIOCTLCMD; + + case TIOCSERGSTRUCT: + BT_ERR("TIOCSERGSTRUCT is not supported"); + return -ENOIOCTLCMD; + + case TIOCSERGETLSR: + BT_ERR("TIOCSERGETLSR is not supported"); + return -ENOIOCTLCMD; + + case TIOCSERCONFIG: + BT_ERR("TIOCSERCONFIG is not supported"); + return -ENOIOCTLCMD; + + default: + return -ENOIOCTLCMD; /* ioctls which we must ignore */ + + } + + return -ENOIOCTLCMD; +} + +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + +static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old) +{ + BT_DBG("tty %p", tty); + + if ((tty->termios->c_cflag == old->c_cflag) && + (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old->c_iflag))) + return; + + /* handle turning off CRTSCTS */ + if ((old->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { + BT_DBG("turning off CRTSCTS"); + } +} + +static void rfcomm_tty_throttle(struct tty_struct *tty) +{ + struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + + BT_DBG("tty %p dev %p", tty, dev); + + rfcomm_dlc_throttle(dev->dlc); +} + +static void rfcomm_tty_unthrottle(struct tty_struct *tty) +{ + struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + + BT_DBG("tty %p dev %p", tty, dev); + + rfcomm_dlc_unthrottle(dev->dlc); +} + +static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + struct rfcomm_dlc *dlc = dev->dlc; + + BT_DBG("tty %p dev %p", tty, dev); + + if (skb_queue_len(&dlc->tx_queue)) + return dlc->mtu; + + return 0; +} + +static void rfcomm_tty_flush_buffer(struct tty_struct *tty) +{ + struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + if (!dev) + return; + + BT_DBG("tty %p dev %p", tty, dev); + + skb_queue_purge(&dev->dlc->tx_queue); + + if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) + tty->ldisc.write_wakeup(tty); +} + +static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch) +{ + BT_DBG("tty %p ch %c", tty, ch); +} + +static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) +{ + BT_DBG("tty %p timeout %d", tty, timeout); +} + +static void rfcomm_tty_hangup(struct tty_struct *tty) +{ + struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + if (!dev) + return; + + BT_DBG("tty %p dev %p", tty, dev); + + rfcomm_tty_flush_buffer(tty); + + if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) + rfcomm_dev_del(dev); +} + +static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused) +{ + return 0; +} + +/* ---- TTY structure ---- */ +static int rfcomm_tty_refcount; /* If we manage several devices */ + +static struct tty_struct *rfcomm_tty_table[RFCOMM_TTY_PORTS]; +static struct termios *rfcomm_tty_termios[RFCOMM_TTY_PORTS]; +static struct termios *rfcomm_tty_termios_locked[RFCOMM_TTY_PORTS]; + +static struct tty_driver rfcomm_tty_driver = { + magic: TTY_DRIVER_MAGIC, + driver_name: "rfcomm", +#ifdef CONFIG_DEVFS_FS + name: "bluetooth/rfcomm/%d", +#else + name: "rfcomm%d", +#endif + major: RFCOMM_TTY_MAJOR, + minor_start: RFCOMM_TTY_MINOR, + num: RFCOMM_TTY_PORTS, + type: TTY_DRIVER_TYPE_SERIAL, + subtype: SERIAL_TYPE_NORMAL, + flags: TTY_DRIVER_REAL_RAW, + + refcount: &rfcomm_tty_refcount, + table: rfcomm_tty_table, + termios: rfcomm_tty_termios, + termios_locked: rfcomm_tty_termios_locked, + + open: rfcomm_tty_open, + close: rfcomm_tty_close, + write: rfcomm_tty_write, + write_room: rfcomm_tty_write_room, + chars_in_buffer: rfcomm_tty_chars_in_buffer, + flush_buffer: rfcomm_tty_flush_buffer, + ioctl: rfcomm_tty_ioctl, + throttle: rfcomm_tty_throttle, + unthrottle: rfcomm_tty_unthrottle, + set_termios: rfcomm_tty_set_termios, + send_xchar: rfcomm_tty_send_xchar, + stop: NULL, + start: NULL, + hangup: rfcomm_tty_hangup, + wait_until_sent: rfcomm_tty_wait_until_sent, + read_proc: rfcomm_tty_read_proc, +}; + +int rfcomm_init_ttys(void) +{ + int i; + + /* Initalize our global data */ + for (i = 0; i < RFCOMM_TTY_PORTS; i++) + rfcomm_tty_table[i] = NULL; + + /* Register the TTY driver */ + rfcomm_tty_driver.init_termios = tty_std_termios; + rfcomm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + rfcomm_tty_driver.flags = TTY_DRIVER_REAL_RAW; + + if (tty_register_driver(&rfcomm_tty_driver)) { + BT_ERR("Can't register RFCOMM TTY driver"); + return -1; + } + + return 0; +} + +void rfcomm_cleanup_ttys(void) +{ + tty_unregister_driver(&rfcomm_tty_driver); + return; +} -- cgit v1.2.3 From 0f8328bc361590cfff60da8fca696fbe5887550d Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 4 Oct 2002 19:49:22 +0200 Subject: ALSA - DEVFS cleanup - removal of compatibility code for 2.2 and 2.4 kernels - fixed sgalaxy driver (save_flags/cli/restore_flags removal) - USB Audio driver - added the missing dev_set_drvdata() for 2.5 API - simplified the conexistence of old and new USB APIs - don't skip the active capture urbs - added the debug print for active capture urbs - don't change runtime->rate even if the current rate is not same - check the bandwidth for urbs (for tests only, now commented out) --- include/sound/version.h | 2 +- sound/core/info.c | 6 ---- sound/core/sound.c | 9 ----- sound/isa/sgalaxy.c | 21 ++++++----- sound/usb/usbaudio.c | 93 ++++++++++++++++++++++++++++++------------------- 5 files changed, 70 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/include/sound/version.h b/include/sound/version.h index bafde4fbc070..ba48af7a3099 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ #define CONFIG_SND_VERSION "0.9.0rc3" -#define CONFIG_SND_DATE " (Tue Oct 01 14:40:23 2002 UTC)" +#define CONFIG_SND_DATE " (Fri Oct 04 13:09:13 2002 UTC)" diff --git a/sound/core/info.c b/sound/core/info.c index 750bf080115e..96dbae13331d 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -960,7 +960,6 @@ void snd_info_free_device(snd_info_entry_t * entry) { #ifdef CONFIG_DEVFS_FS char dname[32]; - devfs_handle_t master; #endif snd_runtime_check(entry, return); @@ -970,12 +969,7 @@ void snd_info_free_device(snd_info_entry_t * entry) #ifdef CONFIG_DEVFS_FS if (entry->p && strncmp(entry->name, "controlC", 8)) { sprintf(dname, "snd/%s", entry->name); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) - master = devfs_find_handle(NULL, dname, strlen(dname), 0, 0, DEVFS_SPECIAL_CHR, 0); - devfs_unregister(master); -#else devfs_find_and_unregister(NULL, dname, 0, 0, DEVFS_SPECIAL_CHR, 0); -#endif } #endif snd_info_free_entry(entry); diff --git a/sound/core/sound.c b/sound/core/sound.c index 7fbf2bd70dfb..6a7056336c57 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -358,21 +358,12 @@ static int __init alsa_sound_init(void) static void __exit alsa_sound_exit(void) { #ifdef CONFIG_DEVFS_FS - devfs_handle_t master; char controlname[24]; short controlnum; for (controlnum = 0; controlnum < snd_cards_limit; controlnum++) { sprintf(controlname, "snd/controlC%d", controlnum); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) - master = devfs_find_handle(NULL, controlname, strlen(controlname), 0, 0, DEVFS_SPECIAL_CHR, 0); - devfs_unregister(master); -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - master = devfs_find_handle(NULL, controlname, 0, 0, DEVFS_SPECIAL_CHR, 0); - devfs_unregister(master); -#else devfs_find_and_unregister(NULL, controlname, 0, 0, DEVFS_SPECIAL_CHR, 0); -#endif } #endif diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c index 5626a5519b07..7e2378eefeb8 100644 --- a/sound/isa/sgalaxy.c +++ b/sound/isa/sgalaxy.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,10 @@ static int __init snd_sgalaxy_sbdsp_command(unsigned long port, unsigned char va return 0; } +static void snd_sgalaxy_dummy_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +} + static int __init snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma) { static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, @@ -117,8 +122,6 @@ static int __init snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma) static int dma_bits[] = {1, 2, 0, 3}; int tmp, tmp1; - unsigned long flags; - if ((tmp = inb(port + 3)) == 0xff) { snd_printdd("I/O address dead (0x%lx)\n", port); @@ -140,20 +143,20 @@ static int __init snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma) snd_printdd("sgalaxy - setting up IRQ/DMA for WSS\n"); #endif - save_flags(flags); - cli(); - /* initialize IRQ for WSS codec */ tmp = interrupt_bits[irq % 16]; - if (tmp < 0) { - restore_flags(flags); + if (tmp < 0) return -EINVAL; - } + + if (request_irq(irq, snd_sgalaxy_dummy_interrupt, SA_INTERRUPT, "sgalaxy", NULL)) + return -EIO; + outb(tmp | 0x40, port); tmp1 = dma_bits[dma % 4]; outb(tmp | tmp1, port); - restore_flags(flags); + free_irq(irq, NULL); + return 0; } diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 67b4ffc6934e..a14cd743d913 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -284,6 +284,16 @@ static int prepare_capture_urb(snd_usb_substream_t *subs, urb->transfer_buffer = ctx->buf; urb->transfer_buffer_length = offs; urb->interval = 1; +#if 0 // for check + if (! urb->bandwidth) { + int bustime; + bustime = usb_check_bandwidth(urb->dev, urb); + 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); + } +#endif // for check return 0; } @@ -305,8 +315,10 @@ static int retire_capture_urb(snd_usb_substream_t *subs, for (i = 0; i < urb->number_of_packets; i++) { cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset; - if (urb->iso_frame_desc[i].status) /* active? hmm, skip this */ - continue; + if (urb->iso_frame_desc[i].status) { + snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status); + // continue; + } len = urb->iso_frame_desc[i].actual_length / stride; if (! len) continue; @@ -1009,6 +1021,7 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) } /* if endpoint has sampling rate control, set it */ if (fmt->attributes & EP_CS_ATTR_SAMPLE_RATE) { + int crate; data[0] = runtime->rate; data[1] = runtime->rate >> 8; data[2] = runtime->rate >> 16; @@ -1026,8 +1039,11 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) dev->devnum, subs->interface, fmt->altsetting, ep); return err; } - runtime->rate = data[0] | (data[1] << 8) | (data[2] << 16); - // printk("ok, getting back rate to %d\n", runtime->rate); + crate = data[0] | (data[1] << 8) | (data[2] << 16); + if (crate != runtime->rate) { + snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, runtime->rate); + // runtime->rate = crate; + } } /* always fill max packet size */ if (fmt->attributes & EP_CS_ATTR_FILL_MAX) @@ -1292,14 +1308,16 @@ void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype * entry point for linux usb interface */ -#ifndef OLD_USB +static void * _usb_audio_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id); +static void _usb_audio_disconnect(struct usb_device *dev, void *ptr); +#ifdef OLD_USB +#define usb_audio_probe _usb_audio_probe +#define usb_audio_disconnect _usb_audio_disconnect +#else static int usb_audio_probe(struct usb_interface *intf, const struct usb_device_id *id); static void usb_audio_disconnect(struct usb_interface *intf); -#else -static void * usb_audio_probe(usb_device *dev, unsigned int ifnum, - const struct usb_device_id *id); -static void usb_audio_disconnect(struct usb_device *dev, void *ptr); #endif static struct usb_device_id usb_audio_ids [] = { @@ -2050,18 +2068,9 @@ static int alloc_desc_buffer(struct usb_device *dev, int index, unsigned char ** * only at the first time. the successive calls of this function will * append the pcm interface to the corresponding card. */ -#ifndef OLD_USB -static int usb_audio_probe(struct usb_interface *intf, - const struct usb_device_id *id) -#else -static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum, +static void *_usb_audio_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) -#endif { -#ifndef OLD_USB - struct usb_device *dev = interface_to_usbdev(intf); - int ifnum = intf->altsetting->bInterfaceNumber; -#endif struct usb_config_descriptor *config = dev->actconfig; const snd_usb_audio_quirk_t *quirk = (const snd_usb_audio_quirk_t *)id->driver_info; unsigned char *buffer; @@ -2143,37 +2152,21 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum, chip->num_interfaces++; up(®ister_mutex); kfree(buffer); -#ifndef OLD_USB - return 0; -#else return chip; -#endif __error: up(®ister_mutex); kfree(buffer); __err_val: -#ifndef OLD_USB - return -EIO; -#else return NULL; -#endif } - /* * we need to take care of counter, since disconnection can be called also * many times as well as usb_audio_probe(). */ -#ifndef OLD_USB -static void usb_audio_disconnect(struct usb_interface *intf) -#else -static void usb_audio_disconnect(struct usb_device *dev, void *ptr) -#endif +static void _usb_audio_disconnect(struct usb_device *dev, void *ptr) { -#ifndef OLD_USB - void *ptr = dev_get_drvdata(&intf->dev); -#endif snd_usb_audio_t *chip; if (ptr == (void *)-1) @@ -2185,6 +2178,34 @@ static void usb_audio_disconnect(struct usb_device *dev, void *ptr) snd_card_free(chip->card); } + +#ifndef OLD_USB +/* + * new 2.5 USB kernel API + */ + +static int usb_audio_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + void *chip; + chip = _usb_audio_probe(interface_to_usbdev(intf), + intf->altsetting->bInterfaceNumber, id); + if (chip) { + dev_set_drvdata(&intf->dev, chip); + return 0; + } else + return -EIO; +} + +static void usb_audio_disconnect(struct usb_interface *intf) +{ + _usb_audio_disconnect(interface_to_usbdev(intf), + dev_get_drvdata(&intf->dev)); +} +#endif + + + static int __init snd_usb_audio_init(void) { usb_register(&usb_audio_driver); -- cgit v1.2.3 From 3900abd5bb7557e1f8463f6aa6981b1475ec89d2 Mon Sep 17 00:00:00 2001 From: Christer Weinigel Date: Fri, 4 Oct 2002 20:13:10 -0700 Subject: [PATCH] Updated NatSemi SCx200 patches for Linux-2.5 This patch adds support for the National Semiconductor SCx200 processor family to Linux 2.5. The patch consists of the following drivers: arch/i386/kernel/scx200.c -- give kernel access to the GPIO pins drivers/chars/scx200_gpio.c -- give userspace access to the GPIO pins drivers/chars/scx200_wdt.c -- watchdog timer driver drivers/i2c/scx200_i2c.c -- use any two GPIO pins as an I2C bus drivers/i2c/scx200_acb.c -- driver for the Access.BUS hardware drivers/mtd/maps/scx200_docflash.c -- driver for a CFI flash connected to the DOCCS pin --- MAINTAINERS | 6 + arch/i386/Config.help | 9 + arch/i386/config.in | 2 + arch/i386/kernel/Makefile | 3 + arch/i386/kernel/scx200.c | 128 ++++++++ drivers/char/Config.help | 12 + drivers/char/Config.in | 3 + drivers/char/Makefile | 2 + drivers/char/scx200_gpio.c | 154 ++++++++++ drivers/char/scx200_wdt.c | 277 ++++++++++++++++++ drivers/i2c/Config.help | 22 ++ drivers/i2c/Config.in | 6 + drivers/i2c/Makefile | 2 + drivers/i2c/scx200_acb.c | 578 +++++++++++++++++++++++++++++++++++++ drivers/i2c/scx200_i2c.c | 156 ++++++++++ drivers/mtd/maps/Config.help | 8 + drivers/mtd/maps/Config.in | 1 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/scx200_docflash.c | 268 +++++++++++++++++ include/linux/pci_ids.h | 6 + include/linux/scx200.h | 56 ++++ include/linux/scx200_gpio.h | 98 +++++++ 22 files changed, 1798 insertions(+) create mode 100644 arch/i386/kernel/scx200.c create mode 100644 drivers/char/scx200_gpio.c create mode 100644 drivers/char/scx200_wdt.c create mode 100644 drivers/i2c/scx200_acb.c create mode 100644 drivers/i2c/scx200_i2c.c create mode 100644 drivers/mtd/maps/scx200_docflash.c create mode 100644 include/linux/scx200.h create mode 100644 include/linux/scx200_gpio.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 3a9119b564ae..db808a97de6f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1433,6 +1433,12 @@ M: Kai.Makisara@metla.fi L: linux-scsi@vger.kernel.org S: Maintained +SCx200 CPU SUPPORT +P: Christer Weinigel +M: christer@weinigel.se +W: http://www.weinigel.se +S: Supported + SGI VISUAL WORKSTATION 320 AND 540 P: Bent Hagemark M: bh@sgi.com diff --git a/arch/i386/Config.help b/arch/i386/Config.help index 407fb1a2233c..cb2550c38a97 100644 --- a/arch/i386/Config.help +++ b/arch/i386/Config.help @@ -1058,3 +1058,12 @@ CONFIG_SOFTWARE_SUSPEND absence of features. For more information take a look at Documentation/swsusp.txt. + +CONFIG_SCx200 + This provides basic support for the National Semiconductor SCx200 + processor. Right now this is just a driver for the GPIO pins. + + If you don't know what to do here, say N. + + This support is also available as a module. If compiled as a + module, it will be called scx200.o. diff --git a/arch/i386/config.in b/arch/i386/config.in index 566971ae7bd6..d1efcd695fbd 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -293,6 +293,8 @@ else fi fi +tristate 'NatSemi SCx200 support' CONFIG_SCx200 + source drivers/pci/Config.in bool 'EISA support' CONFIG_EISA diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 735c11356773..3bdabc3f8f14 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -29,4 +29,7 @@ obj-$(CONFIG_X86_NUMAQ) += numaq.o EXTRA_AFLAGS := -traditional +export-objs += scx200.o +obj-$(CONFIG_SCx200) += scx200.o + include $(TOPDIR)/Rules.make diff --git a/arch/i386/kernel/scx200.c b/arch/i386/kernel/scx200.c new file mode 100644 index 000000000000..340e7f39a718 --- /dev/null +++ b/arch/i386/kernel/scx200.c @@ -0,0 +1,128 @@ +/* linux/arch/i386/kernel/scx200.c + + Copyright (c) 2001,2002 Christer Weinigel + + National Semiconductor SCx200 support. */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define NAME "scx200" + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 Driver"); +MODULE_LICENSE("GPL"); + +unsigned scx200_gpio_base = 0; +long scx200_gpio_shadow[2]; + +spinlock_t scx200_gpio_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t scx200_gpio_config_lock = SPIN_LOCK_UNLOCKED; + +u32 scx200_gpio_configure(int index, u32 mask, u32 bits) +{ + u32 config, new_config; + unsigned long flags; + + spin_lock_irqsave(&scx200_gpio_config_lock, flags); + + outl(index, scx200_gpio_base + 0x20); + config = inl(scx200_gpio_base + 0x24); + + new_config = (config & mask) | bits; + outl(new_config, scx200_gpio_base + 0x24); + + spin_unlock_irqrestore(&scx200_gpio_config_lock, flags); + + return config; +} + +void scx200_gpio_dump(unsigned index) +{ + u32 config = scx200_gpio_configure(index, ~0, 0); + printk(KERN_DEBUG "GPIO%02u: 0x%08lx", index, (unsigned long)config); + + if (config & 1) + printk(" OE"); /* output enabled */ + else + printk(" TS"); /* tristate */ + if (config & 2) + printk(" PP"); /* push pull */ + else + printk(" OD"); /* open drain */ + if (config & 4) + printk(" PUE"); /* pull up enabled */ + else + printk(" PUD"); /* pull up disabled */ + if (config & 8) + printk(" LOCKED"); /* locked */ + if (config & 16) + printk(" LEVEL"); /* level input */ + else + printk(" EDGE"); /* edge input */ + if (config & 32) + printk(" HI"); /* trigger on rising edge */ + else + printk(" LO"); /* trigger on falling edge */ + if (config & 64) + printk(" DEBOUNCE"); /* debounce */ + printk("\n"); +} + +int __init scx200_init(void) +{ + struct pci_dev *bridge; + int bank; + unsigned base; + + printk(KERN_INFO NAME ": NatSemi SCx200 Driver\n"); + + if ((bridge = pci_find_device(PCI_VENDOR_ID_NS, + PCI_DEVICE_ID_NS_SCx200_BRIDGE, + NULL)) == NULL) + return -ENODEV; + + base = pci_resource_start(bridge, 0); + printk(KERN_INFO NAME ": GPIO base 0x%x\n", base); + + if (request_region(base, SCx200_GPIO_SIZE, "NatSemi SCx200 GPIO") == 0) { + printk(KERN_ERR NAME ": can't allocate I/O for GPIOs\n"); + return -EBUSY; + } + + scx200_gpio_base = base; + + /* read the current values driven on the GPIO signals */ + for (bank = 0; bank < 2; ++bank) + scx200_gpio_shadow[bank] = inl(scx200_gpio_base + 0x10 * bank); + + return 0; +} + +void __exit scx200_cleanup(void) +{ + release_region(scx200_gpio_base, SCx200_GPIO_SIZE); +} + +module_init(scx200_init); +module_exit(scx200_cleanup); + +EXPORT_SYMBOL(scx200_gpio_base); +EXPORT_SYMBOL(scx200_gpio_shadow); +EXPORT_SYMBOL(scx200_gpio_lock); +EXPORT_SYMBOL(scx200_gpio_configure); +EXPORT_SYMBOL(scx200_gpio_dump); + +/* + Local variables: + compile-command: "make -k -C ../../.. SUBDIRS=arch/i386/kernel modules" + c-basic-offset: 8 + End: +*/ diff --git a/drivers/char/Config.help b/drivers/char/Config.help index c5272c99c910..e808a660614a 100644 --- a/drivers/char/Config.help +++ b/drivers/char/Config.help @@ -818,6 +818,12 @@ CONFIG_MIXCOMWD module, say M here and read . Most people will say N. +CONFIG_SCx200_WDT + Enable the built-in watchdog timer support on the National + Semiconductor SCx200 processors. + + If compiled as a module, it will be called scx200_watchdog.o. + CONFIG_MACHZ_WDT If you are using a ZF Micro MachZ processor, say Y here, otherwise N. This is the driver for the watchdog timer builtin on that @@ -1021,3 +1027,9 @@ CONFIG_RAW_DRIVER Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. See the raw(8) manpage for more details. +CONFIG_SCx200_GPIO + Give userspace access to the GPIO pins on the National + Semiconductor SCx200 processors. + + If compiled as a module, it will be called scx200_gpio.o. + diff --git a/drivers/char/Config.in b/drivers/char/Config.in index fec2100978dd..680bafe4ca4a 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -131,6 +131,7 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then tristate ' IB700 SBC Watchdog Timer' CONFIG_IB700_WDT tristate ' Intel i810 TCO timer / Watchdog' CONFIG_I810_TCO tristate ' Mixcom Watchdog' CONFIG_MIXCOMWD + tristate ' NatSemi SCx200 Watchdog' CONFIG_SCx200_WDT tristate ' SBC-60XX Watchdog Timer' CONFIG_60XX_WDT tristate ' W83877F (EMACS) Watchdog Timer' CONFIG_W83877F_WDT tristate ' ZF MachZ Watchdog' CONFIG_MACHZ_WDT @@ -193,6 +194,8 @@ if [ "$CONFIG_X86" = "y" ]; then tristate 'ACP Modem (Mwave) support' CONFIG_MWAVE fi +dep_tristate 'NatSemi SCx200 GPIO Support' CONFIG_SCx200_GPIO $CONFIG_SCx200 + tristate ' RAW driver (/dev/raw/rawN)' CONFIG_RAW_DRIVER endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f2dac15888bc..80a28ced908c 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_PPDEV) += ppdev.o obj-$(CONFIG_DZ) += dz.o obj-$(CONFIG_NWBUTTON) += nwbutton.o obj-$(CONFIG_NWFLASH) += nwflash.o +obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o # Only one watchdog can succeed. We probe the hardware watchdog # drivers first, then the softdog driver. This means if your hardware @@ -86,6 +87,7 @@ obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o obj-$(CONFIG_IB700_WDT) += ib700wdt.o obj-$(CONFIG_MIXCOMWD) += mixcomwd.o +obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o obj-$(CONFIG_WDT) += wdt.o obj-$(CONFIG_WDTPCI) += wdt_pci.o diff --git a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c new file mode 100644 index 000000000000..812bfb4bf50b --- /dev/null +++ b/drivers/char/scx200_gpio.c @@ -0,0 +1,154 @@ +/* linux/drivers/char/scx200_gpio.c + + National Semiconductor SCx200 GPIO driver. Allows a user space + process to play with the GPIO pins. + + Copyright (c) 2001,2002 Christer Weinigel */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NAME "scx200_gpio" + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 GPIO Pin Driver"); +MODULE_LICENSE("GPL"); + +static int major = 0; /* default to dynamic major */ +MODULE_PARM(major, "i"); +MODULE_PARM_DESC(major, "Major device number"); + +static ssize_t scx200_gpio_write(struct file *file, const char *data, + size_t len, loff_t *ppos) +{ + unsigned m = minor(file->f_dentry->d_inode->i_rdev); + size_t i; + + if (ppos != &file->f_pos) + return -ESPIPE; + + for (i = 0; i < len; ++i) { + char c; + if (get_user(c, data+i)) + return -EFAULT; + switch (c) + { + case '0': + scx200_gpio_set(m, 0); + break; + case '1': + scx200_gpio_set(m, 1); + break; + case 'O': + printk(KERN_INFO NAME ": GPIO%d output enabled\n", m); + scx200_gpio_configure(m, ~1, 1); + break; + case 'o': + printk(KERN_INFO NAME ": GPIO%d output disabled\n", m); + scx200_gpio_configure(m, ~1, 0); + break; + case 'T': + printk(KERN_INFO NAME ": GPIO%d output is push pull\n", m); + scx200_gpio_configure(m, ~2, 2); + break; + case 't': + printk(KERN_INFO NAME ": GPIO%d output is open drain\n", m); + scx200_gpio_configure(m, ~2, 0); + break; + case 'P': + printk(KERN_INFO NAME ": GPIO%d pull up enabled\n", m); + scx200_gpio_configure(m, ~4, 4); + break; + case 'p': + printk(KERN_INFO NAME ": GPIO%d pull up disabled\n", m); + scx200_gpio_configure(m, ~4, 0); + break; + } + } + + return len; +} + +static ssize_t scx200_gpio_read(struct file *file, char *buf, + size_t len, loff_t *ppos) +{ + unsigned m = minor(file->f_dentry->d_inode->i_rdev); + int value; + + if (ppos != &file->f_pos) + return -ESPIPE; + + value = scx200_gpio_get(m); + if (put_user(value ? '1' : '0', buf)) + return -EFAULT; + + return 1; +} + +static int scx200_gpio_open(struct inode *inode, struct file *file) +{ + unsigned m = minor(inode->i_rdev); + if (m > 63) + return -EINVAL; + return 0; +} + +static int scx200_gpio_release(struct inode *inode, struct file *file) +{ + return 0; +} + + +static struct file_operations scx200_gpio_fops = { + .owner = THIS_MODULE, + .write = scx200_gpio_write, + .read = scx200_gpio_read, + .open = scx200_gpio_open, + .release = scx200_gpio_release, +}; + +static int __init scx200_gpio_init(void) +{ + int r; + + printk(KERN_DEBUG NAME ": NatSemi SCx200 GPIO Driver\n"); + + if (!scx200_gpio_present()) { + printk(KERN_ERR NAME ": no SCx200 gpio pins available\n"); + return -ENODEV; + } + + r = register_chrdev(major, NAME, &scx200_gpio_fops); + if (r < 0) { + printk(KERN_ERR NAME ": unable to register character device\n"); + return r; + } + if (!major) { + major = r; + printk(KERN_DEBUG NAME ": got dynamic major %d\n", major); + } + + return 0; +} + +static void __exit scx200_gpio_cleanup(void) +{ + unregister_chrdev(major, NAME); +} + +module_init(scx200_gpio_init); +module_exit(scx200_gpio_cleanup); + +/* + Local variables: + compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules" + c-basic-offset: 8 + End: +*/ diff --git a/drivers/char/scx200_wdt.c b/drivers/char/scx200_wdt.c new file mode 100644 index 000000000000..c02aa8b609e2 --- /dev/null +++ b/drivers/char/scx200_wdt.c @@ -0,0 +1,277 @@ +/* linux/drivers/char/scx200_wdt.c + + National Semiconductor SCx200 Watchdog support + + Copyright (c) 2001,2002 Christer Weinigel + + Som code taken from: + National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver + (c) Copyright 2002 Zwane Mwaikambo + + 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. + + The author(s) of this software shall not be held liable for damages + of any nature resulting due to the use of this software. This + software is provided AS-IS with no warranties. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NAME "scx200_wdt" + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver"); +MODULE_LICENSE("GPL"); + +#ifndef CONFIG_WATCHDOG_NOWAYOUT +#define CONFIG_WATCHDOG_NOWAYOUT 0 +#endif + +static int margin = 60; /* in seconds */ +MODULE_PARM(margin, "i"); +MODULE_PARM_DESC(margin, "Watchdog margin in seconds"); + +static int nowayout = CONFIG_WATCHDOG_NOWAYOUT; +MODULE_PARM(nowayout, "i"); +MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); + +static u16 wdto_restart; +static struct semaphore open_semaphore; +static unsigned expect_close; + +/* Bits of the WDCNFG register */ +#define W_ENABLE 0x00fa /* Enable watchdog */ +#define W_DISABLE 0x0000 /* Disable watchdog */ + +/* The scaling factor for the timer, this depends on the value of W_ENABLE */ +#define W_SCALE (32768/1024) + +static void scx200_wdt_ping(void) +{ + outw(wdto_restart, SCx200_CB_BASE + SCx200_WDT_WDTO); +} + +static void scx200_wdt_update_margin(void) +{ + printk(KERN_INFO NAME ": timer margin %d seconds\n", margin); + wdto_restart = margin * W_SCALE; +} + +static void scx200_wdt_enable(void) +{ + printk(KERN_DEBUG NAME ": enabling watchdog timer, wdto_restart = %d\n", + wdto_restart); + + outw(0, SCx200_CB_BASE + SCx200_WDT_WDTO); + outb(SCx200_WDT_WDSTS_WDOVF, SCx200_CB_BASE + SCx200_WDT_WDSTS); + outw(W_ENABLE, SCx200_CB_BASE + SCx200_WDT_WDCNFG); + + scx200_wdt_ping(); +} + +static void scx200_wdt_disable(void) +{ + printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); + + outw(0, SCx200_CB_BASE + SCx200_WDT_WDTO); + outb(SCx200_WDT_WDSTS_WDOVF, SCx200_CB_BASE + SCx200_WDT_WDSTS); + outw(W_DISABLE, SCx200_CB_BASE + SCx200_WDT_WDCNFG); +} + +static int scx200_wdt_open(struct inode *inode, struct file *file) +{ + /* only allow one at a time */ + if (down_trylock(&open_semaphore)) + return -EBUSY; + scx200_wdt_enable(); + expect_close = 0; + + return 0; +} + +static int scx200_wdt_release(struct inode *inode, struct file *file) +{ + if (!expect_close) { + printk(KERN_WARNING NAME ": watchdog device closed unexpectedly, will not disable the watchdog timer\n"); + } else if (!nowayout) { + scx200_wdt_disable(); + } + up(&open_semaphore); + + return 0; +} + +static int scx200_wdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_HALT || code == SYS_POWER_OFF) + if (!nowayout) + scx200_wdt_disable(); + + return NOTIFY_DONE; +} + +static struct notifier_block scx200_wdt_notifier = +{ + notifier_call: scx200_wdt_notify_sys +}; + +static ssize_t scx200_wdt_write(struct file *file, const char *data, + size_t len, loff_t *ppos) +{ + if (ppos != &file->f_pos) + return -ESPIPE; + + /* check for a magic close character */ + if (len) + { + size_t i; + + scx200_wdt_ping(); + + expect_close = 0; + for (i = 0; i < len; ++i) { + char c; + if (get_user(c, data+i)) + return -EFAULT; + if (c == 'V') + expect_close = 1; + } + + return len; + } + + return 0; +} + +static int scx200_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = { + .identity = "NatSemi SCx200 Watchdog", + .firmware_version = 1, + .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING), + }; + int new_margin; + + switch (cmd) { + default: + return -ENOTTY; + case WDIOC_GETSUPPORT: + if(copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident))) + return -EFAULT; + return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + if (put_user(0, (int *)arg)) + return -EFAULT; + return 0; + case WDIOC_KEEPALIVE: + scx200_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int *)arg)) + return -EFAULT; + if (new_margin < 1) + return -EINVAL; + margin = new_margin; + scx200_wdt_update_margin(); + scx200_wdt_ping(); + case WDIOC_GETTIMEOUT: + if (put_user(margin, (int *)arg)) + return -EFAULT; + return 0; + } +} + +static struct file_operations scx200_wdt_fops = { + .owner = THIS_MODULE, + .write = scx200_wdt_write, + .ioctl = scx200_wdt_ioctl, + .open = scx200_wdt_open, + .release = scx200_wdt_release, +}; + +static struct miscdevice scx200_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = NAME, + .fops = &scx200_wdt_fops, +}; + +static int __init scx200_wdt_init(void) +{ + int r; + + printk(KERN_DEBUG NAME ": NatSemi SCx200 Watchdog Driver\n"); + + /* First check that this really is a NatSemi SCx200 CPU */ + if ((pci_find_device(PCI_VENDOR_ID_NS, + PCI_DEVICE_ID_NS_SCx200_BRIDGE, + NULL)) == NULL) + return -ENODEV; + + /* More sanity checks, verify that the configuration block is there */ + if (!scx200_cb_probe(SCx200_CB_BASE)) { + printk(KERN_WARNING NAME ": no configuration block found\n"); + return -ENODEV; + } + + if (!request_region(SCx200_CB_BASE + SCx200_WDT_OFFSET, + SCx200_WDT_SIZE, + "NatSemi SCx200 Watchdog")) { + printk(KERN_WARNING NAME ": watchdog I/O region busy\n"); + return -EBUSY; + } + + scx200_wdt_update_margin(); + scx200_wdt_disable(); + + sema_init(&open_semaphore, 1); + + r = misc_register(&scx200_wdt_miscdev); + if (r) + return r; + + r = register_reboot_notifier(&scx200_wdt_notifier); + if (r) { + printk(KERN_ERR NAME ": unable to register reboot notifier"); + misc_deregister(&scx200_wdt_miscdev); + return r; + } + + return 0; +} + +static void __exit scx200_wdt_cleanup(void) +{ + unregister_reboot_notifier(&scx200_wdt_notifier); + misc_deregister(&scx200_wdt_miscdev); + release_region(SCx200_CB_BASE + SCx200_WDT_OFFSET, + SCx200_WDT_SIZE); +} + +module_init(scx200_wdt_init); +module_exit(scx200_wdt_cleanup); + +/* + Local variables: + compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules" + c-basic-offset: 8 + End: +*/ diff --git a/drivers/i2c/Config.help b/drivers/i2c/Config.help index 371162eb2ad5..b21fce5bd96a 100644 --- a/drivers/i2c/Config.help +++ b/drivers/i2c/Config.help @@ -91,6 +91,28 @@ CONFIG_I2C_CHARDEV . The module will be called i2c-dev.o. +CONFIG_SCx200_I2C + Enable the use of two GPIO pins of a SCx200 processor as an I2C bus. + + If you don't know what to do here, say N. + + If compiled as a module, it will be called scx200_i2c.o. + +CONFIG_SCx200_I2C_SCL + Enter the GPIO pin number used for the SCL signal. This value can + also be specified with a module parameter. + +CONFIG_SCx200_I2C_SDA + Enter the GPIO pin number used for the SSA signal. This value can + also be specified with a module parameter. + +CONFIG_SCx200_ACB + Enable the use of the ACCESS.bus controllers of a SCx200 processor. + + If you don't know what to do here, say N. + + If compiled as a module, it will be called scx200_acb.o. + CONFIG_I2C_PROC This provides support for i2c device entries in the /proc filesystem. The entries will be found in /proc/sys/dev/sensors. diff --git a/drivers/i2c/Config.in b/drivers/i2c/Config.in index af9fd0f1f444..6054a16568bc 100644 --- a/drivers/i2c/Config.in +++ b/drivers/i2c/Config.in @@ -13,6 +13,12 @@ if [ "$CONFIG_I2C" != "n" ]; then dep_tristate ' Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT $CONFIG_PARPORT dep_tristate ' ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT dep_tristate ' Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT + dep_tristate ' NatSemi SCx200 I2C using GPIO pins' CONFIG_SCx200_I2C $CONFIG_SCx200 $CONFIG_I2C_ALGOBIT + if [ "$CONFIG_SCx200_I2C" != "n" ]; then + int ' GPIO pin used for SCL' CONFIG_SCx200_I2C_SCL 12 + int ' GPIO pin used for SDA' CONFIG_SCx200_I2C_SDA 13 + fi + dep_tristate ' NatSemi SCx200 ACCESS.bus' CONFIG_SCx200_ACB $CONFIG_I2C fi dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 33ef7be099e9..56c6474c5611 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -15,6 +15,8 @@ obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o obj-$(CONFIG_ITE_I2C_ADAP) += i2c-adap-ite.o +obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o +obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_I2C_PROC) += i2c-proc.o # This is needed for automatic patch generation: sensors code starts here diff --git a/drivers/i2c/scx200_acb.c b/drivers/i2c/scx200_acb.c new file mode 100644 index 000000000000..1b29f806a830 --- /dev/null +++ b/drivers/i2c/scx200_acb.c @@ -0,0 +1,578 @@ +/* linux/drivers/i2c/scx200_acb.c + + Copyright (c) 2001,2002 Christer Weinigel + + National Semiconductor SCx200 ACCESS.bus support + + Based on i2c-keywest.c which is: + Copyright (c) 2001 Benjamin Herrenschmidt + Copyright (c) 2000 Philip Edelbrock + + 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. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NAME "scx200_acb" + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 ACCESS.bus Driver"); +MODULE_LICENSE("GPL"); + +#define MAX_DEVICES 4 +static int base[MAX_DEVICES] = { 0x840 }; +MODULE_PARM(base, "1-4i"); +MODULE_PARM_DESC(base, "Base addresses for the ACCESS.bus controllers"); + +#define DEBUG 0 + +#if DEBUG +#define DBG(x...) printk(KERN_DEBUG NAME ": " x) +#else +#define DBG(x...) +#endif + +/* The hardware supports interrupt driven mode too, but I haven't + implemented that. */ +#define POLLED_MODE 1 +#define POLL_TIMEOUT (HZ) + +enum scx200_acb_state { + state_idle, + state_address, + state_command, + state_repeat_start, + state_quick, + state_read, + state_write, +}; + +static const char *scx200_acb_state_name[] = { + "idle", + "address", + "command", + "repeat_start", + "quick", + "read", + "write", +}; + +/* Physical interface */ +struct scx200_acb_iface +{ + struct scx200_acb_iface *next; + struct i2c_adapter adapter; + unsigned base; + struct semaphore sem; + + /* State machine data */ + enum scx200_acb_state state; + int result; + u8 address_byte; + u8 command; + u8 *ptr; + char needs_reset; + unsigned len; +}; + +/* Register Definitions */ +#define ACBSDA (iface->base + 0) +#define ACBST (iface->base + 1) +#define ACBST_SDAST 0x40 /* SDA Status */ +#define ACBST_BER 0x20 +#define ACBST_NEGACK 0x10 /* Negative Acknowledge */ +#define ACBST_STASTR 0x08 /* Stall After Start */ +#define ACBST_MASTER 0x02 +#define ACBCST (iface->base + 2) +#define ACBCST_BB 0x02 +#define ACBCTL1 (iface->base + 3) +#define ACBCTL1_STASTRE 0x80 +#define ACBCTL1_NMINTE 0x40 +#define ACBCTL1_ACK 0x10 +#define ACBCTL1_STOP 0x02 +#define ACBCTL1_START 0x01 +#define ACBADDR (iface->base + 4) +#define ACBCTL2 (iface->base + 5) +#define ACBCTL2_ENABLE 0x01 + +/************************************************************************/ + +static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status) +{ + const char *errmsg; + + DBG("state %s, status = 0x%02x\n", + scx200_acb_state_name[iface->state], status); + + if (status & ACBST_BER) { + errmsg = "bus error"; + goto error; + } + if (!(status & ACBST_MASTER)) { + errmsg = "not master"; + goto error; + } + if (status & ACBST_NEGACK) + goto negack; + + switch (iface->state) { + case state_idle: + printk(KERN_WARNING NAME ": %s, interrupt in idle state\n", + iface->adapter.name); + break; + + case state_address: + /* Do a pointer write first */ + outb(iface->address_byte & ~1, ACBSDA); + + iface->state = state_command; + break; + + case state_command: + outb(iface->command, ACBSDA); + + if (iface->address_byte & 1) + iface->state = state_repeat_start; + else + iface->state = state_write; + break; + + case state_repeat_start: + outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1); + /* fallthrough */ + + case state_quick: + if (iface->address_byte & 1) { + if (iface->len == 1) + outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1); + else + outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1); + outb(iface->address_byte, ACBSDA); + + iface->state = state_read; + } else { + outb(iface->address_byte, ACBSDA); + + iface->state = state_write; + } + break; + + case state_read: + /* Set ACK if receiving the last byte */ + if (iface->len == 1) + outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1); + else + outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1); + + *iface->ptr++ = inb(ACBSDA); + --iface->len; + + if (iface->len == 0) { + iface->result = 0; + iface->state = state_idle; + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + } + + break; + + case state_write: + if (iface->len == 0) { + iface->result = 0; + iface->state = state_idle; + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + break; + } + + outb(*iface->ptr++, ACBSDA); + --iface->len; + + break; + } + + return; + + negack: + DBG("negative acknowledge in state %s\n", + scx200_acb_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -ENXIO; + + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + outb(ACBST_STASTR | ACBST_NEGACK, ACBST); + return; + + error: + printk(KERN_ERR NAME ": %s, %s in state %s\n", iface->adapter.name, + errmsg, scx200_acb_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -EIO; + iface->needs_reset = 1; +} + +static void scx200_acb_timeout(struct scx200_acb_iface *iface) +{ + printk(KERN_ERR NAME ": %s, timeout in state %s\n", + iface->adapter.name, scx200_acb_state_name[iface->state]); + + iface->state = state_idle; + iface->result = -EIO; + iface->needs_reset = 1; +} + +#ifdef POLLED_MODE +static void scx200_acb_poll(struct scx200_acb_iface *iface) +{ + u8 status = 0; + unsigned long timeout; + + timeout = jiffies + POLL_TIMEOUT; + while (time_before(jiffies, timeout)) { + status = inb(ACBST); + if ((status & (ACBST_SDAST|ACBST_BER|ACBST_NEGACK)) != 0) { + scx200_acb_machine(iface, status); + return; + } + schedule_timeout(HZ/100+1); + } + + scx200_acb_timeout(iface); +} +#endif /* POLLED_MODE */ + +static void scx200_acb_reset(struct scx200_acb_iface *iface) +{ + /* Disable the ACCESS.bus device and Configure the SCL + frequency: 16 clock cycles */ + outb(0x70, ACBCTL2); + /* Polling mode */ + outb(0, ACBCTL1); + /* Disable slave address */ + outb(0, ACBADDR); + /* Enable the ACCESS.bus device */ + outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2); + /* Free STALL after START */ + outb(inb(ACBCTL1) & ~(ACBCTL1_STASTRE | ACBCTL1_NMINTE), ACBCTL1); + /* Send a STOP */ + outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1); + /* Clear BER, NEGACK and STASTR bits */ + outb(ACBST_BER | ACBST_NEGACK | ACBST_STASTR, ACBST); + /* Clear BB bit */ + outb(inb(ACBCST) | ACBCST_BB, ACBCST); +} + +static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter, + u16 address, unsigned short flags, + char rw, u8 command, int size, + union i2c_smbus_data *data) +{ + struct scx200_acb_iface *iface = adapter->data; + int len; + u8 *buffer; + u16 cur_word; + int rc; + + switch (size) { + case I2C_SMBUS_QUICK: + len = 0; + buffer = NULL; + break; + case I2C_SMBUS_BYTE: + if (rw == I2C_SMBUS_READ) { + len = 1; + buffer = &data->byte; + } else { + len = 1; + buffer = &command; + } + break; + case I2C_SMBUS_BYTE_DATA: + len = 1; + buffer = &data->byte; + break; + case I2C_SMBUS_WORD_DATA: + len = 2; + cur_word = cpu_to_le16(data->word); + buffer = (u8 *)&cur_word; + break; + case I2C_SMBUS_BLOCK_DATA: + len = data->block[0]; + buffer = &data->block[1]; + break; + default: + return -EINVAL; + } + + DBG("size=%d, address=0x%x, command=0x%x, len=%d, read=%d\n", + size, address, command, len, rw == I2C_SMBUS_READ); + + if (!len && rw == I2C_SMBUS_READ) { + printk(KERN_WARNING NAME ": %s, zero length read\n", + adapter->name); + return -EINVAL; + } + + if (len && !buffer) { + printk(KERN_WARNING NAME ": %s, nonzero length but no buffer\n", adapter->name); + return -EFAULT; + } + + down(&iface->sem); + + iface->address_byte = address<<1; + if (rw == I2C_SMBUS_READ) + iface->address_byte |= 1; + iface->command = command; + iface->ptr = buffer; + iface->len = len; + iface->result = -EINVAL; + iface->needs_reset = 0; + + outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1); + + if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE) + iface->state = state_quick; + else + iface->state = state_address; + +#ifdef POLLED_MODE + while (iface->state != state_idle) + scx200_acb_poll(iface); +#else /* POLLED_MODE */ +#error Interrupt driven mode not implemented +#endif /* POLLED_MODE */ + + if (iface->needs_reset) + scx200_acb_reset(iface); + + rc = iface->result; + + up(&iface->sem); + + if (rc == 0 && size == I2C_SMBUS_WORD_DATA && rw == I2C_SMBUS_READ) + data->word = le16_to_cpu(cur_word); + +#if DEBUG + printk(KERN_DEBUG NAME ": transfer done, result: %d", rc); + if (buffer) { + int i; + printk(" data:"); + for (i = 0; i < len; ++i) + printk(" %02x", buffer[i]); + } + printk("\n"); +#endif + + return rc; +} + +static u32 scx200_acb_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static int scx200_acb_reg(struct i2c_client *client) +{ + return 0; +} + +static int scx200_acb_unreg(struct i2c_client *client) +{ + return 0; +} + +static void scx200_acb_inc_use(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +static void scx200_acb_dec_use(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +/* For now, we only handle combined mode (smbus) */ +static struct i2c_algorithm scx200_acb_algorithm = { + name: "NatSemi SCx200 ACCESS.bus", + id: I2C_ALGO_SMBUS, + smbus_xfer: scx200_acb_smbus_xfer, + functionality: scx200_acb_func, +}; + +struct scx200_acb_iface *scx200_acb_list; + +int scx200_acb_probe(struct scx200_acb_iface *iface) +{ + u8 val; + + /* Disable the ACCESS.bus device and Configure the SCL + frequency: 16 clock cycles */ + outb(0x70, ACBCTL2); + + if (inb(ACBCTL2) != 0x70) { + DBG("ACBCTL2 readback failed\n"); + return -ENXIO; + } + + outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1); + + val = inb(ACBCTL1); + if (val) { + DBG("disabled, but ACBCTL1=0x%02x\n", val); + return -ENXIO; + } + + outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2); + + outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1); + + val = inb(ACBCTL1); + if ((val & ACBCTL1_NMINTE) != ACBCTL1_NMINTE) { + DBG("enabled, but NMINTE won't be set, ACBCTL1=0x%02x\n", val); + return -ENXIO; + } + + return 0; +} + +static int __init scx200_acb_create(int base, int index) +{ + struct scx200_acb_iface *iface; + struct i2c_adapter *adapter; + int rc = 0; + char description[64]; + + iface = kmalloc(sizeof(*iface), GFP_KERNEL); + if (!iface) { + printk(KERN_ERR NAME ": can't allocate memory\n"); + rc = -ENOMEM; + goto errout; + } + + memset(iface, 0, sizeof(*iface)); + adapter = &iface->adapter; + adapter->data = iface; + sprintf(adapter->name, "SCx200 ACB%d", index); + adapter->id = I2C_ALGO_SMBUS; + adapter->algo = &scx200_acb_algorithm; + adapter->inc_use = scx200_acb_inc_use; + adapter->dec_use = scx200_acb_dec_use; + adapter->client_register = scx200_acb_reg; + adapter->client_unregister = scx200_acb_unreg; + + init_MUTEX(&iface->sem); + + sprintf(description, "NatSemi SCx200 ACCESS.bus [%s]", adapter->name); + if (request_region(base, 8, description) == 0) { + printk(KERN_ERR NAME ": %s, can't allocate io 0x%x-0x%x\n", + adapter->name, base, base + 8-1); + rc = -EBUSY; + goto errout; + } + iface->base = base; + + rc = scx200_acb_probe(iface); + if (rc) { + printk(KERN_WARNING NAME ": %s, probe failed\n", adapter->name); + goto errout; + } + + scx200_acb_reset(iface); + + if (i2c_add_adapter(adapter) < 0) { + printk(KERN_ERR NAME ": %s, failed to register\n", adapter->name); + rc = -ENODEV; + goto errout; + } + + lock_kernel(); + iface->next = scx200_acb_list; + scx200_acb_list = iface; + unlock_kernel(); + + return 0; + + errout: + if (iface) { + if (iface->base) + release_region(iface->base, 8); + kfree(iface); + } + return rc; +} + +static int __init scx200_acb_init(void) +{ + int i; + int rc; + + printk(KERN_DEBUG NAME ": NatSemi SCx200 ACCESS.bus Driver\n"); + + /* Verify that this really is a SCx200 processor */ + if (pci_find_device(PCI_VENDOR_ID_NS, + PCI_DEVICE_ID_NS_SCx200_BRIDGE, + NULL) == NULL) + return -ENODEV; + + rc = -ENXIO; + for (i = 0; i < MAX_DEVICES; ++i) { + if (base[i] > 0) + rc = scx200_acb_create(base[i], i); + } + if (scx200_acb_list) + return 0; + return rc; +} + +static void __exit scx200_acb_cleanup(void) +{ + struct scx200_acb_iface *iface; + lock_kernel(); + while ((iface = scx200_acb_list) != NULL) { + scx200_acb_list = iface->next; + unlock_kernel(); + + i2c_del_adapter(&iface->adapter); + release_region(iface->base, 8); + kfree(iface); + lock_kernel(); + } + unlock_kernel(); +} + +module_init(scx200_acb_init); +module_exit(scx200_acb_cleanup); + +/* + Local variables: + compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules" + c-basic-offset: 8 + End: +*/ + diff --git a/drivers/i2c/scx200_i2c.c b/drivers/i2c/scx200_i2c.c new file mode 100644 index 000000000000..515e0c73d523 --- /dev/null +++ b/drivers/i2c/scx200_i2c.c @@ -0,0 +1,156 @@ +/* linux/drivers/i2c/scx200_i2c.c + + Copyright (c) 2001,2002 Christer Weinigel + + National Semiconductor SCx200 I2C bus on GPIO pins + + Based on i2c-velleman.c Copyright (C) 1995-96, 2000 Simon G. Vogl + + 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define NAME "scx200_i2c" + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 I2C Driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(scl, "i"); +MODULE_PARM_DESC(scl, "GPIO line for SCL"); +MODULE_PARM(sda, "i"); +MODULE_PARM_DESC(sda, "GPIO line for SDA"); + +static int scl = CONFIG_SCx200_I2C_SCL; +static int sda = CONFIG_SCx200_I2C_SDA; + +static void scx200_i2c_setscl(void *data, int state) +{ + scx200_gpio_set(scl, state); +} + +static void scx200_i2c_setsda(void *data, int state) +{ + scx200_gpio_set(sda, state); +} + +static int scx200_i2c_getscl(void *data) +{ + return scx200_gpio_get(scl); +} + +static int scx200_i2c_getsda(void *data) +{ + return scx200_gpio_get(sda); +} + +static int scx200_i2c_reg(struct i2c_client *client) +{ + return 0; +} + +static int scx200_i2c_unreg(struct i2c_client *client) +{ + return 0; +} + +static void scx200_i2c_inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void scx200_i2c_dec_use(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + +static struct i2c_algo_bit_data scx200_i2c_data = { + NULL, + scx200_i2c_setsda, + scx200_i2c_setscl, + scx200_i2c_getsda, + scx200_i2c_getscl, + 10, 10, 100, /* waits, timeout */ +}; + +static struct i2c_adapter scx200_i2c_ops = { + .name = "NatSemi SCx200 I2C", + .id = I2C_HW_B_VELLE, + .algo_data = &scx200_i2c_data, + .inc_use = scx200_i2c_inc_use, + .dec_use = scx200_i2c_dec_use, + .client_register = scx200_i2c_reg, + .client_unregister = scx200_i2c_unreg, +}; + +int scx200_i2c_init(void) +{ + printk(KERN_DEBUG NAME ": NatSemi SCx200 I2C Driver\n"); + + if (!scx200_gpio_present()) { + printk(KERN_ERR NAME ": no SCx200 gpio pins available\n"); + return -ENODEV; + } + + printk(KERN_DEBUG NAME ": SCL=GPIO%02u, SDA=GPIO%02u\n", + scl, sda); + + if (scl == -1 || sda == -1 || scl == sda) { + printk(KERN_ERR NAME ": scl and sda must be specified\n"); + return -EINVAL; + } + + /* Configure GPIOs as open collector outputs */ + scx200_gpio_configure(scl, ~2, 5); + scx200_gpio_configure(sda, ~2, 5); + + if (i2c_bit_add_bus(&scx200_i2c_ops) < 0) { + printk(KERN_ERR NAME ": adapter %s registration failed\n", + scx200_i2c_ops.name); + return -ENODEV; + } + + return 0; +} + +void scx200_i2c_cleanup(void) +{ + i2c_bit_del_bus(&scx200_i2c_ops); +} + +module_init(scx200_i2c_init); +module_exit(scx200_i2c_cleanup); + +/* + Local variables: + compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules" + c-basic-offset: 8 + End: +*/ diff --git a/drivers/mtd/maps/Config.help b/drivers/mtd/maps/Config.help index bee634915694..aaf3a1aa894e 100644 --- a/drivers/mtd/maps/Config.help +++ b/drivers/mtd/maps/Config.help @@ -127,6 +127,14 @@ CONFIG_MTD_MIXMEM you probably want to enable this mapping driver. More info is at . +CONFIG_MTD_SCx200_DOCFLASH + Enable support for a flash chip mapped using the DOCCS signal on a + National Semiconductor SCx200 processor. + + If you don't know what to do here, say N. + + If compiled as a module, it will be called scx200_docflash.o. + CONFIG_MTD_OCTAGON This provides a 'mapping' driver which supports the way in which the flash chips are connected in the Octagon-5066 Single Board diff --git a/drivers/mtd/maps/Config.in b/drivers/mtd/maps/Config.in index dc9b5069cc4e..7b4cbd4eda85 100644 --- a/drivers/mtd/maps/Config.in +++ b/drivers/mtd/maps/Config.in @@ -26,6 +26,7 @@ if [ "$CONFIG_X86" = "y" ]; then dep_tristate ' JEDEC Flash device mapped on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC + dep_tristate ' Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_MTD_CFI dep_tristate ' BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_MTD_JEDEC fi diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index d6cd7af1b203..c0bdc2fa8f23 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_NETSC520) += netsc520.o obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o obj-$(CONFIG_MTD_VMAX) += vmax301.o +obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o obj-$(CONFIG_MTD_OCELOT) += ocelot.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c new file mode 100644 index 000000000000..64583df88707 --- /dev/null +++ b/drivers/mtd/maps/scx200_docflash.c @@ -0,0 +1,268 @@ +/* linux/drivers/mtd/maps/scx200_docflash.c + + Copyright (c) 2001,2002 Christer Weinigel + + National Semiconductor SCx200 flash mapped with DOCCS +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define NAME "scx200_docflash" + +MODULE_AUTHOR("Christer Weinigel "); +MODULE_DESCRIPTION("NatSemi SCx200 DOCCS Flash Driver"); +MODULE_LICENSE("GPL"); + +/* Set this to one if you want to partition the flash */ +#define PARTITION 1 + +MODULE_PARM(probe, "i"); +MODULE_PARM_DESC(probe, "Probe for a BIOS mapping"); +MODULE_PARM(size, "i"); +MODULE_PARM_DESC(size, "Size of the flash mapping"); +MODULE_PARM(width, "i"); +MODULE_PARM_DESC(width, "Data width of the flash mapping (8/16)"); +MODULE_PARM(flashtype, "s"); +MODULE_PARM_DESC(flashtype, "Type of MTD probe to do"); + +static int probe = 0; /* Don't autoprobe */ +static unsigned size = 0x1000000; /* 16 MB the whole ISA address space */ +static unsigned width = 8; /* Default to 8 bits wide */ +static char *flashtype = "cfi_probe"; + +static struct resource docmem = { + .flags = IORESOURCE_MEM, + .name = "NatSemi SCx200 DOCCS Flash", +}; + +static struct mtd_info *mymtd; + +#if PARTITION +static struct mtd_partition partition_info[] = { + { + .name = "DOCCS Boot kernel", + .offset = 0, + .size = 0xc0000 + }, + { + .name = "DOCCS Low BIOS", + .offset = 0xc0000, + .size = 0x40000 + }, + { + .name = "DOCCS File system", + .offset = 0x100000, + .size = ~0 /* calculate from flash size */ + }, + { + .name = "DOCCS High BIOS", + .offset = ~0, /* calculate from flash size */ + .size = 0x80000 + }, +}; +#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) +#endif + +static __u8 scx200_docflash_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->map_priv_1 + ofs); +} + +static __u16 scx200_docflash_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->map_priv_1 + ofs); +} + +static void scx200_docflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +static void scx200_docflash_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +static void scx200_docflash_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); + mb(); +} + +static void scx200_docflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + +static struct map_info scx200_docflash_map = { + .name = "NatSemi SCx200 DOCCS Flash", + .read8 = scx200_docflash_read8, + .read16 = scx200_docflash_read16, + .copy_from = scx200_docflash_copy_from, + .write8 = scx200_docflash_write8, + .write16 = scx200_docflash_write16, + .copy_to = scx200_docflash_copy_to +}; + +int __init init_scx200_docflash(void) +{ + unsigned u; + unsigned base; + unsigned ctrl; + unsigned pmr; + struct pci_dev *bridge; + + printk(KERN_DEBUG NAME ": NatSemi SCx200 DOCCS Flash Driver\n"); + + if ((bridge = pci_find_device(PCI_VENDOR_ID_NS, + PCI_DEVICE_ID_NS_SCx200_BRIDGE, + NULL)) == NULL) + return -ENODEV; + + if (!scx200_cb_probe(SCx200_CB_BASE)) { + printk(KERN_WARNING NAME ": no configuration block found\n"); + return -ENODEV; + } + + if (probe) { + /* Try to use the present flash mapping if any */ + pci_read_config_dword(bridge, SCx200_DOCCS_BASE, &base); + pci_read_config_dword(bridge, SCx200_DOCCS_CTRL, &ctrl); + pmr = inl(SCx200_CB_BASE + SCx200_PMR); + + if (base == 0 + || (ctrl & 0x07000000) != 0x07000000 + || (ctrl & 0x0007ffff) == 0) + return -ENODEV; + + size = ((ctrl&0x1fff)<<13) + (1<<13); + + for (u = size; u > 1; u >>= 1) + ; + if (u != 1) + return -ENODEV; + + if (pmr & (1<<6)) + width = 16; + else + width = 8; + + docmem.start = base; + docmem.end = base + size; + + if (request_resource(&iomem_resource, &docmem)) { + printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n"); + return -ENOMEM; + } + } else { + for (u = size; u > 1; u >>= 1) + ; + if (u != 1) { + printk(KERN_ERR NAME ": invalid size for flash mapping\n"); + return -EINVAL; + } + + if (width != 8 && width != 16) { + printk(KERN_ERR NAME ": invalid bus width for flash mapping\n"); + return -EINVAL; + } + + if (allocate_resource(&iomem_resource, &docmem, + size, + 0xc0000000, 0xffffffff, + size, NULL, NULL)) { + printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n"); + return -ENOMEM; + } + + ctrl = 0x07000000 | ((size-1) >> 13); + + printk(KERN_INFO "DOCCS BASE=0x%08lx, CTRL=0x%08lx\n", (long)docmem.start, (long)ctrl); + + pci_write_config_dword(bridge, SCx200_DOCCS_BASE, docmem.start); + pci_write_config_dword(bridge, SCx200_DOCCS_CTRL, ctrl); + pmr = inl(SCx200_CB_BASE + SCx200_PMR); + + if (width == 8) { + pmr &= ~(1<<6); + } else { + pmr |= (1<<6); + } + outl(pmr, SCx200_CB_BASE + SCx200_PMR); + } + + printk(KERN_INFO NAME ": DOCCS mapped at 0x%lx-0x%lx, width %d\n", + docmem.start, docmem.end, width); + + scx200_docflash_map.size = size; + if (width == 8) + scx200_docflash_map.buswidth = 1; + else + scx200_docflash_map.buswidth = 2; + + scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size); + if (!scx200_docflash_map.map_priv_1) { + printk(KERN_ERR NAME ": failed to ioremap the flash\n"); + release_resource(&docmem); + return -EIO; + } + + mymtd = do_map_probe(flashtype, &scx200_docflash_map); + if (!mymtd) { + printk(KERN_ERR NAME ": unable to detect flash\n"); + iounmap((void *)scx200_docflash_map.map_priv_1); + release_resource(&docmem); + return -ENXIO; + } + + if (size < mymtd->size) + printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n"); + + mymtd->module = THIS_MODULE; + +#if PARTITION + partition_info[3].offset = mymtd->size-partition_info[3].size; + partition_info[2].size = partition_info[3].offset-partition_info[2].offset; + add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); +#else + add_mtd_device(mymtd); +#endif + return 0; +} + +static void __exit cleanup_scx200_docflash(void) +{ + if (mymtd) { +#if PARTITION + del_mtd_partitions(mymtd); +#else + del_mtd_device(mymtd); +#endif + map_destroy(mymtd); + } + if (scx200_docflash_map.map_priv_1) { + iounmap((void *)scx200_docflash_map.map_priv_1); + release_resource(&docmem); + } +} + +module_init(init_scx200_docflash); +module_exit(cleanup_scx200_docflash); + +/* + Local variables: + compile-command: "make -k -C ../../.. SUBDIRS=drivers/mtd/maps modules" + c-basic-offset: 8 + End: +*/ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index be4d21956fc5..a652bbf6682d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -288,6 +288,12 @@ #define PCI_DEVICE_ID_NS_87560_USB 0x0012 #define PCI_DEVICE_ID_NS_83815 0x0020 #define PCI_DEVICE_ID_NS_83820 0x0022 +#define PCI_DEVICE_ID_NS_SCx200_BRIDGE 0x0500 +#define PCI_DEVICE_ID_NS_SCx200_SMI 0x0501 +#define PCI_DEVICE_ID_NS_SCx200_IDE 0x0502 +#define PCI_DEVICE_ID_NS_SCx200_AUDIO 0x0503 +#define PCI_DEVICE_ID_NS_SCx200_VIDEO 0x0504 +#define PCI_DEVICE_ID_NS_SCx200_XBUS 0x0505 #define PCI_DEVICE_ID_NS_87410 0xd001 #define PCI_VENDOR_ID_TSENG 0x100c diff --git a/include/linux/scx200.h b/include/linux/scx200.h new file mode 100644 index 000000000000..af7d53acad99 --- /dev/null +++ b/include/linux/scx200.h @@ -0,0 +1,56 @@ +/* linux/include/linux/scx200.h + + Copyright (c) 2001,2002 Christer Weinigel + + Defines for the National Semiconductor SCx200 Processors +*/ + +/* Interesting stuff for the National Semiconductor SCx200 CPU */ + +/* F0 PCI Header/Bridge Configuration Registers */ +#define SCx200_DOCCS_BASE 0x78 /* DOCCS Base Address Register */ +#define SCx200_DOCCS_CTRL 0x7c /* DOCCS Control Register */ + +/* GPIO Register Block */ +#define SCx200_GPIO_SIZE 0x2c /* Size of GPIO register block */ + +/* General Configuration Block */ +#define SCx200_CB_BASE 0x9000 /* Base fixed at 0x9000 according to errata */ + +/* Watchdog Timer */ +#define SCx200_WDT_OFFSET 0x00 /* offset within configuration block */ +#define SCx200_WDT_SIZE 0x05 /* size */ + +#define SCx200_WDT_WDTO 0x00 /* Time-Out Register */ +#define SCx200_WDT_WDCNFG 0x02 /* Configuration Register */ +#define SCx200_WDT_WDSTS 0x04 /* Status Register */ +#define SCx200_WDT_WDSTS_WDOVF (1<<0) /* Overflow bit */ + +/* High Resolution Timer */ +#define SCx200_TIMER_OFFSET 0x08 +#define SCx200_TIMER_SIZE 0x05 + +/* Clock Generators */ +#define SCx200_CLOCKGEN_OFFSET 0x10 +#define SCx200_CLOCKGEN_SIZE 0x10 + +/* Pin Multiplexing and Miscellaneous Configuration Registers */ +#define SCx200_MISC_OFFSET 0x30 +#define SCx200_MISC_SIZE 0x10 + +#define SCx200_PMR 0x30 /* Pin Multiplexing Register */ +#define SCx200_MCR 0x34 /* Miscellaneous Configuration Register */ +#define SCx200_INTSEL 0x38 /* Interrupt Selection Register */ +#define SCx200_IID 0x3c /* IA On a Chip Identification Number Reg */ +#define SCx200_REV 0x3d /* Revision Register */ +#define SCx200_CBA 0x3e /* Configuration Base Address Register */ + +/* Verify that the configuration block really is there */ +#define scx200_cb_probe(base) (inw((base) + SCx200_CBA) == (base)) + +/* + Local variables: + compile-command: "make -C ../.. bzImage modules" + c-basic-offset: 8 + End: +*/ diff --git a/include/linux/scx200_gpio.h b/include/linux/scx200_gpio.h new file mode 100644 index 000000000000..2c6d739706b6 --- /dev/null +++ b/include/linux/scx200_gpio.h @@ -0,0 +1,98 @@ +#include + +u32 scx200_gpio_configure(int index, u32 set, u32 clear); +void scx200_gpio_dump(unsigned index); + +extern unsigned scx200_gpio_base; +extern spinlock_t scx200_gpio_lock; +extern long scx200_gpio_shadow[2]; + +#define scx200_gpio_present() (scx200_gpio_base!=0) + +/* Definitions to make sure I do the same thing in all functions */ +#define __SCx200_GPIO_BANK unsigned bank = index>>5 +#define __SCx200_GPIO_IOADDR unsigned short ioaddr = scx200_gpio_base+0x10*bank +#define __SCx200_GPIO_SHADOW long *shadow = scx200_gpio_shadow+bank +#define __SCx200_GPIO_INDEX index &= 31 + +#define __SCx200_GPIO_OUT __asm__ __volatile__("outsl":"=mS" (shadow):"d" (ioaddr), "0" (shadow)) + +/* returns the value of the GPIO pin */ + +static inline int scx200_gpio_get(int index) { + __SCx200_GPIO_BANK; + __SCx200_GPIO_IOADDR + 0x04; + __SCx200_GPIO_INDEX; + + return (inl(ioaddr) & (1< Date: Fri, 4 Oct 2002 20:15:55 -0700 Subject: [PATCH] struct super_block cleanup - hpfs Remove hpfs_sb from struct super_block. --- fs/hpfs/alloc.c | 54 +++++++++-------- fs/hpfs/anode.c | 18 +++--- fs/hpfs/buffer.c | 12 ++-- fs/hpfs/dentry.c | 2 +- fs/hpfs/dir.c | 12 ++-- fs/hpfs/dnode.c | 34 +++++------ fs/hpfs/hpfs_fn.h | 39 +++++++----- fs/hpfs/inode.c | 38 ++++++------ fs/hpfs/map.c | 24 ++++---- fs/hpfs/name.c | 8 +-- fs/hpfs/namei.c | 6 +- fs/hpfs/super.c | 148 +++++++++++++++++++++++++-------------------- include/linux/fs.h | 2 - include/linux/hpfs_fs_sb.h | 27 --------- 14 files changed, 209 insertions(+), 215 deletions(-) (limited to 'include') diff --git a/fs/hpfs/alloc.c b/fs/hpfs/alloc.c index 2a9e1c3da6d8..511616f1eb18 100644 --- a/fs/hpfs/alloc.c +++ b/fs/hpfs/alloc.c @@ -24,8 +24,8 @@ static int chk_if_allocated(struct super_block *s, secno sec, char *msg) goto fail1; } hpfs_brelse4(&qbh); - if (sec >= s->s_hpfs_dirband_start && sec < s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) { - unsigned ssec = (sec - s->s_hpfs_dirband_start) / 4; + if (sec >= hpfs_sb(s)->sb_dirband_start && sec < hpfs_sb(s)->sb_dirband_start + hpfs_sb(s)->sb_dirband_size) { + unsigned ssec = (sec - hpfs_sb(s)->sb_dirband_start) / 4; if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto fail; if ((bmp[ssec >> 5] >> (ssec & 0x1f)) & 1) { hpfs_error(s, "sector '%s' - %08x not allocated in directory bitmap", msg, sec); @@ -48,11 +48,11 @@ static int chk_if_allocated(struct super_block *s, secno sec, char *msg) int hpfs_chk_sectors(struct super_block *s, secno start, int len, char *msg) { if (start + len < start || start < 0x12 || - start + len > s->s_hpfs_fs_size) { + start + len > hpfs_sb(s)->sb_fs_size) { hpfs_error(s, "sector(s) '%s' badly placed at %08x", msg, start); return 1; } - if (s->s_hpfs_chk>=2) { + if (hpfs_sb(s)->sb_chk>=2) { int i; for (i = 0; i < len; i++) if (chk_if_allocated(s, start + i, msg)) return 1; @@ -127,7 +127,7 @@ static secno alloc_in_bmp(struct super_block *s, secno near, unsigned n, unsigne } rt: if (ret) { - if (s->s_hpfs_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) { + if (hpfs_sb(s)->sb_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) { hpfs_error(s, "Allocation doesn't work! Wanted %d, allocated at %08x", n, ret); ret = 0; goto b; @@ -155,14 +155,15 @@ secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forwa secno sec; unsigned i; unsigned n_bmps; - int b = s->s_hpfs_c_bitmap; + struct hpfs_sb_info *sbi = hpfs_sb(s); + int b = sbi->sb_c_bitmap; int f_p = 0; if (forward < 0) { forward = -forward; f_p = 1; } if (lock) hpfs_lock_creation(s); - if (near && near < s->s_hpfs_fs_size) + if (near && near < sbi->sb_fs_size) if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret; if (b != -1) { if ((sec = alloc_in_bmp(s, b<<14, n, f_p ? forward : forward/2))) { @@ -171,25 +172,25 @@ secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forwa } if (b > 0x10000000) if ((sec = alloc_in_bmp(s, (b&0xfffffff)<<14, n, f_p ? forward : 0))) goto ret; } - n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14; + n_bmps = (sbi->sb_fs_size + 0x4000 - 1) >> 14; for (i = 0; i < n_bmps / 2; i++) { if ((sec = alloc_in_bmp(s, (n_bmps/2+i) << 14, n, forward))) { - s->s_hpfs_c_bitmap = n_bmps/2+i; + sbi->sb_c_bitmap = n_bmps/2+i; goto ret; } if ((sec = alloc_in_bmp(s, (n_bmps/2-i-1) << 14, n, forward))) { - s->s_hpfs_c_bitmap = n_bmps/2-i-1; + sbi->sb_c_bitmap = n_bmps/2-i-1; goto ret; } } if ((sec = alloc_in_bmp(s, (n_bmps-1) << 14, n, forward))) { - s->s_hpfs_c_bitmap = n_bmps-1; + sbi->sb_c_bitmap = n_bmps-1; goto ret; } if (!f_p) { for (i = 0; i < n_bmps; i++) if ((sec = alloc_in_bmp(s, i << 14, n, 0))) { - s->s_hpfs_c_bitmap = 0x10000000 + i; + sbi->sb_c_bitmap = 0x10000000 + i; goto ret; } } @@ -212,17 +213,18 @@ static secno alloc_in_dirband(struct super_block *s, secno near, int lock) { unsigned nr = near; secno sec; - if (nr < s->s_hpfs_dirband_start) - nr = s->s_hpfs_dirband_start; - if (nr >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) - nr = s->s_hpfs_dirband_start + s->s_hpfs_dirband_size - 4; - nr -= s->s_hpfs_dirband_start; + struct hpfs_sb_info *sbi = hpfs_sb(s); + if (nr < sbi->sb_dirband_start) + nr = sbi->sb_dirband_start; + if (nr >= sbi->sb_dirband_start + sbi->sb_dirband_size) + nr = sbi->sb_dirband_start + sbi->sb_dirband_size - 4; + nr -= sbi->sb_dirband_start; nr >>= 2; if (lock) hpfs_lock_creation(s); sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0); if (lock) hpfs_unlock_creation(s); if (!sec) return 0; - return ((sec & 0x3fff) << 2) + s->s_hpfs_dirband_start; + return ((sec & 0x3fff) << 2) + sbi->sb_dirband_start; } /* Alloc sector if it's free */ @@ -303,8 +305,8 @@ void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n) int hpfs_check_free_dnodes(struct super_block *s, int n) { - int n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14; - int b = s->s_hpfs_c_bitmap & 0x0fffffff; + int n_bmps = (hpfs_sb(s)->sb_fs_size + 0x4000 - 1) >> 14; + int b = hpfs_sb(s)->sb_c_bitmap & 0x0fffffff; int i, j; unsigned *bmp; struct quad_buffer_head qbh; @@ -320,7 +322,7 @@ int hpfs_check_free_dnodes(struct super_block *s, int n) } hpfs_brelse4(&qbh); i = 0; - if (s->s_hpfs_c_bitmap != -1 ) { + if (hpfs_sb(s)->sb_c_bitmap != -1 ) { bmp = hpfs_map_bitmap(s, b, &qbh, "chkdn1"); goto chk_bmp; } @@ -349,17 +351,17 @@ int hpfs_check_free_dnodes(struct super_block *s, int n) void hpfs_free_dnode(struct super_block *s, dnode_secno dno) { - if (s->s_hpfs_chk) if (dno & 3) { + if (hpfs_sb(s)->sb_chk) if (dno & 3) { hpfs_error(s, "hpfs_free_dnode: dnode %08x not aligned", dno); return; } - if (dno < s->s_hpfs_dirband_start || - dno >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) { + if (dno < hpfs_sb(s)->sb_dirband_start || + dno >= hpfs_sb(s)->sb_dirband_start + hpfs_sb(s)->sb_dirband_size) { hpfs_free_sectors(s, dno, 4); } else { struct quad_buffer_head qbh; unsigned *bmp; - unsigned ssec = (dno - s->s_hpfs_dirband_start) / 4; + unsigned ssec = (dno - hpfs_sb(s)->sb_dirband_start) / 4; lock_super(s); if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) { unlock_super(s); @@ -377,7 +379,7 @@ struct dnode *hpfs_alloc_dnode(struct super_block *s, secno near, int lock) { struct dnode *d; - if (hpfs_count_one_bitmap(s, s->s_hpfs_dmap) > FREE_DNODES_ADD) { + if (hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_dmap) > FREE_DNODES_ADD) { if (!(*dno = alloc_in_dirband(s, near, lock))) if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock))) return NULL; } else { diff --git a/fs/hpfs/anode.c b/fs/hpfs/anode.c index 100efe503f6d..4135a8c8368f 100644 --- a/fs/hpfs/anode.c +++ b/fs/hpfs/anode.c @@ -20,7 +20,7 @@ secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode, int i; int c1, c2 = 0; go_down: - if (s->s_hpfs_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1; + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1; if (btree->internal) { for (i = 0; i < btree->n_used_nodes; i++) if (btree->u.internal[i].file_secno > sec) { @@ -38,7 +38,7 @@ secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode, if (btree->u.external[i].file_secno <= sec && btree->u.external[i].file_secno + btree->u.external[i].length > sec) { a = btree->u.external[i].disk_secno + sec - btree->u.external[i].file_secno; - if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, a, 1, "data")) { + if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, a, 1, "data")) { brelse(bh); return -1; } @@ -88,7 +88,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi btree->u.internal[n].file_secno = -1; mark_buffer_dirty(bh); brelse(bh); - if (s->s_hpfs_chk) + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_add_sector_to_btree #1")) return -1; if (!(anode = hpfs_map_anode(s, a, &bh))) return -1; btree = &anode->btree; @@ -164,7 +164,7 @@ secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsi c2 = 0; while (up != -1) { struct anode *new_anode; - if (s->s_hpfs_chk) + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, up, &c1, &c2, "hpfs_add_sector_to_btree #2")) return -1; if (up != node || !fnod) { if (!(anode = hpfs_map_anode(s, up, &bh))) return -1; @@ -283,7 +283,7 @@ void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree) while (btree1->internal) { ano = btree1->u.internal[pos].down; if (level) brelse(bh); - if (s->s_hpfs_chk) + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1")) return; if (!(anode = hpfs_map_anode(s, ano, &bh))) return; @@ -296,7 +296,7 @@ void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree) go_up: if (!level) return; brelse(bh); - if (s->s_hpfs_chk) + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, ano, &c1, &c2, "hpfs_remove_btree #2")) return; hpfs_free_sectors(s, ano, 1); oano = ano; @@ -343,7 +343,7 @@ int hpfs_ea_read(struct super_block *s, secno a, int ano, unsigned pos, if ((sec = anode_lookup(s, a, pos >> 9)) == -1) return -1; } else sec = a + (pos >> 9); - if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1; + if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1; if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9))) return -1; l = 0x200 - (pos & 0x1ff); if (l > len) l = len; @@ -366,7 +366,7 @@ int hpfs_ea_write(struct super_block *s, secno a, int ano, unsigned pos, if ((sec = anode_lookup(s, a, pos >> 9)) == -1) return -1; } else sec = a + (pos >> 9); - if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1; + if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1; if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9))) return -1; l = 0x200 - (pos & 0x1ff); if (l > len) l = len; @@ -440,7 +440,7 @@ void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs) } node = btree->u.internal[i].down; brelse(bh); - if (s->s_hpfs_chk) + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, node, &c1, &c2, "hpfs_truncate_btree")) return; if (!(anode = hpfs_map_anode(s, node, &bh))) return; diff --git a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c index d1ce3afadcd4..36f463947a20 100644 --- a/fs/hpfs/buffer.c +++ b/fs/hpfs/buffer.c @@ -15,7 +15,7 @@ void hpfs_lock_creation(struct super_block *s) #ifdef DEBUG_LOCKS printk("lock creation\n"); #endif - down(&s->u.hpfs_sb.hpfs_creation_de); + down(&hpfs_sb(s)->hpfs_creation_de); } void hpfs_unlock_creation(struct super_block *s) @@ -23,7 +23,7 @@ void hpfs_unlock_creation(struct super_block *s) #ifdef DEBUG_LOCKS printk("unlock creation\n"); #endif - up(&s->u.hpfs_sb.hpfs_creation_de); + up(&hpfs_sb(s)->hpfs_creation_de); } void hpfs_lock_iget(struct super_block *s, int mode) @@ -31,8 +31,8 @@ void hpfs_lock_iget(struct super_block *s, int mode) #ifdef DEBUG_LOCKS printk("lock iget\n"); #endif - while (s->s_hpfs_rd_inode) sleep_on(&s->s_hpfs_iget_q); - s->s_hpfs_rd_inode = mode; + while (hpfs_sb(s)->sb_rd_inode) sleep_on(&hpfs_sb(s)->sb_iget_q); + hpfs_sb(s)->sb_rd_inode = mode; } void hpfs_unlock_iget(struct super_block *s) @@ -40,8 +40,8 @@ void hpfs_unlock_iget(struct super_block *s) #ifdef DEBUG_LOCKS printk("unlock iget\n"); #endif - s->s_hpfs_rd_inode = 0; - wake_up(&s->s_hpfs_iget_q); + hpfs_sb(s)->sb_rd_inode = 0; + wake_up(&hpfs_sb(s)->sb_iget_q); } void hpfs_lock_inode(struct inode *i) diff --git a/fs/hpfs/dentry.c b/fs/hpfs/dentry.c index 3aad4ba50ce2..e572d3e0d6f3 100644 --- a/fs/hpfs/dentry.c +++ b/fs/hpfs/dentry.c @@ -28,7 +28,7 @@ int hpfs_hash_dentry(struct dentry *dentry, struct qstr *qstr) hash = init_name_hash(); for (i = 0; i < l; i++) - hash = partial_name_hash(hpfs_upcase(dentry->d_sb->s_hpfs_cp_table,qstr->name[i]), hash); + hash = partial_name_hash(hpfs_upcase(hpfs_sb(dentry->d_sb)->sb_cp_table,qstr->name[i]), hash); qstr->hash = end_name_hash(hash); return 0; diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c index 2a7574901f8a..ff8039bc4fe8 100644 --- a/fs/hpfs/dir.c +++ b/fs/hpfs/dir.c @@ -65,7 +65,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir) int c1, c2 = 0; int ret = 0; - if (inode->i_sb->s_hpfs_chk) { + if (hpfs_sb(inode->i_sb)->sb_chk) { if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode")) { ret = -EFSERROR; goto out; @@ -75,7 +75,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir) goto out; } } - if (inode->i_sb->s_hpfs_chk >= 2) { + if (hpfs_sb(inode->i_sb)->sb_chk >= 2) { struct buffer_head *bh; struct fnode *fno; int e = 0; @@ -97,7 +97,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir) goto out; } } - lc = inode->i_sb->s_hpfs_lowercase; + lc = hpfs_sb(inode->i_sb)->sb_lowercase; if (filp->f_pos == 12) { /* diff -r requires this (note, that diff -r */ filp->f_pos = 13; /* also fails on msdos filesystem in 2.0) */ goto out; @@ -114,7 +114,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir) /* This won't work when cycle is longer than number of dirents accepted by filldir, but what can I do? maybe killall -9 ls helps */ - if (inode->i_sb->s_hpfs_chk) + if (hpfs_sb(inode->i_sb)->sb_chk) if (hpfs_stop_cycles(inode->i_sb, filp->f_pos, &c1, &c2, "hpfs_readdir")) { hpfs_unlock_inode(inode); ret = -EFSERROR; @@ -160,7 +160,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir) goto out; } if (de->first || de->last) { - if (inode->i_sb->s_hpfs_chk) { + if (hpfs_sb(inode->i_sb)->sb_chk) { if (de->first && !de->last && (de->namelen != 2 || de ->name[0] != 1 || de->name[1] != 1)) hpfs_error(inode->i_sb, "hpfs_readdir: bad ^A^A entry; pos = %08x", old_pos); if (de->last && (de->namelen != 1 || de ->name[0] != 255)) hpfs_error(inode->i_sb, "hpfs_readdir: bad \\377 entry; pos = %08x", old_pos); } @@ -241,7 +241,7 @@ struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry) * Go find or make an inode. */ - hpfs_lock_iget(dir->i_sb, de->directory || (de->ea_size && dir->i_sb->s_hpfs_eas) ? 1 : 2); + hpfs_lock_iget(dir->i_sb, de->directory || (de->ea_size && hpfs_sb(dir->i_sb)->sb_eas) ? 1 : 2); if (!(result = iget(dir->i_sb, ino))) { hpfs_unlock_iget(dir->i_sb); hpfs_error(dir->i_sb, "hpfs_lookup: can't get inode"); diff --git a/fs/hpfs/dnode.c b/fs/hpfs/dnode.c index 37971b77c751..72b3be59c1f1 100644 --- a/fs/hpfs/dnode.c +++ b/fs/hpfs/dnode.c @@ -134,7 +134,7 @@ static void set_last_pointer(struct super_block *s, struct dnode *d, dnode_secno hpfs_error(s, "set_last_pointer: empty dnode %08x", d->self); return; } - if (s->s_hpfs_chk) { + if (hpfs_sb(s)->sb_chk) { if (de->down) { hpfs_error(s, "set_last_pointer: dnode %08x has already last pointer %08x", d->self, de_down_pointer(de)); @@ -253,7 +253,7 @@ int hpfs_add_to_dnode(struct inode *i, dnode_secno dno, unsigned char *name, uns return 1; } go_up_a: - if (i->i_sb->s_hpfs_chk) + if (hpfs_sb(i->i_sb)->sb_chk) if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_to_dnode")) { hpfs_brelse4(&qbh); if (nd) kfree(nd); @@ -379,7 +379,7 @@ int hpfs_add_dirent(struct inode *i, unsigned char *name, unsigned namelen, int c1, c2 = 0; dno = hpfs_inode->i_dno; down: - if (i->i_sb->s_hpfs_chk) + if (hpfs_sb(i->i_sb)->sb_chk) if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_dirent")) return 1; if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 1; de_end = dnode_end_de(d); @@ -427,11 +427,11 @@ static secno move_to_top(struct inode *i, dnode_secno from, dnode_secno to) int c1, c2 = 0; dno = from; while (1) { - if (i->i_sb->s_hpfs_chk) + if (hpfs_sb(i->i_sb)->sb_chk) if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "move_to_top")) return 0; if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 0; - if (i->i_sb->s_hpfs_chk) { + if (hpfs_sb(i->i_sb)->sb_chk) { if (dnode->up != chk_up) { hpfs_error(i->i_sb, "move_to_top: up pointer from %08x should be %08x, is %08x", dno, chk_up, dnode->up); @@ -519,7 +519,7 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno) up = dnode->up; de = dnode_first_de(dnode); down = de->down ? de_down_pointer(de) : 0; - if (i->i_sb->s_hpfs_chk) if (root && !down) { + if (hpfs_sb(i->i_sb)->sb_chk) if (root && !down) { hpfs_error(i->i_sb, "delete_empty_dnode: root dnode %08x is empty", dno); goto end; } @@ -532,7 +532,7 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno) struct buffer_head *bh; struct dnode *d1; struct quad_buffer_head qbh1; - if (i->i_sb->s_hpfs_chk) if (up != i->i_ino) { + if (hpfs_sb(i->i_sb)->sb_chk) if (up != i->i_ino) { hpfs_error(i->i_sb, "bad pointer to fnode, dnode %08x, pointing to %08x, should be %08x", dno, up, i->i_ino); return; } @@ -628,14 +628,14 @@ static void delete_empty_dnode(struct inode *i, dnode_secno dno) dlp = del->down ? de_down_pointer(del) : 0; if (!dlp && down) { if (d1->first_free > 2044) { - if (i->i_sb->s_hpfs_chk >= 2) { + if (hpfs_sb(i->i_sb)->sb_chk >= 2) { printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n"); printk("HPFS: warning: terminating balancing operation\n"); } hpfs_brelse4(&qbh1); goto endm; } - if (i->i_sb->s_hpfs_chk >= 2) { + if (hpfs_sb(i->i_sb)->sb_chk >= 2) { printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n"); printk("HPFS: warning: goin'on\n"); } @@ -738,12 +738,12 @@ void hpfs_count_dnodes(struct super_block *s, dnode_secno dno, int *n_dnodes, int d1, d2 = 0; go_down: if (n_dnodes) (*n_dnodes)++; - if (s->s_hpfs_chk) + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, dno, &c1, &c2, "hpfs_count_dnodes #1")) return; ptr = 0; go_up: if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return; - if (s->s_hpfs_chk) if (odno && odno != -1 && dnode->up != odno) + if (hpfs_sb(s)->sb_chk) if (odno && odno != -1 && dnode->up != odno) hpfs_error(s, "hpfs_count_dnodes: bad up pointer; dnode %08x, down %08x points to %08x", odno, dno, dnode->up); de = dnode_first_de(dnode); if (ptr) while(1) { @@ -774,7 +774,7 @@ void hpfs_count_dnodes(struct super_block *s, dnode_secno dno, int *n_dnodes, return; } hpfs_brelse4(&qbh); - if (s->s_hpfs_chk) + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, ptr, &d1, &d2, "hpfs_count_dnodes #2")) return; odno = -1; goto go_up; @@ -811,11 +811,11 @@ dnode_secno hpfs_de_as_down_as_possible(struct super_block *s, dnode_secno dno) int c1, c2 = 0; again: - if (s->s_hpfs_chk) + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, d, &c1, &c2, "hpfs_de_as_down_as_possible")) return d; if (!(de = map_nth_dirent(s, d, 1, &qbh, NULL))) return dno; - if (s->s_hpfs_chk) + if (hpfs_sb(s)->sb_chk) if (up && ((struct dnode *)qbh.data)->up != up) hpfs_error(s, "hpfs_de_as_down_as_possible: bad up pointer; dnode %08x, down %08x points to %08x", up, d, ((struct dnode *)qbh.data)->up); if (!de->down) { @@ -901,7 +901,7 @@ struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno, char *name, if (!S_ISDIR(inode->i_mode)) hpfs_error(inode->i_sb, "map_dirent: not a directory\n"); again: - if (inode->i_sb->s_hpfs_chk) + if (hpfs_sb(inode->i_sb)->sb_chk) if (hpfs_stop_cycles(inode->i_sb, dno, &c1, &c2, "map_dirent")) return NULL; if (!(dnode = hpfs_map_dnode(inode->i_sb, dno, qbh))) return NULL; @@ -1046,7 +1046,7 @@ struct hpfs_dirent *map_fnode_dirent(struct super_block *s, fnode_secno fno, if (c < 0 && de->down) { dno = de_down_pointer(de); hpfs_brelse4(qbh); - if (s->s_hpfs_chk) + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, dno, &c1, &c2, "map_fnode_dirent #1")) { kfree(name2); return NULL; @@ -1065,7 +1065,7 @@ struct hpfs_dirent *map_fnode_dirent(struct super_block *s, fnode_secno fno, downd = dno; dno = d->up; hpfs_brelse4(qbh); - if (s->s_hpfs_chk) + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, downd, &d1, &d2, "map_fnode_dirent #2")) { kfree(name2); return NULL; diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index 775cc92a77ee..237e55f6ee42 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -57,22 +58,6 @@ typedef void nonconst; /* What this is for ? */ -/* - * local time (HPFS) to GMT (Unix) - */ - -extern inline time_t local_to_gmt(struct super_block *s, time_t t) -{ - extern struct timezone sys_tz; - return t + sys_tz.tz_minuteswest * 60 + s->s_hpfs_timeshift; -} - -extern inline time_t gmt_to_local(struct super_block *s, time_t t) -{ - extern struct timezone sys_tz; - return t - sys_tz.tz_minuteswest * 60 - s->s_hpfs_timeshift; -} - /* * conv= options */ @@ -309,6 +294,11 @@ static inline struct hpfs_inode_info *hpfs_i(struct inode *inode) return list_entry(inode, struct hpfs_inode_info, vfs_inode); } +static inline struct hpfs_sb_info *hpfs_sb(struct super_block *sb) +{ + return sb->u.generic_sbp; +} + /* super.c */ void hpfs_error(struct super_block *, char *, ...); @@ -319,3 +309,20 @@ unsigned hpfs_count_one_bitmap(struct super_block *, secno); int hpfs_statfs(struct super_block *, struct statfs *); extern struct address_space_operations hpfs_aops; + +/* + * local time (HPFS) to GMT (Unix) + */ + +extern inline time_t local_to_gmt(struct super_block *s, time_t t) +{ + extern struct timezone sys_tz; + return t + sys_tz.tz_minuteswest * 60 + hpfs_sb(s)->sb_timeshift; +} + +extern inline time_t gmt_to_local(struct super_block *s, time_t t) +{ + extern struct timezone sys_tz; + return t - sys_tz.tz_minuteswest * 60 - hpfs_sb(s)->sb_timeshift; +} + diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index cdcfb294fc49..407acc268f7e 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -66,10 +66,10 @@ void hpfs_read_inode(struct inode *i) unsigned char *ea; int ea_size; - i->i_uid = sb->s_hpfs_uid; - i->i_gid = sb->s_hpfs_gid; - i->i_mode = sb->s_hpfs_mode; - hpfs_inode->i_conv = sb->s_hpfs_conv; + i->i_uid = hpfs_sb(sb)->sb_uid; + i->i_gid = hpfs_sb(sb)->sb_gid; + i->i_mode = hpfs_sb(sb)->sb_mode; + hpfs_inode->i_conv = hpfs_sb(sb)->sb_conv; i->i_blksize = 512; i->i_size = -1; i->i_blocks = -1; @@ -93,9 +93,9 @@ void hpfs_read_inode(struct inode *i) i->i_mtime = 0; i->i_ctime = 0; - if (!i->i_sb->s_hpfs_rd_inode) - hpfs_error(i->i_sb, "read_inode: s_hpfs_rd_inode == 0"); - if (i->i_sb->s_hpfs_rd_inode == 2) { + if (!hpfs_sb(i->i_sb)->sb_rd_inode) + hpfs_error(i->i_sb, "read_inode: sb_rd_inode == 0"); + if (hpfs_sb(i->i_sb)->sb_rd_inode == 2) { i->i_mode |= S_IFREG; i->i_mode &= ~0111; i->i_op = &hpfs_file_iops; @@ -112,7 +112,7 @@ void hpfs_read_inode(struct inode *i) make_bad_inode(i); return; } - if (i->i_sb->s_hpfs_eas) { + if (hpfs_sb(i->i_sb)->sb_eas) { if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) { if (ea_size == 2) { i->i_uid = ea[0] + (ea[1] << 8); @@ -140,7 +140,7 @@ void hpfs_read_inode(struct inode *i) } if ((ea = hpfs_get_ea(i->i_sb, fnode, "MODE", &ea_size))) { int rdev = 0; - umode_t mode = sb->s_hpfs_mode; + umode_t mode = hpfs_sb(sb)->sb_mode; if (ea_size == 2) { mode = ea[0] + (ea[1] << 8); hpfs_inode->i_ea_mode = 1; @@ -171,7 +171,7 @@ void hpfs_read_inode(struct inode *i) i->i_fop = &hpfs_dir_ops; hpfs_inode->i_parent_dir = fnode->up; hpfs_inode->i_dno = fnode->u.external[0].disk_secno; - if (sb->s_hpfs_chk >= 2) { + if (hpfs_sb(sb)->sb_chk >= 2) { struct buffer_head *bh0; if (hpfs_map_fnode(sb, hpfs_inode->i_parent_dir, &bh0)) brelse(bh0); } @@ -201,24 +201,24 @@ void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode) /* Some unknown structures like ACL may be in fnode, we'd better not overwrite them */ hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino); - } else if (i->i_sb->s_hpfs_eas >= 2) { + } else if (hpfs_sb(i->i_sb)->sb_eas >= 2) { unsigned char ea[4]; - if ((i->i_uid != i->i_sb->s_hpfs_uid) || hpfs_inode->i_ea_uid) { + if ((i->i_uid != hpfs_sb(i->i_sb)->sb_uid) || hpfs_inode->i_ea_uid) { ea[0] = i->i_uid & 0xff; ea[1] = i->i_uid >> 8; hpfs_set_ea(i, fnode, "UID", ea, 2); hpfs_inode->i_ea_uid = 1; } - if ((i->i_gid != i->i_sb->s_hpfs_gid) || hpfs_inode->i_ea_gid) { + if ((i->i_gid != hpfs_sb(i->i_sb)->sb_gid) || hpfs_inode->i_ea_gid) { ea[0] = i->i_gid & 0xff; ea[1] = i->i_gid >> 8; hpfs_set_ea(i, fnode, "GID", ea, 2); hpfs_inode->i_ea_gid = 1; } if (!S_ISLNK(i->i_mode)) - if ((i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111)) + if ((i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111)) | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG)) - && i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333)) + && i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333)) | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || hpfs_inode->i_ea_mode) { ea[0] = i->i_mode & 0xff; ea[1] = i->i_mode >> 8; @@ -241,7 +241,7 @@ void hpfs_write_inode(struct inode *i) struct hpfs_inode_info *hpfs_inode = hpfs_i(i); struct inode *parent; if (!i->i_nlink) return; - if (i->i_ino == i->i_sb->s_hpfs_root) return; + if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return; if (hpfs_inode->i_rddir_off && !atomic_read(&i->i_count)) { if (*hpfs_inode->i_rddir_off) printk("HPFS: write_inode: some position still there\n"); kfree(hpfs_inode->i_rddir_off); @@ -264,9 +264,9 @@ void hpfs_write_inode_nolock(struct inode *i) struct fnode *fnode; struct quad_buffer_head qbh; struct hpfs_dirent *de; - if (i->i_ino == i->i_sb->s_hpfs_root) return; + if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return; if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) return; - if (i->i_ino != i->i_sb->s_hpfs_root) { + if (i->i_ino != hpfs_sb(i->i_sb)->sb_root) { if (!(de = map_fnode_dirent(i->i_sb, i->i_ino, fnode, &qbh))) { brelse(bh); return; @@ -309,7 +309,7 @@ int hpfs_notify_change(struct dentry *dentry, struct iattr *attr) int error=0; lock_kernel(); if ( ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size) || - (inode->i_sb->s_hpfs_root == inode->i_ino) ) { + (hpfs_sb(inode->i_sb)->sb_root == inode->i_ino) ) { error = -EINVAL; } else if ((error = inode_change_ok(inode, attr))) { } else if ((error = inode_setattr(inode, attr))) { diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c index 762522742a6a..f5774801b67f 100644 --- a/fs/hpfs/map.c +++ b/fs/hpfs/map.c @@ -11,19 +11,19 @@ unsigned *hpfs_map_dnode_bitmap(struct super_block *s, struct quad_buffer_head *qbh) { - return hpfs_map_4sectors(s, s->s_hpfs_dmap, qbh, 0); + return hpfs_map_4sectors(s, hpfs_sb(s)->sb_dmap, qbh, 0); } unsigned int *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block, struct quad_buffer_head *qbh, char *id) { secno sec; - if (s->s_hpfs_chk) if (bmp_block * 16384 > s->s_hpfs_fs_size) { + if (hpfs_sb(s)->sb_chk) if (bmp_block * 16384 > hpfs_sb(s)->sb_fs_size) { hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id); return NULL; } - sec = s->s_hpfs_bmp_dir[bmp_block]; - if (!sec || sec > s->s_hpfs_fs_size-4) { + sec = hpfs_sb(s)->sb_bmp_dir[bmp_block]; + if (!sec || sec > hpfs_sb(s)->sb_fs_size-4) { hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id); return NULL; } @@ -93,7 +93,7 @@ char *hpfs_load_code_page(struct super_block *s, secno cps) secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp) { struct buffer_head *bh; - int n = (s->s_hpfs_fs_size + 0x200000 - 1) >> 21; + int n = (hpfs_sb(s)->sb_fs_size + 0x200000 - 1) >> 21; int i; secno *b; if (!(b = kmalloc(n * 512, GFP_KERNEL))) { @@ -119,11 +119,11 @@ secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp) struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_head **bhp) { struct fnode *fnode; - if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) { + if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) { return NULL; } if ((fnode = hpfs_map_sector(s, ino, bhp, FNODE_RD_AHEAD))) { - if (s->s_hpfs_chk) { + if (hpfs_sb(s)->sb_chk) { struct extended_attribute *ea; struct extended_attribute *ea_end; if (fnode->magic != FNODE_MAGIC) { @@ -168,9 +168,9 @@ struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_hea struct anode *hpfs_map_anode(struct super_block *s, anode_secno ano, struct buffer_head **bhp) { struct anode *anode; - if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL; + if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL; if ((anode = hpfs_map_sector(s, ano, bhp, ANODE_RD_AHEAD))) - if (s->s_hpfs_chk) { + if (hpfs_sb(s)->sb_chk) { if (anode->magic != ANODE_MAGIC || anode->self != ano) { hpfs_error(s, "bad magic on anode %08x", ano); goto bail; @@ -200,7 +200,7 @@ struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno, struct quad_buffer_head *qbh) { struct dnode *dnode; - if (s->s_hpfs_chk) { + if (hpfs_sb(s)->sb_chk) { if (hpfs_chk_sectors(s, secno, 4, "dnode")) return NULL; if (secno & 3) { hpfs_error(s, "dnode %08x not byte-aligned", secno); @@ -208,7 +208,7 @@ struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno, } } if ((dnode = hpfs_map_4sectors(s, secno, qbh, DNODE_RD_AHEAD))) - if (s->s_hpfs_chk) { + if (hpfs_sb(s)->sb_chk) { unsigned p, pp = 0; unsigned char *d = (char *)dnode; int b = 0; @@ -234,7 +234,7 @@ struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno, hpfs_error(s, "namelen does not match dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp); goto bail; } - if (s->s_hpfs_chk >= 2) b |= 1 << de->down; + if (hpfs_sb(s)->sb_chk >= 2) b |= 1 << de->down; if (de->down) if (de_down_pointer(de) < 0x10) { hpfs_error(s, "bad down pointer in dnode %08x, dirent %03x, last %03x", secno, p, pp); goto bail; diff --git a/fs/hpfs/name.c b/fs/hpfs/name.c index a50b0b004c5b..6dca89a70590 100644 --- a/fs/hpfs/name.c +++ b/fs/hpfs/name.c @@ -89,7 +89,7 @@ char *hpfs_translate_name(struct super_block *s, unsigned char *from, { char *to; int i; - if (s->s_hpfs_chk >= 2) if (hpfs_is_name_long(from, len) != lng) { + if (hpfs_sb(s)->sb_chk >= 2) if (hpfs_is_name_long(from, len) != lng) { printk("HPFS: Long name flag mismatch - name "); for (i=0; is_hpfs_cp_table,from[i]); + for (i = 0; i < len; i++) to[i] = locase(hpfs_sb(s)->sb_cp_table,from[i]); return to; } @@ -111,8 +111,8 @@ int hpfs_compare_names(struct super_block *s, unsigned char *n1, unsigned l1, unsigned i; if (last) return -1; for (i = 0; i < l; i++) { - unsigned char c1 = upcase(s->s_hpfs_cp_table,n1[i]); - unsigned char c2 = upcase(s->s_hpfs_cp_table,n2[i]); + unsigned char c1 = upcase(hpfs_sb(s)->sb_cp_table,n1[i]); + unsigned char c2 = upcase(hpfs_sb(s)->sb_cp_table,n2[i]); if (c1 < c2) return -1; if (c1 > c2) return 1; } diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index ce4040c730c9..080470e3b38e 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -187,7 +187,7 @@ int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) struct inode *result = NULL; int err; if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; - if (dir->i_sb->s_hpfs_eas < 2) return -EPERM; + if (hpfs_sb(dir->i_sb)->sb_eas < 2) return -EPERM; lock_kernel(); if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail; memset(&dee, 0, sizeof dee); @@ -255,7 +255,7 @@ int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink) int err; if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; lock_kernel(); - if (dir->i_sb->s_hpfs_eas < 2) { + if (hpfs_sb(dir->i_sb)->sb_eas < 2) { unlock_kernel(); return -EPERM; } @@ -559,7 +559,7 @@ int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry, mark_buffer_dirty(bh); brelse(bh); } - hpfs_i(i)->i_conv = i->i_sb->s_hpfs_conv; + hpfs_i(i)->i_conv = hpfs_sb(i->i_sb)->sb_conv; hpfs_decide_conv(i, (char *)new_name, new_len); end1: hpfs_unlock_3inodes(old_dir, new_dir, i); diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index b8306f5f5db9..4d7ae1631844 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -16,7 +16,7 @@ static void mark_dirty(struct super_block *s) { - if (s->s_hpfs_chkdsk && !(s->s_flags & MS_RDONLY)) { + if (hpfs_sb(s)->sb_chkdsk && !(s->s_flags & MS_RDONLY)) { struct buffer_head *bh; struct hpfs_spare_block *sb; if ((sb = hpfs_map_sector(s, 17, &bh, 0))) { @@ -37,8 +37,8 @@ static void unmark_dirty(struct super_block *s) struct hpfs_spare_block *sb; if (s->s_flags & MS_RDONLY) return; if ((sb = hpfs_map_sector(s, 17, &bh, 0))) { - sb->dirty = s->s_hpfs_chkdsk > 1 - s->s_hpfs_was_error; - sb->old_wrote = s->s_hpfs_chkdsk >= 2 && !s->s_hpfs_was_error; + sb->dirty = hpfs_sb(s)->sb_chkdsk > 1 - hpfs_sb(s)->sb_was_error; + sb->old_wrote = hpfs_sb(s)->sb_chkdsk >= 2 && !hpfs_sb(s)->sb_was_error; mark_buffer_dirty(bh); brelse(bh); } @@ -60,12 +60,12 @@ void hpfs_error(struct super_block *s, char *m,...) printk("HPFS: filesystem error: "); if (buf) printk("%s", buf); else printk("%s\n",m); - if (!s->s_hpfs_was_error) { - if (s->s_hpfs_err == 2) { + if (!hpfs_sb(s)->sb_was_error) { + if (hpfs_sb(s)->sb_err == 2) { printk("; crashing the system because you wanted it\n"); mark_dirty(s); panic("HPFS panic"); - } else if (s->s_hpfs_err == 1) { + } else if (hpfs_sb(s)->sb_err == 1) { if (s->s_flags & MS_RDONLY) printk("; already mounted read-only\n"); else { printk("; remounting read-only\n"); @@ -76,7 +76,7 @@ void hpfs_error(struct super_block *s, char *m,...) else printk("; corrupted filesystem mounted read/write - your computer will explode within 20 seconds ... but you wanted it so!\n"); } else printk("\n"); if (buf) kfree(buf); - s->s_hpfs_was_error = 1; + hpfs_sb(s)->sb_was_error = 1; } /* @@ -101,9 +101,12 @@ int hpfs_stop_cycles(struct super_block *s, int key, int *c1, int *c2, void hpfs_put_super(struct super_block *s) { - if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table); - if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir); + struct hpfs_sb_info *sbi = hpfs_sb(s); + if (sbi->sb_cp_table) kfree(sbi->sb_cp_table); + if (sbi->sb_bmp_dir) kfree(sbi->sb_bmp_dir); unmark_dirty(s); + s->u.generic_sbp = NULL; + kfree(sbi); } unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno) @@ -125,28 +128,29 @@ unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno) static unsigned count_bitmaps(struct super_block *s) { unsigned n, count, n_bands; - n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14; + n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14; count = 0; for (n = 0; n < n_bands; n++) - count += hpfs_count_one_bitmap(s, s->s_hpfs_bmp_dir[n]); + count += hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_bmp_dir[n]); return count; } int hpfs_statfs(struct super_block *s, struct statfs *buf) { + struct hpfs_sb_info *sbi = hpfs_sb(s); lock_kernel(); - /*if (s->s_hpfs_n_free == -1) {*/ - s->s_hpfs_n_free = count_bitmaps(s); - s->s_hpfs_n_free_dnodes = hpfs_count_one_bitmap(s, s->s_hpfs_dmap); + /*if (sbi->sb_n_free == -1) {*/ + sbi->sb_n_free = count_bitmaps(s); + sbi->sb_n_free_dnodes = hpfs_count_one_bitmap(s, sbi->sb_dmap); /*}*/ buf->f_type = s->s_magic; buf->f_bsize = 512; - buf->f_blocks = s->s_hpfs_fs_size; - buf->f_bfree = s->s_hpfs_n_free; - buf->f_bavail = s->s_hpfs_n_free; - buf->f_files = s->s_hpfs_dirband_size / 4; - buf->f_ffree = s->s_hpfs_n_free_dnodes; + buf->f_blocks = sbi->sb_fs_size; + buf->f_bfree = sbi->sb_n_free; + buf->f_bavail = sbi->sb_n_free; + buf->f_files = sbi->sb_dirband_size / 4; + buf->f_ffree = sbi->sb_n_free_dnodes; buf->f_namelen = 254; unlock_kernel(); @@ -377,14 +381,15 @@ int hpfs_remount_fs(struct super_block *s, int *flags, char *data) umode_t umask; int lowercase, conv, eas, chk, errs, chkdsk, timeshift; int o; + struct hpfs_sb_info *sbi = hpfs_sb(s); *flags |= MS_NOATIME; - uid = s->s_hpfs_uid; gid = s->s_hpfs_gid; - umask = 0777 & ~s->s_hpfs_mode; - lowercase = s->s_hpfs_lowercase; conv = s->s_hpfs_conv; - eas = s->s_hpfs_eas; chk = s->s_hpfs_chk; chkdsk = s->s_hpfs_chkdsk; - errs = s->s_hpfs_err; timeshift = s->s_hpfs_timeshift; + uid = sbi->sb_uid; gid = sbi->sb_gid; + umask = 0777 & ~sbi->sb_mode; + lowercase = sbi->sb_lowercase; conv = sbi->sb_conv; + eas = sbi->sb_eas; chk = sbi->sb_chk; chkdsk = sbi->sb_chkdsk; + errs = sbi->sb_err; timeshift = sbi->sb_timeshift; if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase, &conv, &eas, &chk, &errs, &chkdsk, ×hift))) { @@ -395,18 +400,18 @@ int hpfs_remount_fs(struct super_block *s, int *flags, char *data) hpfs_help(); return 1; } - if (timeshift != s->s_hpfs_timeshift) { + if (timeshift != sbi->sb_timeshift) { printk("HPFS: timeshift can't be changed using remount.\n"); return 1; } unmark_dirty(s); - s->s_hpfs_uid = uid; s->s_hpfs_gid = gid; - s->s_hpfs_mode = 0777 & ~umask; - s->s_hpfs_lowercase = lowercase; s->s_hpfs_conv = conv; - s->s_hpfs_eas = eas; s->s_hpfs_chk = chk; s->s_hpfs_chkdsk = chkdsk; - s->s_hpfs_err = errs; s->s_hpfs_timeshift = timeshift; + sbi->sb_uid = uid; sbi->sb_gid = gid; + sbi->sb_mode = 0777 & ~umask; + sbi->sb_lowercase = lowercase; sbi->sb_conv = conv; + sbi->sb_eas = eas; sbi->sb_chk = chk; sbi->sb_chkdsk = chkdsk; + sbi->sb_err = errs; sbi->sb_timeshift = timeshift; if (!(*flags & MS_RDONLY)) mark_dirty(s); @@ -419,6 +424,7 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) struct hpfs_boot_block *bootblock; struct hpfs_super_block *superblock; struct hpfs_spare_block *spareblock; + struct hpfs_sb_info *sbi; uid_t uid; gid_t gid; @@ -431,12 +437,18 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) int o; - s->s_hpfs_bmp_dir = NULL; - s->s_hpfs_cp_table = NULL; + sbi = kmalloc(sizeof(*sbi), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + s->u.generic_sbp = sbi; + memset(sbi, 0, sizeof(*sbi)); + + sbi->sb_bmp_dir = NULL; + sbi->sb_cp_table = NULL; - s->s_hpfs_rd_inode = 0; - init_MUTEX(&s->u.hpfs_sb.hpfs_creation_de); - init_waitqueue_head(&s->s_hpfs_iget_q); + sbi->sb_rd_inode = 0; + init_MUTEX(&sbi->hpfs_creation_de); + init_waitqueue_head(&sbi->sb_iget_q); uid = current->uid; gid = current->gid; @@ -459,9 +471,9 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) goto bail0; } - /*s->s_hpfs_mounting = 1;*/ + /*sbi->sb_mounting = 1;*/ sb_set_blocksize(s, 512); - s->s_hpfs_fs_size = -1; + sbi->sb_fs_size = -1; if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1; if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2; if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3; @@ -489,30 +501,30 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) s->s_magic = HPFS_SUPER_MAGIC; s->s_op = &hpfs_sops; - s->s_hpfs_root = superblock->root; - s->s_hpfs_fs_size = superblock->n_sectors; - s->s_hpfs_bitmaps = superblock->bitmaps; - s->s_hpfs_dirband_start = superblock->dir_band_start; - s->s_hpfs_dirband_size = superblock->n_dir_band; - s->s_hpfs_dmap = superblock->dir_band_bitmap; - s->s_hpfs_uid = uid; - s->s_hpfs_gid = gid; - s->s_hpfs_mode = 0777 & ~umask; - s->s_hpfs_n_free = -1; - s->s_hpfs_n_free_dnodes = -1; - s->s_hpfs_lowercase = lowercase; - s->s_hpfs_conv = conv; - s->s_hpfs_eas = eas; - s->s_hpfs_chk = chk; - s->s_hpfs_chkdsk = chkdsk; - s->s_hpfs_err = errs; - s->s_hpfs_timeshift = timeshift; - s->s_hpfs_was_error = 0; - s->s_hpfs_cp_table = NULL; - s->s_hpfs_c_bitmap = -1; + sbi->sb_root = superblock->root; + sbi->sb_fs_size = superblock->n_sectors; + sbi->sb_bitmaps = superblock->bitmaps; + sbi->sb_dirband_start = superblock->dir_band_start; + sbi->sb_dirband_size = superblock->n_dir_band; + sbi->sb_dmap = superblock->dir_band_bitmap; + sbi->sb_uid = uid; + sbi->sb_gid = gid; + sbi->sb_mode = 0777 & ~umask; + sbi->sb_n_free = -1; + sbi->sb_n_free_dnodes = -1; + sbi->sb_lowercase = lowercase; + sbi->sb_conv = conv; + sbi->sb_eas = eas; + sbi->sb_chk = chk; + sbi->sb_chkdsk = chkdsk; + sbi->sb_err = errs; + sbi->sb_timeshift = timeshift; + sbi->sb_was_error = 0; + sbi->sb_cp_table = NULL; + sbi->sb_c_bitmap = -1; /* Load bitmap directory */ - if (!(s->s_hpfs_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps))) + if (!(sbi->sb_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps))) goto bail4; /* Check for general fs errors*/ @@ -557,20 +569,20 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) superblock->dir_band_start, superblock->dir_band_end, superblock->n_dir_band); goto bail4; } - a = s->s_hpfs_dirband_size; - s->s_hpfs_dirband_size = 0; + a = sbi->sb_dirband_size; + sbi->sb_dirband_size = 0; if (hpfs_chk_sectors(s, superblock->dir_band_start, superblock->n_dir_band, "dir_band") || hpfs_chk_sectors(s, superblock->dir_band_bitmap, 4, "dir_band_bitmap") || hpfs_chk_sectors(s, superblock->bitmaps, 4, "bitmaps")) { mark_dirty(s); goto bail4; } - s->s_hpfs_dirband_size = a; + sbi->sb_dirband_size = a; } else printk("HPFS: You really don't want any checks? You are crazy...\n"); /* Load code page table */ if (spareblock->n_code_pages) - if (!(s->s_hpfs_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir))) + if (!(sbi->sb_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir))) printk("HPFS: Warning: code page support is disabled\n"); brelse(bh2); @@ -578,7 +590,7 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) brelse(bh0); hpfs_lock_iget(s, 1); - s->s_root = d_alloc_root(iget(s, s->s_hpfs_root)); + s->s_root = d_alloc_root(iget(s, sbi->sb_root)); hpfs_unlock_iget(s); if (!s->s_root || !s->s_root->d_inode) { printk("HPFS: iget failed. Why???\n"); @@ -590,7 +602,7 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) * find the root directory's . pointer & finish filling in the inode */ - root_dno = hpfs_fnode_dno(s, s->s_hpfs_root); + root_dno = hpfs_fnode_dno(s, sbi->sb_root); if (root_dno) de = map_dirent(s->s_root->d_inode, root_dno, "\001\001", 2, NULL, &qbh); if (!root_dno || !de) hpfs_error(s, "unable to find root dir"); @@ -612,8 +624,10 @@ bail3: brelse(bh1); bail2: brelse(bh0); bail1: bail0: - if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir); - if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table); + if (sbi->sb_bmp_dir) kfree(sbi->sb_bmp_dir); + if (sbi->sb_cp_table) kfree(sbi->sb_cp_table); + s->u.generic_sbp = NULL; + kfree(sbi); return -EINVAL; } diff --git a/include/linux/fs.h b/include/linux/fs.h index f0ba1e96325c..1157bf24d307 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -628,7 +628,6 @@ extern int send_sigurg(struct fown_struct *fown); #define MNT_DETACH 0x00000002 /* Just detach from the tree */ #include -#include extern struct list_head super_blocks; extern spinlock_t sb_lock; @@ -671,7 +670,6 @@ struct super_block { union { struct ext3_sb_info ext3_sb; - struct hpfs_sb_info hpfs_sb; void *generic_sbp; } u; /* diff --git a/include/linux/hpfs_fs_sb.h b/include/linux/hpfs_fs_sb.h index 068035e9a4bd..c6355adaf6f1 100644 --- a/include/linux/hpfs_fs_sb.h +++ b/include/linux/hpfs_fs_sb.h @@ -36,31 +36,4 @@ struct hpfs_sb_info { int sb_timeshift; }; -#define s_hpfs_root u.hpfs_sb.sb_root -#define s_hpfs_fs_size u.hpfs_sb.sb_fs_size -#define s_hpfs_bitmaps u.hpfs_sb.sb_bitmaps -#define s_hpfs_dirband_start u.hpfs_sb.sb_dirband_start -#define s_hpfs_dirband_size u.hpfs_sb.sb_dirband_size -#define s_hpfs_dmap u.hpfs_sb.sb_dmap -#define s_hpfs_uid u.hpfs_sb.sb_uid -#define s_hpfs_gid u.hpfs_sb.sb_gid -#define s_hpfs_mode u.hpfs_sb.sb_mode -#define s_hpfs_n_free u.hpfs_sb.sb_n_free -#define s_hpfs_n_free_dnodes u.hpfs_sb.sb_n_free_dnodes -#define s_hpfs_lowercase u.hpfs_sb.sb_lowercase -#define s_hpfs_conv u.hpfs_sb.sb_conv -#define s_hpfs_eas u.hpfs_sb.sb_eas -#define s_hpfs_err u.hpfs_sb.sb_err -#define s_hpfs_chk u.hpfs_sb.sb_chk -#define s_hpfs_was_error u.hpfs_sb.sb_was_error -#define s_hpfs_chkdsk u.hpfs_sb.sb_chkdsk -/*#define s_hpfs_rd_fnode u.hpfs_sb.sb_rd_fnode*/ -#define s_hpfs_rd_inode u.hpfs_sb.sb_rd_inode -#define s_hpfs_cp_table u.hpfs_sb.sb_cp_table -#define s_hpfs_bmp_dir u.hpfs_sb.sb_bmp_dir -#define s_hpfs_c_bitmap u.hpfs_sb.sb_c_bitmap -#define s_hpfs_iget_q u.hpfs_sb.sb_iget_q -/*#define s_hpfs_mounting u.hpfs_sb.sb_mounting*/ -#define s_hpfs_timeshift u.hpfs_sb.sb_timeshift - #endif -- cgit v1.2.3 From 5868a4993b5d980a5486e4b2bec11b88d8e016e6 Mon Sep 17 00:00:00 2001 From: Brian Gerst Date: Fri, 4 Oct 2002 20:16:00 -0700 Subject: [PATCH] struct super_block cleanup - ext3 Removes the last member of the union, ext3. --- include/linux/ext3_fs.h | 1 + include/linux/fs.h | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index d4550e28f37e..3f370ab642dd 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -17,6 +17,7 @@ #define _LINUX_EXT3_FS_H #include +#include /* * The second extended filesystem constants/structures diff --git a/include/linux/fs.h b/include/linux/fs.h index 1157bf24d307..ad6648e3fd9c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -627,8 +627,6 @@ extern int send_sigurg(struct fown_struct *fown); #define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */ #define MNT_DETACH 0x00000002 /* Just detach from the tree */ -#include - extern struct list_head super_blocks; extern spinlock_t sb_lock; @@ -669,7 +667,6 @@ struct super_block { char s_id[32]; /* Informational name */ union { - struct ext3_sb_info ext3_sb; void *generic_sbp; } u; /* -- cgit v1.2.3 From 9d66d9e91730e97f653c3143b637f1d63605f074 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 4 Oct 2002 20:34:40 -0700 Subject: [PATCH] remove debug code from list_del() It hasn't caught any bugs, and it is causing confusion over whether this is a permanent part of list_del() behaviour. --- include/linux/list.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/list.h b/include/linux/list.h index bd6f0ac3fb6b..634aab6c4c94 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -86,13 +86,12 @@ static inline void __list_del(struct list_head * prev, struct list_head * next) /** * list_del - deletes entry from list. * @entry: the element to delete from the list. - * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. */ static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); - entry->next = (void *) 0; - entry->prev = (void *) 0; } /** -- cgit v1.2.3 From d39755802e430876d612bc792e4c29652ed8b99b Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 4 Oct 2002 20:34:45 -0700 Subject: [PATCH] distinguish between address span of a zone and the number From David Mosberger The patch below fixes a bug in nr_free_zone_pages() which shows when a zone has hole. The problem is due to the fact that "struct zone" didn't keep track of the amount of real memory in a zone. Because of this, nr_free_zone_pages() simply assumed that a zone consists entirely of real memory. On machines with large holes, this has catastrophic effects on VM performance, because the VM system ends up thinking that there is plenty of memory left over in a zone, when in fact it may be completely full. The patch below fixes the problem by replacing the "size" member in "struct zone" with "spanned_pages" and "present_pages" and updating page_alloc.c. --- include/linux/mmzone.h | 3 ++- mm/page_alloc.c | 17 +++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index d7d12a69f505..dab0f76cfb20 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -120,7 +120,8 @@ struct zone { * rarely used fields: */ char *name; - unsigned long size; + unsigned long spanned_pages; /* total size, including holes */ + unsigned long present_pages; /* amount of memory (excluding holes) */ } ____cacheline_maxaligned_in_smp; #define ZONE_DMA 0 diff --git a/mm/page_alloc.c b/mm/page_alloc.c index b9cced8d19a0..9694db4322b0 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -48,7 +48,7 @@ static int zone_balance_max[MAX_NR_ZONES] __initdata = { 255 , 255, 255, }; */ static inline int bad_range(struct zone *zone, struct page *page) { - if (page_to_pfn(page) >= zone->zone_start_pfn + zone->size) + if (page_to_pfn(page) >= zone->zone_start_pfn + zone->spanned_pages) return 1; if (page_to_pfn(page) < zone->zone_start_pfn) return 1; @@ -509,7 +509,7 @@ static unsigned int nr_free_zone_pages(int offset) struct zone *zone; for (zone = *zonep++; zone; zone = *zonep++) { - unsigned long size = zone->size; + unsigned long size = zone->present_pages; unsigned long high = zone->pages_high; if (size > high) sum += size - high; @@ -681,7 +681,7 @@ void show_free_areas(void) struct zone *zone = &pgdat->node_zones[type]; unsigned long nr, flags, order, total = 0; - if (!zone->size) + if (!zone->present_pages) continue; spin_lock_irqsave(&zone->lock, flags); @@ -710,7 +710,7 @@ static int __init build_zonelists_node(pg_data_t *pgdat, struct zonelist *zoneli BUG(); case ZONE_HIGHMEM: zone = pgdat->node_zones + ZONE_HIGHMEM; - if (zone->size) { + if (zone->present_pages) { #ifndef CONFIG_HIGHMEM BUG(); #endif @@ -718,11 +718,11 @@ static int __init build_zonelists_node(pg_data_t *pgdat, struct zonelist *zoneli } case ZONE_NORMAL: zone = pgdat->node_zones + ZONE_NORMAL; - if (zone->size) + if (zone->present_pages) zonelist->zones[j++] = zone; case ZONE_DMA: zone = pgdat->node_zones + ZONE_DMA; - if (zone->size) + if (zone->present_pages) zonelist->zones[j++] = zone; } @@ -866,7 +866,8 @@ void __init free_area_init_core(pg_data_t *pgdat, realsize -= zholes_size[j]; printk(" %s zone: %lu pages\n", zone_names[j], realsize); - zone->size = size; + zone->spanned_pages = size; + zone->present_pages = realsize; zone->name = zone_names[j]; spin_lock_init(&zone->lock); spin_lock_init(&zone->lru_lock); @@ -1034,7 +1035,7 @@ static int frag_show(struct seq_file *m, void *arg) int order; for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) { - if (!zone->size) + if (!zone->present_pages) continue; spin_lock_irqsave(&zone->lock, flags); -- cgit v1.2.3 From bf3f607a57d27cab30d5ddfd203d873856bc22b7 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 4 Oct 2002 20:35:08 -0700 Subject: [PATCH] separation of direct-reclaim and kswapd functions There is some lack of clarity in what kswapd does and what direct-reclaim tasks do; try_to_free_pages() tries to service both functions, and they are different. - kswapd's role is to keep all zones on its node at zone->free_pages >= zone->pages_high. and to never stop as long as any zones do not meet that condition. - A direct reclaimer's role is to try to free some pages from the zones which are suitable for this particular allocation request, and to return when that has been achieved, or when all the relevant zones are at zone->free_pages >= zone->pages_high. The patch explicitly separates these two code paths; kswapd does not run try_to_free_pages() any more. kswapd should not be aware of zone fallbacks. --- include/linux/mmzone.h | 1 - mm/page_alloc.c | 3 - mm/vmscan.c | 230 +++++++++++++++++++++++-------------------------- 3 files changed, 110 insertions(+), 124 deletions(-) (limited to 'include') diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index dab0f76cfb20..2b83fca9e08d 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -62,7 +62,6 @@ struct zone { spinlock_t lock; unsigned long free_pages; unsigned long pages_min, pages_low, pages_high; - int need_balance; ZONE_PADDING(_pad1_) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 9694db4322b0..7763adf4073e 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -346,8 +346,6 @@ __alloc_pages(unsigned int gfp_mask, unsigned int order, } } - classzone->need_balance = 1; - mb(); /* we're somewhat low on memory, failed to find what we needed */ for (i = 0; zones[i] != NULL; i++) { struct zone *z = zones[i]; @@ -873,7 +871,6 @@ void __init free_area_init_core(pg_data_t *pgdat, spin_lock_init(&zone->lru_lock); zone->zone_pgdat = pgdat; zone->free_pages = 0; - zone->need_balance = 0; INIT_LIST_HEAD(&zone->active_list); INIT_LIST_HEAD(&zone->inactive_list); atomic_set(&zone->refill_counter, 0); diff --git a/mm/vmscan.c b/mm/vmscan.c index f8b879a35775..e97711f8127d 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -101,15 +101,19 @@ static inline int is_page_cache_freeable(struct page *page) return page_count(page) - !!PagePrivate(page) == 2; } + +/* + * shrink_list returns the number of reclaimed pages + */ static /* inline */ int -shrink_list(struct list_head *page_list, int nr_pages, - unsigned int gfp_mask, int *max_scan, int *nr_mapped) +shrink_list(struct list_head *page_list, unsigned int gfp_mask, + int *max_scan, int *nr_mapped) { struct address_space *mapping; LIST_HEAD(ret_pages); struct pagevec freed_pvec; - const int nr_pages_in = nr_pages; int pgactivate = 0; + int ret = 0; pagevec_init(&freed_pvec); while (!list_empty(page_list)) { @@ -295,7 +299,7 @@ shrink_list(struct list_head *page_list, int nr_pages, __put_page(page); /* The pagecache ref */ free_it: unlock_page(page); - nr_pages--; + ret++; if (!pagevec_add(&freed_pvec, page)) __pagevec_release_nonlru(&freed_pvec); continue; @@ -312,11 +316,11 @@ keep: list_splice(&ret_pages, page_list); if (pagevec_count(&freed_pvec)) __pagevec_release_nonlru(&freed_pvec); - mod_page_state(pgsteal, nr_pages_in - nr_pages); + mod_page_state(pgsteal, ret); if (current->flags & PF_KSWAPD) - mod_page_state(kswapd_steal, nr_pages_in - nr_pages); + mod_page_state(kswapd_steal, ret); mod_page_state(pgactivate, pgactivate); - return nr_pages; + return ret; } /* @@ -325,18 +329,19 @@ keep: * not freed will be added back to the LRU. * * shrink_cache() is passed the number of pages to try to free, and returns - * the number which are yet-to-free. + * the number of pages which were reclaimed. * * For pagecache intensive workloads, the first loop here is the hottest spot * in the kernel (apart from the copy_*_user functions). */ static /* inline */ int -shrink_cache(int nr_pages, struct zone *zone, +shrink_cache(const int nr_pages, struct zone *zone, unsigned int gfp_mask, int max_scan, int *nr_mapped) { LIST_HEAD(page_list); struct pagevec pvec; int nr_to_process; + int ret = 0; /* * Try to ensure that we free `nr_pages' pages in one pass of the loop. @@ -349,10 +354,11 @@ shrink_cache(int nr_pages, struct zone *zone, lru_add_drain(); spin_lock_irq(&zone->lru_lock); - while (max_scan > 0 && nr_pages > 0) { + while (max_scan > 0 && ret < nr_pages) { struct page *page; int nr_taken = 0; int nr_scan = 0; + int nr_freed; while (nr_scan++ < nr_to_process && !list_empty(&zone->inactive_list)) { @@ -383,10 +389,10 @@ shrink_cache(int nr_pages, struct zone *zone, max_scan -= nr_scan; mod_page_state(pgscan, nr_scan); - nr_pages = shrink_list(&page_list, nr_pages, - gfp_mask, &max_scan, nr_mapped); - - if (nr_pages <= 0 && list_empty(&page_list)) + nr_freed = shrink_list(&page_list, gfp_mask, + &max_scan, nr_mapped); + ret += nr_freed; + if (nr_freed <= 0 && list_empty(&page_list)) goto done; spin_lock_irq(&zone->lru_lock); @@ -412,7 +418,7 @@ shrink_cache(int nr_pages, struct zone *zone, spin_unlock_irq(&zone->lru_lock); done: pagevec_release(&pvec); - return nr_pages; + return ret; } /* @@ -533,9 +539,14 @@ refill_inactive_zone(struct zone *zone, const int nr_pages_in) mod_page_state(pgdeactivate, pgdeactivate); } +/* + * Try to reclaim `nr_pages' from this zone. Returns the number of reclaimed + * pages. This is a basic per-zone page freer. Used by both kswapd and + * direct reclaim. + */ static /* inline */ int -shrink_zone(struct zone *zone, int max_scan, - unsigned int gfp_mask, int nr_pages, int *nr_mapped) +shrink_zone(struct zone *zone, int max_scan, unsigned int gfp_mask, + const int nr_pages, int *nr_mapped) { unsigned long ratio; @@ -556,36 +567,60 @@ shrink_zone(struct zone *zone, int max_scan, atomic_sub(SWAP_CLUSTER_MAX, &zone->refill_counter); refill_inactive_zone(zone, SWAP_CLUSTER_MAX); } - nr_pages = shrink_cache(nr_pages, zone, gfp_mask, - max_scan, nr_mapped); - return nr_pages; + return shrink_cache(nr_pages, zone, gfp_mask, max_scan, nr_mapped); +} + +/* + * FIXME: don't do this for ZONE_HIGHMEM + */ +/* + * Here we assume it costs one seek to replace a lru page and that it also + * takes a seek to recreate a cache object. With this in mind we age equal + * percentages of the lru and ageable caches. This should balance the seeks + * generated by these structures. + * + * NOTE: for now I do this for all zones. If we find this is too aggressive + * on large boxes we may want to exclude ZONE_HIGHMEM. + * + * If we're encountering mapped pages on the LRU then increase the pressure on + * slab to avoid swapping. + */ +static void shrink_slab(int total_scanned, int gfp_mask) +{ + int shrink_ratio; + int pages = nr_used_zone_pages(); + + shrink_ratio = (pages / (total_scanned + 1)) + 1; + shrink_dcache_memory(shrink_ratio, gfp_mask); + shrink_icache_memory(shrink_ratio, gfp_mask); + shrink_dqcache_memory(shrink_ratio, gfp_mask); } +/* + * This is the direct reclaim path, for page-allocating processes. We only + * try to reclaim pages from zones which will satisfy the caller's allocation + * request. + */ static int shrink_caches(struct zone *classzone, int priority, - int *total_scanned, int gfp_mask, int nr_pages) + int *total_scanned, int gfp_mask, const int nr_pages) { struct zone *first_classzone; struct zone *zone; - int ratio; int nr_mapped = 0; - int pages = nr_used_zone_pages(); + int ret = 0; first_classzone = classzone->zone_pgdat->node_zones; for (zone = classzone; zone >= first_classzone; zone--) { int max_scan; int to_reclaim; - int unreclaimed; to_reclaim = zone->pages_high - zone->free_pages; if (to_reclaim < 0) continue; /* zone has enough memory */ - if (to_reclaim > SWAP_CLUSTER_MAX) - to_reclaim = SWAP_CLUSTER_MAX; - - if (to_reclaim < nr_pages) - to_reclaim = nr_pages; + to_reclaim = min(to_reclaim, SWAP_CLUSTER_MAX); + to_reclaim = max(to_reclaim, nr_pages); /* * If we cannot reclaim `nr_pages' pages by scanning twice @@ -594,33 +629,18 @@ shrink_caches(struct zone *classzone, int priority, max_scan = zone->nr_inactive >> priority; if (max_scan < to_reclaim * 2) max_scan = to_reclaim * 2; - unreclaimed = shrink_zone(zone, max_scan, - gfp_mask, to_reclaim, &nr_mapped); - nr_pages -= to_reclaim - unreclaimed; + ret += shrink_zone(zone, max_scan, gfp_mask, + to_reclaim, &nr_mapped); *total_scanned += max_scan; + *total_scanned += nr_mapped; + if (ret >= nr_pages) + break; } - - /* - * Here we assume it costs one seek to replace a lru page and that - * it also takes a seek to recreate a cache object. With this in - * mind we age equal percentages of the lru and ageable caches. - * This should balance the seeks generated by these structures. - * - * NOTE: for now I do this for all zones. If we find this is too - * aggressive on large boxes we may want to exclude ZONE_HIGHMEM - * - * If we're encountering mapped pages on the LRU then increase the - * pressure on slab to avoid swapping. - */ - ratio = (pages / (*total_scanned + nr_mapped + 1)) + 1; - shrink_dcache_memory(ratio, gfp_mask); - shrink_icache_memory(ratio, gfp_mask); - shrink_dqcache_memory(ratio, gfp_mask); - return nr_pages; + return ret; } /* - * This is the main entry point to page reclaim. + * This is the main entry point to direct page reclaim. * * If a full scan of the inactive list fails to free enough memory then we * are "out of memory" and something needs to be killed. @@ -640,17 +660,18 @@ int try_to_free_pages(struct zone *classzone, unsigned int gfp_mask, unsigned int order) { - int priority = DEF_PRIORITY; - int nr_pages = SWAP_CLUSTER_MAX; + int priority; + const int nr_pages = SWAP_CLUSTER_MAX; + int nr_reclaimed = 0; inc_page_state(pageoutrun); for (priority = DEF_PRIORITY; priority; priority--) { int total_scanned = 0; - nr_pages = shrink_caches(classzone, priority, &total_scanned, - gfp_mask, nr_pages); - if (nr_pages <= 0) + nr_reclaimed += shrink_caches(classzone, priority, + &total_scanned, gfp_mask, nr_pages); + if (nr_reclaimed >= nr_pages) return 1; if (total_scanned == 0) return 1; /* All zones had enough free memory */ @@ -665,62 +686,46 @@ try_to_free_pages(struct zone *classzone, /* Take a nap, wait for some writeback to complete */ blk_congestion_wait(WRITE, HZ/4); + shrink_slab(total_scanned, gfp_mask); } if (gfp_mask & __GFP_FS) out_of_memory(); return 0; } -static int check_classzone_need_balance(struct zone *classzone) +/* + * kswapd will work across all this node's zones until they are all at + * pages_high. + */ +static void kswapd_balance_pgdat(pg_data_t *pgdat) { - struct zone *first_classzone; + int priority = DEF_PRIORITY; + int i; - first_classzone = classzone->zone_pgdat->node_zones; - while (classzone >= first_classzone) { - if (classzone->free_pages > classzone->pages_high) - return 0; - classzone--; - } - return 1; -} + for (priority = DEF_PRIORITY; priority; priority--) { + int success = 1; -static int kswapd_balance_pgdat(pg_data_t * pgdat) -{ - int need_more_balance = 0, i; - struct zone *zone; + for (i = 0; i < pgdat->nr_zones; i++) { + struct zone *zone = pgdat->node_zones + i; + int nr_mapped = 0; + int max_scan; + int to_reclaim; - for (i = pgdat->nr_zones-1; i >= 0; i--) { - zone = pgdat->node_zones + i; - cond_resched(); - if (!zone->need_balance) - continue; - if (!try_to_free_pages(zone, GFP_KSWAPD, 0)) { - zone->need_balance = 0; - __set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); - continue; + to_reclaim = zone->pages_high - zone->free_pages; + if (to_reclaim <= 0) + continue; + success = 0; + max_scan = zone->nr_inactive >> priority; + if (max_scan < to_reclaim * 2) + max_scan = to_reclaim * 2; + shrink_zone(zone, max_scan, GFP_KSWAPD, + to_reclaim, &nr_mapped); + shrink_slab(max_scan + nr_mapped, GFP_KSWAPD); } - if (check_classzone_need_balance(zone)) - need_more_balance = 1; - else - zone->need_balance = 0; - } - - return need_more_balance; -} - -static int kswapd_can_sleep_pgdat(pg_data_t * pgdat) -{ - struct zone *zone; - int i; - - for (i = pgdat->nr_zones-1; i >= 0; i--) { - zone = pgdat->node_zones + i; - if (zone->need_balance) - return 0; + if (success) + break; /* All zones are at pages_high */ + blk_congestion_wait(WRITE, HZ/4); } - - return 1; } /* @@ -740,7 +745,7 @@ int kswapd(void *p) { pg_data_t *pgdat = (pg_data_t*)p; struct task_struct *tsk = current; - DECLARE_WAITQUEUE(wait, tsk); + DEFINE_WAIT(wait); daemonize(); set_cpus_allowed(tsk, __node_to_cpu_mask(pgdat->node_id)); @@ -761,27 +766,12 @@ int kswapd(void *p) */ tsk->flags |= PF_MEMALLOC|PF_KSWAPD; - /* - * Kswapd main loop. - */ - for (;;) { + for ( ; ; ) { if (current->flags & PF_FREEZE) refrigerator(PF_IOTHREAD); - __set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&pgdat->kswapd_wait, &wait); - - mb(); - if (kswapd_can_sleep_pgdat(pgdat)) - schedule(); - - __set_current_state(TASK_RUNNING); - remove_wait_queue(&pgdat->kswapd_wait, &wait); - - /* - * If we actually get into a low-memory situation, - * the processes needing more memory will wake us - * up on a more timely basis. - */ + prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE); + schedule(); + finish_wait(&pgdat->kswapd_wait, &wait); kswapd_balance_pgdat(pgdat); blk_run_queues(); } -- cgit v1.2.3 From 4ac833da2fec12985c33f4f23a446ff09950dd1f Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 4 Oct 2002 20:35:37 -0700 Subject: [PATCH] remove write_mapping_buffers() When the global buffer LRU was present, dirty ext2 indirect blocks were automatically scheduled for writeback alongside their data. I added write_mapping_buffers() to replace this - the idea was to schedule the indirects close in time to the scheduling of their data. It works OK for small-to-medium sized files but for large, linear writes it doesn't work: the request queue is completely full of file data and when we later come to scheduling the indirects, their neighbouring data has already been written. So writeback of really huge files tends to be a bit seeky. So. Kill it. Will fix this problem by other means. --- fs/buffer.c | 75 --------------------------------------------- fs/ext2/inode.c | 9 +----- fs/ext3/inode.c | 9 +----- include/linux/buffer_head.h | 1 - 4 files changed, 2 insertions(+), 92 deletions(-) (limited to 'include') diff --git a/fs/buffer.c b/fs/buffer.c index d1da2c0ffac8..b00fb755781d 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -735,81 +735,6 @@ int sync_mapping_buffers(struct address_space *mapping) } EXPORT_SYMBOL(sync_mapping_buffers); -/** - * write_mapping_buffers - Start writeout of a mapping's "associated" buffers. - * @mapping - the mapping which wants those buffers written. - * - * Starts I/O against dirty buffers which are on @mapping->private_list. - * Those buffers must be backed by @mapping->assoc_mapping. - * - * The private_list buffers generally contain filesystem indirect blocks. - * The idea is that the filesystem can start I/O against the indirects at - * the same time as running generic_writepages(), so the indirect's - * I/O will be merged with the data. - * - * We sneakliy write the buffers in probable tail-to-head order. This is - * because generic_writepages() writes in probable head-to-tail - * order. If the file is so huge that the data or the indirects overflow - * the request queue we will at least get some merging this way. - * - * Any clean+unlocked buffers are de-listed. clean/locked buffers must be - * left on the list for an fsync() to wait on. - * - * Couldn't think of a smart way of avoiding livelock, so chose the dumb - * way instead. - * - * FIXME: duplicates fsync_inode_buffers() functionality a bit. - */ -int write_mapping_buffers(struct address_space *mapping) -{ - spinlock_t *lock; - struct address_space *buffer_mapping; - unsigned nr_to_write; /* livelock avoidance */ - struct list_head *lh; - int ret = 0; - - if (list_empty(&mapping->private_list)) - goto out; - - buffer_mapping = mapping->assoc_mapping; - lock = &buffer_mapping->private_lock; - spin_lock(lock); - nr_to_write = 0; - lh = mapping->private_list.next; - while (lh != &mapping->private_list) { - lh = lh->next; - nr_to_write++; - } - nr_to_write *= 2; /* Allow for some late additions */ - - while (nr_to_write-- && !list_empty(&mapping->private_list)) { - struct buffer_head *bh; - - bh = BH_ENTRY(mapping->private_list.prev); - list_del_init(&bh->b_assoc_buffers); - if (!buffer_dirty(bh) && !buffer_locked(bh)) - continue; - /* Stick it on the far end of the list. Order is preserved. */ - list_add(&bh->b_assoc_buffers, &mapping->private_list); - if (test_set_buffer_locked(bh)) - continue; - get_bh(bh); - spin_unlock(lock); - if (test_clear_buffer_dirty(bh)) { - bh->b_end_io = end_buffer_io_sync; - submit_bh(WRITE, bh); - } else { - unlock_buffer(bh); - put_bh(bh); - } - spin_lock(lock); - } - spin_unlock(lock); -out: - return ret; -} -EXPORT_SYMBOL(write_mapping_buffers); - void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode) { struct address_space *mapping = inode->i_mapping; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 99627183120e..d27313d1dd10 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -629,14 +629,7 @@ ext2_direct_IO(int rw, struct inode *inode, const struct iovec *iov, static int ext2_writepages(struct address_space *mapping, struct writeback_control *wbc) { - int ret; - int err; - - ret = write_mapping_buffers(mapping); - err = mpage_writepages(mapping, wbc, ext2_get_block); - if (!ret) - ret = err; - return ret; + return mpage_writepages(mapping, wbc, ext2_get_block); } struct address_space_operations ext2_aops = { diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 978e9e60d070..5b2c49a9b34e 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1477,14 +1477,7 @@ struct address_space_operations ext3_aops = { static int ext3_writepages(struct address_space *mapping, struct writeback_control *wbc) { - int ret; - int err; - - ret = write_mapping_buffers(mapping); - err = mpage_writepages(mapping, wbc, ext3_get_block); - if (!ret) - ret = err; - return ret; + return mpage_writepages(mapping, wbc, ext3_get_block); } #endif diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index dd8f1bfb150e..7ad9fafbea0b 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -140,7 +140,6 @@ void end_buffer_io_sync(struct buffer_head *bh, int uptodate); void buffer_insert_list(spinlock_t *lock, struct buffer_head *, struct list_head *); void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode); -int write_mapping_buffers(struct address_space *mapping); int inode_has_buffers(struct inode *); void invalidate_inode_buffers(struct inode *); int fsync_buffers_list(spinlock_t *lock, struct list_head *); -- cgit v1.2.3 From 343893e647d27c96bf07e3f549b77b89aa9581ce Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 4 Oct 2002 20:35:43 -0700 Subject: [PATCH] use buffer_boundary() for writeback scheduling hints This is the replacement for write_mapping_buffers(). Whenever the mpage code sees that it has just written a block which had buffer_boundary() set, it assumes that the next block is dirty filesystem metadata. (This is a good assumption - that's what buffer_boundary is for). So we do a lookup in the blockdev mapping for the next block and it if is present and dirty, then schedule it for IO. So the indirect blocks in the blockdev mapping get merged with the data blocks in the file mapping. This is a bit more general than the write_mapping_buffers() approach. write_mapping_buffers() required that the fs carefully maintain the correct buffers on the mapping->private_list, and that the fs call write_mapping_buffers(), and the implementation was generally rather yuk. This version will "just work" for filesystems which implement buffer_boundary correctly. Currently this is ext2, ext3 and some not-yet-merged reiserfs patches. JFS implements buffer_boundary() but does not use ext2-like layouts - so there will be no change there. Works nicely. --- fs/buffer.c | 17 +++++++++++++++++ fs/mpage.c | 19 +++++++++++++++++-- include/linux/buffer_head.h | 3 +++ 3 files changed, 37 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/buffer.c b/fs/buffer.c index b00fb755781d..f989f5fdd070 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -735,6 +735,23 @@ int sync_mapping_buffers(struct address_space *mapping) } EXPORT_SYMBOL(sync_mapping_buffers); +/* + * Called when we've recently written block `bblock', and it is known that + * `bblock' was for a buffer_boundary() buffer. This means that the block at + * `bblock + 1' is probably a dirty indirect block. Hunt it down and, if it's + * dirty, schedule it for IO. So that indirects merge nicely with their data. + */ +void write_boundary_block(struct block_device *bdev, + sector_t bblock, unsigned blocksize) +{ + struct buffer_head *bh = __find_get_block(bdev, bblock + 1, blocksize); + if (bh) { + if (buffer_dirty(bh)) + ll_rw_block(WRITE, 1, &bh); + put_bh(bh); + } +} + void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode) { struct address_space *mapping = inode->i_mapping; diff --git a/fs/mpage.c b/fs/mpage.c index b082adc807ff..2cbd183dfd7c 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -331,6 +331,8 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, unsigned first_unmapped = blocks_per_page; struct block_device *bdev = NULL; int boundary = 0; + sector_t boundary_block = 0; + struct block_device *boundary_bdev = NULL; if (page_has_buffers(page)) { struct buffer_head *head = page_buffers(page); @@ -363,6 +365,10 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, } blocks[page_block++] = bh->b_blocknr; boundary = buffer_boundary(bh); + if (boundary) { + boundary_block = bh->b_blocknr; + boundary_bdev = bh->b_bdev; + } bdev = bh->b_bdev; } while ((bh = bh->b_this_page) != head); @@ -393,6 +399,10 @@ mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, if (buffer_new(&map_bh)) unmap_underlying_metadata(map_bh.b_bdev, map_bh.b_blocknr); + if (buffer_boundary(&map_bh)) { + boundary_block = map_bh.b_blocknr; + boundary_bdev = map_bh.b_bdev; + } if (page_block) { if (map_bh.b_blocknr != blocks[page_block-1] + 1) goto confused; @@ -464,10 +474,15 @@ alloc_new: BUG_ON(PageWriteback(page)); SetPageWriteback(page); unlock_page(page); - if (boundary || (first_unmapped != blocks_per_page)) + if (boundary || (first_unmapped != blocks_per_page)) { bio = mpage_bio_submit(WRITE, bio); - else + if (boundary_block) { + write_boundary_block(boundary_bdev, + boundary_block, 1 << blkbits); + } + } else { *last_block_in_bio = blocks[blocks_per_page - 1]; + } goto out; confused: diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 7ad9fafbea0b..71732e1216fc 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -167,6 +167,9 @@ void free_buffer_head(struct buffer_head * bh); void FASTCALL(unlock_buffer(struct buffer_head *bh)); void ll_rw_block(int, int, struct buffer_head * bh[]); int submit_bh(int, struct buffer_head *); +void write_boundary_block(struct block_device *bdev, + sector_t bblock, unsigned blocksize); + extern int buffer_heads_over_limit; /* -- cgit v1.2.3 From a27efcaff9ffd5ad05f4e111751da41a8820f7ab Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 4 Oct 2002 20:35:48 -0700 Subject: [PATCH] remove page->virtual The patch removes page->virtual for all architectures which do not define WANT_PAGE_VIRTUAL. Hash for it instead. Possibly we could define WANT_PAGE_VIRTUAL for CONFIG_HIGHMEM4G, but it seems unlikely. A lot of the pressure went off kmap() and page_address() as a result of the move to kmap_atomic(). That should be the preferred way to address CPU load in the set_page_address() and page_address() hashing and locking. If kmap_atomic is not usable then the next best approach is for users to cache the result of kmap() in a local rather than calling page_address() repeatedly. One heavy user of kmap() and page_address() is the ext2 directory code. On a 7G Quad PIII, running four concurrent instances of while true do find /usr/src/linux > /dev/null done on ext2 with everything cached, profiling shows that the new hashed set_page_address() and page_address() implementations consume 0.4% and 1.3% of CPU time respectively. I think that's OK. --- include/linux/mm.h | 48 +++++++++---------- init/main.c | 1 + kernel/ksyms.c | 3 ++ mm/highmem.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++--- mm/page_alloc.c | 5 +- 5 files changed, 156 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/linux/mm.h b/include/linux/mm.h index 4ae8eb10dcb2..a5107b5043f7 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -176,7 +176,7 @@ struct page { * Architectures with slow multiplication can define * WANT_PAGE_VIRTUAL in asm/page.h */ -#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL) +#if defined(WANT_PAGE_VIRTUAL) void *virtual; /* Kernel virtual address (NULL if not kmapped, ie. highmem) */ #endif /* CONFIG_HIGMEM || WANT_PAGE_VIRTUAL */ @@ -289,38 +289,34 @@ static inline void set_page_zone(struct page *page, unsigned long zone_num) page->flags |= zone_num << ZONE_SHIFT; } -/* - * In order to avoid #ifdefs within C code itself, we define - * set_page_address to a noop for non-highmem machines, where - * the field isn't useful. - * The same is true for page_address() in arch-dependent code. - */ -#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL) +#define lowmem_page_address(page) \ + __va( ( ((page) - page_zone(page)->zone_mem_map) \ + + page_zone(page)->zone_start_pfn) << PAGE_SHIFT) + +#if defined(CONFIG_HIGHMEM) && !defined(WANT_PAGE_VIRTUAL) +#define HASHED_PAGE_VIRTUAL +#endif +#if defined(WANT_PAGE_VIRTUAL) +#define page_address(page) ((page)->virtual) #define set_page_address(page, address) \ do { \ (page)->virtual = (address); \ } while(0) +#define page_address_init() do { } while(0) +#endif -#else /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */ -#define set_page_address(page, address) do { } while(0) -#endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */ - -/* - * Permanent address of a page. Obviously must never be - * called on a highmem page. - */ -#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL) - -#define page_address(page) ((page)->virtual) - -#else /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */ - -#define page_address(page) \ - __va( ( ((page) - page_zone(page)->zone_mem_map) \ - + page_zone(page)->zone_start_pfn) << PAGE_SHIFT) +#if defined(HASHED_PAGE_VIRTUAL) +void *page_address(struct page *page); +void set_page_address(struct page *page, void *virtual); +void page_address_init(void); +#endif -#endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */ +#if !defined(HASHED_PAGE_VIRTUAL) && !defined(WANT_PAGE_VIRTUAL) +#define page_address(page) lowmem_page_address(page) +#define set_page_address(page, address) do { } while(0) +#define page_address_init() do { } while(0) +#endif /* * Return true if this page is mapped into pagetables. Subtle: test pte.direct diff --git a/init/main.c b/init/main.c index 3cf336cf6f8d..f69c298b9a6f 100644 --- a/init/main.c +++ b/init/main.c @@ -433,6 +433,7 @@ asmlinkage void __init start_kernel(void) initrd_start = 0; } #endif + page_address_init(); mem_init(); kmem_cache_sizes_init(); pidhash_init(); diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 4954fc80381f..56852f3be54c 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -132,6 +132,9 @@ EXPORT_SYMBOL(highmem_start_page); EXPORT_SYMBOL(kmap_prot); EXPORT_SYMBOL(kmap_pte); #endif +#ifdef HASHED_PAGE_VIRTUAL +EXPORT_SYMBOL(page_address); +#endif EXPORT_SYMBOL(get_user_pages); /* filesystem internal functions */ diff --git a/mm/highmem.c b/mm/highmem.c index 11d599cacbd0..68fedb0e559f 100644 --- a/mm/highmem.c +++ b/mm/highmem.c @@ -22,6 +22,7 @@ #include #include #include +#include #include static mempool_t *page_pool, *isa_page_pool; @@ -88,7 +89,7 @@ static void flush_all_zero_pkmaps(void) page = pte_page(pkmap_page_table[i]); pte_clear(&pkmap_page_table[i]); - page->virtual = NULL; + set_page_address(page, NULL); } flush_tlb_kernel_range(PKMAP_ADDR(0), PKMAP_ADDR(LAST_PKMAP)); } @@ -126,8 +127,8 @@ start: spin_lock(&kmap_lock); /* Somebody else might have mapped it while we slept */ - if (page->virtual) - return (unsigned long) page->virtual; + if (page_address(page)) + return (unsigned long)page_address(page); /* Re-start */ goto start; @@ -137,7 +138,7 @@ start: set_pte(&(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot)); pkmap_count[last_pkmap_nr] = 1; - page->virtual = (void *) vaddr; + set_page_address(page, (void *)vaddr); return vaddr; } @@ -153,7 +154,7 @@ void *kmap_high(struct page *page) * We cannot call this from interrupts, as it may block */ spin_lock(&kmap_lock); - vaddr = (unsigned long) page->virtual; + vaddr = (unsigned long)page_address(page); if (!vaddr) vaddr = map_new_virtual(page); pkmap_count[PKMAP_NR(vaddr)]++; @@ -170,7 +171,7 @@ void kunmap_high(struct page *page) int need_wakeup; spin_lock(&kmap_lock); - vaddr = (unsigned long) page->virtual; + vaddr = (unsigned long)page_address(page); if (!vaddr) BUG(); nr = PKMAP_NR(vaddr); @@ -467,7 +468,7 @@ void blk_queue_bounce(request_queue_t *q, struct bio **bio_orig) *bio_orig = bio; } -#if CONFIG_DEBUG_HIGHMEM +#if defined(CONFIG_DEBUG_HIGHMEM) && defined(CONFIG_HIGHMEM) void check_highmem_ptes(void) { int idx, type; @@ -484,3 +485,121 @@ void check_highmem_ptes(void) } #endif +#if defined(HASHED_PAGE_VIRTUAL) + +#define PA_HASH_ORDER 7 + +/* + * Describes one page->virtual association + */ +struct page_address_map { + struct page *page; + void *virtual; + struct list_head list; +}; + +/* + * page_address_map freelist, allocated from page_address_maps. + */ +static struct list_head page_address_pool; /* freelist */ +static spinlock_t pool_lock; /* protects page_address_pool */ + +/* + * Hash table bucket + */ +static struct page_address_slot { + struct list_head lh; /* List of page_address_maps */ + spinlock_t lock; /* Protect this bucket's list */ +} ____cacheline_aligned_in_smp page_address_htable[1<lock, flags); + if (!list_empty(&pas->lh)) { + struct page_address_map *pam; + + list_for_each_entry(pam, &pas->lh, list) { + if (pam->page == page) { + ret = pam->virtual; + goto done; + } + } + } +done: + spin_unlock_irqrestore(&pas->lock, flags); + return ret; +} + +void set_page_address(struct page *page, void *virtual) +{ + unsigned long flags; + struct page_address_slot *pas; + struct page_address_map *pam; + + BUG_ON(!PageHighMem(page)); + + pas = page_slot(page); + if (virtual) { /* Add */ + BUG_ON(list_empty(&page_address_pool)); + + spin_lock_irqsave(&pool_lock, flags); + pam = list_entry(page_address_pool.next, + struct page_address_map, list); + list_del(&pam->list); + spin_unlock_irqrestore(&pool_lock, flags); + + pam->page = page; + pam->virtual = virtual; + + spin_lock_irqsave(&pas->lock, flags); + list_add_tail(&pam->list, &pas->lh); + spin_unlock_irqrestore(&pas->lock, flags); + } else { /* Remove */ + spin_lock_irqsave(&pas->lock, flags); + list_for_each_entry(pam, &pas->lh, list) { + if (pam->page == page) { + list_del(&pam->list); + spin_unlock_irqrestore(&pas->lock, flags); + spin_lock_irqsave(&pool_lock, flags); + list_add_tail(&pam->list, &page_address_pool); + spin_unlock_irqrestore(&pool_lock, flags); + goto done; + } + } + spin_unlock_irqrestore(&pas->lock, flags); + } +done: + return; +} + +static struct page_address_map page_address_maps[LAST_PKMAP]; + +void __init page_address_init(void) +{ + int i; + + INIT_LIST_HEAD(&page_address_pool); + for (i = 0; i < ARRAY_SIZE(page_address_maps); i++) + list_add(&page_address_maps[i].list, &page_address_pool); + for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) { + INIT_LIST_HEAD(&page_address_htable[i].lh); + spin_lock_init(&page_address_htable[i].lock); + } + spin_lock_init(&pool_lock); +} + +#endif /* defined(CONFIG_HIGHMEM) && !defined(WANT_PAGE_VIRTUAL) */ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 7763adf4073e..4f892bb13250 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -921,12 +921,15 @@ void __init free_area_init_core(pg_data_t *pgdat, set_page_count(page, 0); SetPageReserved(page); INIT_LIST_HEAD(&page->list); +#ifdef WANT_PAGE_VIRTUAL if (j != ZONE_HIGHMEM) /* * The shift left won't overflow because the * ZONE_NORMAL is below 4G. */ - set_page_address(page, __va(zone_start_pfn << PAGE_SHIFT)); + set_page_address(page, + __va(zone_start_pfn << PAGE_SHIFT)); +#endif zone_start_pfn++; } -- cgit v1.2.3 From ae8172699dc084417d18f0f839e220cfd3363166 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 6 Oct 2002 01:30:10 +0100 Subject: [SERIAL] Allow PCMCIA serial cards to work again. The PCMCIA layer claims the IO or memory regions for all cards. This means that any port registered via 8250_cs must not cause the 8250 code to claim the resources itself. We also add support for iomem-based ports at initialisation time for PPC. --- drivers/serial/8250.c | 53 +++++++++++++++++++++++++++------------------ drivers/serial/8250.h | 5 ++++- include/linux/serial_core.h | 2 ++ 3 files changed, 38 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 446243c8f4b9..ce9319f16132 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1560,21 +1560,22 @@ static int serial8250_request_port(struct uart_port *port) { struct uart_8250_port *up = (struct uart_8250_port *)port; struct resource *res = NULL, *res_rsa = NULL; - int ret = -EBUSY; + int ret = 0; - if (up->port.type == PORT_RSA) { - ret = serial8250_request_rsa_resource(up, &res_rsa); - if (ret) - return ret; - } + if (up->port.flags & UPF_RESOURCES) { + if (up->port.type == PORT_RSA) { + ret = serial8250_request_rsa_resource(up, &res_rsa); + if (ret) + return ret; + } - ret = serial8250_request_std_resource(up, &res); + ret = serial8250_request_std_resource(up, &res); + } /* * If we have a mapbase, then request that as well. */ - if (res != NULL && up->port.iotype == SERIAL_IO_MEM && - up->port.mapbase) { + if (ret == 0 && up->port.flags & UPF_IOREMAP) { int size = res->end - res->start + 1; up->port.membase = ioremap(up->port.mapbase, size); @@ -1610,13 +1611,17 @@ static void serial8250_config_port(struct uart_port *port, int flags) * Find the region that we can probe for. This in turn * tells us whether we can probe for the type of port. */ - ret = serial8250_request_std_resource(up, &res_std); - if (ret) - return; + if (up->port.flags & UPF_RESOURCES) { + ret = serial8250_request_std_resource(up, &res_std); + if (ret) + return; - ret = serial8250_request_rsa_resource(up, &res_rsa); - if (ret) + ret = serial8250_request_rsa_resource(up, &res_rsa); + if (ret) + probeflags &= ~PROBE_RSA; + } else { probeflags &= ~PROBE_RSA; + } if (flags & UART_CONFIG_TYPE) autoconfig(up, probeflags); @@ -1678,6 +1683,7 @@ static struct uart_8250_port serial8250_ports[UART_NR]; static void __init serial8250_isa_init_ports(void) { + struct uart_8250_port *up; static int first = 1; int i; @@ -1685,13 +1691,18 @@ static void __init serial8250_isa_init_ports(void) return; first = 0; - for (i = 0; i < ARRAY_SIZE(old_serial_port); i++) { - serial8250_ports[i].port.iobase = old_serial_port[i].port; - serial8250_ports[i].port.irq = irq_cannonicalize(old_serial_port[i].irq); - serial8250_ports[i].port.uartclk = old_serial_port[i].base_baud * 16; - serial8250_ports[i].port.flags = old_serial_port[i].flags; - serial8250_ports[i].port.hub6 = old_serial_port[i].hub6; - serial8250_ports[i].port.ops = &serial8250_pops; + for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port); + i++, up++) { + up->port.iobase = old_serial_port[i].port; + up->port.irq = irq_cannonicalize(old_serial_port[i].irq); + up->port.uartclk = old_serial_port[i].baud_base * 16; + up->port.flags = old_serial_port[i].flags | + UPF_RESOURCES; + up->port.hub6 = old_serial_port[i].hub6; + up->port.membase = old_serial_port[i].iomem_base; + up->port.iotype = old_serial_port[i].io_type; + up->port.regshift = old_serial_port[i].iomem_reg_shift; + up->port.ops = &serial8250_pops; } } diff --git a/drivers/serial/8250.h b/drivers/serial/8250.h index 7e40a4f497e4..030116e6ebf6 100644 --- a/drivers/serial/8250.h +++ b/drivers/serial/8250.h @@ -30,11 +30,14 @@ void serial8250_get_irq_map(unsigned int *map); struct old_serial_port { unsigned int uart; - unsigned int base_baud; + unsigned int baud_base; unsigned int port; unsigned int irq; unsigned int flags; unsigned char hub6; + unsigned char io_type; + unsigned char *iomem_base; + unsigned short iomem_reg_shift; }; #undef SERIAL_DEBUG_PCI diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 8cf9eccab7c1..c38330747ee1 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -168,6 +168,8 @@ struct uart_port { #define UPF_BUGGY_UART (1 << 14) #define UPF_AUTOPROBE (1 << 15) #define UPF_BOOT_AUTOCONF (1 << 28) +#define UPF_RESOURCES (1 << 30) +#define UPF_IOREMAP (1 << 31) #define UPF_FLAGS (0x7fff) #define UPF_USR_MASK (UPF_SPD_MASK|UPF_LOW_LATENCY) -- cgit v1.2.3 From 529ba807382a8fb05b096d4194c7bd16377f34ed Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Sat, 5 Oct 2002 04:22:13 -0700 Subject: [PATCH] nbd switched to alloc_disk() --- drivers/block/nbd.c | 29 ++++++++++++++++++++++------- include/linux/nbd.h | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index e554bfb4d331..be27027d32b8 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -444,15 +444,15 @@ static int nbd_ioctl(struct inode *inode, struct file *file, temp >>= 1; } nbd_bytesizes[dev] &= ~(nbd_blksizes[dev]-1); - set_capacity(&lo->disk, nbd_bytesizes[dev] >> 9); + set_capacity(lo->disk, nbd_bytesizes[dev] >> 9); return 0; case NBD_SET_SIZE: nbd_bytesizes[dev] = arg & ~(nbd_blksizes[dev]-1); - set_capacity(&lo->disk, nbd_bytesizes[dev] >> 9); + set_capacity(lo->disk, nbd_bytesizes[dev] >> 9); return 0; case NBD_SET_SIZE_BLOCKS: nbd_bytesizes[dev] = ((u64) arg) << nbd_blksize_bits[dev]; - set_capacity(&lo->disk, nbd_bytesizes[dev] >> 9); + set_capacity(lo->disk, nbd_bytesizes[dev] >> 9); return 0; case NBD_DO_IT: if (!lo->file) @@ -498,6 +498,7 @@ static struct block_device_operations nbd_fops = static int __init nbd_init(void) { + int err = -ENOMEM; int i; if (sizeof(struct nbd_request) != 28) { @@ -505,17 +506,25 @@ static int __init nbd_init(void) return -EIO; } + for (i = 0; i < MAX_NBD; i++) { + struct gendisk *disk = alloc_disk(); + if (!disk) + goto out; + nbd_dev[i].disk = disk; + } + if (register_blkdev(MAJOR_NR, "nbd", &nbd_fops)) { printk("Unable to get major number %d for NBD\n", MAJOR_NR); - return -EIO; + err = -EIO; + goto out; } #ifdef MODULE printk("nbd: registered device at major %d\n", MAJOR_NR); #endif blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_nbd_request, &nbd_lock); for (i = 0; i < MAX_NBD; i++) { - struct gendisk *disk = &nbd_dev[i].disk; + struct gendisk *disk = nbd_dev[i].disk; nbd_dev[i].refcnt = 0; nbd_dev[i].file = NULL; nbd_dev[i].magic = LO_MAGIC; @@ -541,13 +550,19 @@ static int __init nbd_init(void) &nbd_fops, NULL); return 0; +out: + while (i--) + put_disk(nbd_dev[i].disk); + return err; } static void __exit nbd_cleanup(void) { int i; - for (i = 0; i < MAX_NBD; i++) - del_gendisk(&nbd_dev[i].disk); + for (i = 0; i < MAX_NBD; i++) { + del_gendisk(nbd_dev[i].disk); + put_disk(nbd_dev[i].disk); + } devfs_unregister (devfs_handle); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); diff --git a/include/linux/nbd.h b/include/linux/nbd.h index 8e4b8c236e8b..b20c045dbb79 100644 --- a/include/linux/nbd.h +++ b/include/linux/nbd.h @@ -79,7 +79,7 @@ struct nbd_device { spinlock_t queue_lock; struct list_head queue_head; /* Requests are added here... */ struct semaphore tx_lock; - struct gendisk disk; + struct gendisk *disk; }; #endif -- cgit v1.2.3 From 4d1fc631c727b96c5fc7de3e7aa3bef70f57dddf Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Sat, 5 Oct 2002 04:22:53 -0700 Subject: [PATCH] amiga floppy switched to alloc_disk() --- drivers/block/amiflop.c | 47 +++++++++++++++++++++++++++-------------------- include/linux/amifd.h | 2 +- 2 files changed, 28 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index 53c752eed240..22790c4145fe 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -1643,7 +1643,7 @@ static int floppy_open(struct inode *inode, struct file *filp) unit[drive].dtype=&data_types[system]; unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks* data_types[system].sects*unit[drive].type->sect_mult; - set_capacity(&unit[drive].disk, unit[drive].blocks); + set_capacity(unit[drive].gendisk, unit[drive].blocks); printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive, unit[drive].type->name, data_types[system].name); @@ -1731,25 +1731,31 @@ static int __init fd_probe_drives(void) drives=0; nomem=0; for(drive=0;drivecode != FD_NODRIVE) { - struct gendisk *disk = &unit[drive].disk; - drives++; - if ((unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL)) == NULL) { - printk("no mem for "); - unit[drive].type = &drive_types[num_dr_types - 1]; /* FD_NODRIVE */ - drives--; - nomem = 1; - } - printk("fd%d ",drive); - disk->major = MAJOR_NR; - disk->first_minor = drive; - disk->minor_shift = 0; - disk->fops = &floppy_fops; - sprintf(disk->disk_name, "fd%d", drive); - set_capacity(disk, 880*2); - add_disk(disk); + if (unit[drive].type->code == FD_NODRIVE) + continue; + disk = alloc_disk(); + if (!disk) { + unit[drive].type->code = FD_NODRIVE; + continue; + } + unit[drive].gendisk = disk; + drives++; + if ((unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL)) == NULL) { + printk("no mem for "); + unit[drive].type = &drive_types[num_dr_types - 1]; /* FD_NODRIVE */ + drives--; + nomem = 1; } + printk("fd%d ",drive); + disk->major = MAJOR_NR; + disk->first_minor = drive; + disk->minor_shift = 0; + disk->fops = &floppy_fops; + sprintf(disk->disk_name, "fd%d", drive); + set_capacity(disk, 880*2); + add_disk(disk); } if ((drives > 0) || (nomem == 0)) { if (drives == 0) @@ -1766,7 +1772,7 @@ static struct gendisk *floppy_find(int minor) int drive = minor & 3; if (unit[drive].type->code == FD_NODRIVE) return NULL; - return &unit[drive].disk; + return unit[drive].gendisk; } int __init amiga_floppy_init(void) @@ -1875,7 +1881,8 @@ void cleanup_module(void) for( i = 0; i < FD_MAX_UNITS; i++) { if (unit[i].type->code != FD_NODRIVE) { - del_gendisk(&unit[i].disk); + del_gendisk(unit[i].gendisk); + put_disk(unit[i].gendisk); kfree(unit[i].trackbuf); } } diff --git a/include/linux/amifd.h b/include/linux/amifd.h index eac36f77fa8f..346993268b45 100644 --- a/include/linux/amifd.h +++ b/include/linux/amifd.h @@ -55,7 +55,7 @@ struct amiga_floppy_struct { int busy; /* true when drive is active */ int dirty; /* true when trackbuf is not on disk */ int status; /* current error code for unit */ - struct gendisk disk; + struct gendisk *gendisk; }; #endif -- cgit v1.2.3 From 727a7d1e4d9074b3f7c2b70d1c3f6559dab18177 Mon Sep 17 00:00:00 2001 From: Brian Gerst Date: Sat, 5 Oct 2002 04:38:59 -0700 Subject: [PATCH] unistd.h cleanups This patch removes the stubs for syscalls that are not used from the kernel anymore. --- include/asm-alpha/unistd.h | 23 ----------------------- include/asm-arm/unistd.h | 29 ----------------------------- include/asm-cris/unistd.h | 14 -------------- include/asm-i386/unistd.h | 8 -------- include/asm-ia64/unistd.h | 8 -------- include/asm-m68k/unistd.h | 8 -------- include/asm-mips/unistd.h | 7 ------- include/asm-mips64/unistd.h | 7 ------- include/asm-parisc/unistd.h | 30 ------------------------------ include/asm-ppc/unistd.h | 4 ---- include/asm-ppc64/unistd.h | 8 -------- include/asm-s390/unistd.h | 9 --------- include/asm-s390x/unistd.h | 9 --------- include/asm-sh/unistd.h | 7 ------- include/asm-sparc/unistd.h | 8 -------- include/asm-sparc64/unistd.h | 8 -------- include/asm-um/unistd.h | 5 ----- include/asm-x86_64/unistd.h | 23 ----------------------- init/do_mounts.c | 2 +- 19 files changed, 1 insertion(+), 216 deletions(-) (limited to 'include') diff --git a/include/asm-alpha/unistd.h b/include/asm-alpha/unistd.h index 23eb6d2eb42d..363f1477ac16 100644 --- a/include/asm-alpha/unistd.h +++ b/include/asm-alpha/unistd.h @@ -533,12 +533,6 @@ type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5, type6 arg6)\ #include #include -extern void sys_idle(void); -static inline void idle(void) -{ - sys_idle(); -} - extern long sys_open(const char *, int, int); static inline long open(const char * name, int mode, int flags) { @@ -597,12 +591,6 @@ static inline long setsid(void) return sys_setsid(); } -extern long sys_sync(void); -static inline long sync(void) -{ - return sys_sync(); -} - struct rusage; extern asmlinkage long sys_wait4(pid_t, unsigned int *, int, struct rusage *); static inline pid_t waitpid(int pid, int * wait_stat, int flags) @@ -610,17 +598,6 @@ static inline pid_t waitpid(int pid, int * wait_stat, int flags) return sys_wait4(pid, wait_stat, flags, NULL); } -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} - -extern long sys_delete_module(const char *name); -static inline long delete_module(const char *name) -{ - return sys_delete_module(name); -} - #endif /* diff --git a/include/asm-arm/unistd.h b/include/asm-arm/unistd.h index 32d3f3bff93b..5ac18f2fcfd0 100644 --- a/include/asm-arm/unistd.h +++ b/include/asm-arm/unistd.h @@ -403,24 +403,6 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6 struct rusage; asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru); -static inline long idle(void) -{ - extern long sys_idle(void); - return sys_idle(); -} - -static inline long pause(void) -{ - extern long sys_pause(void); - return sys_pause(); -} - -static inline long sync(void) -{ - extern long sys_sync(void); - return sys_sync(); -} - static inline pid_t setsid(void) { extern long sys_setsid(void); @@ -474,17 +456,6 @@ static inline pid_t waitpid(pid_t pid, int *wait_stat, int options) return sys_wait4((int)pid, wait_stat, options, NULL); } -static inline long delete_module(const char *name) -{ - extern long sys_delete_module(const char *name); - return sys_delete_module(name); -} - -static inline pid_t wait(int * wait_stat) -{ - return sys_wait4(-1, wait_stat, 0, NULL); -} - /* * The following two can't be eliminated yet - they rely on * specific conditions. diff --git a/include/asm-cris/unistd.h b/include/asm-cris/unistd.h index f386c73562ea..7657bf9b1d92 100644 --- a/include/asm-cris/unistd.h +++ b/include/asm-cris/unistd.h @@ -365,12 +365,6 @@ type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5,type6 arg6) \ * some others too. */ #define __NR__exit __NR_exit -static inline _syscall0(int,idle) -static inline _syscall0(int,fork) -static inline _syscall2(int,clone,unsigned long,flags,char *,esp) -static inline _syscall0(int,pause) -static inline _syscall0(int,setup) -static inline _syscall0(int,sync) static inline _syscall0(pid_t,setsid) static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) static inline _syscall1(int,dup,int,fd) @@ -384,14 +378,6 @@ static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count) /* the following are just while developing the elinux port! */ static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) -static inline _syscall2(int,socketcall,int,call,unsigned long *,args) -static inline _syscall3(int,ioctl,unsigned int,fd,unsigned int,cmd,unsigned long,arg) -static inline _syscall5(int,mount,const char *,a,const char *,b,const char *,c,unsigned long,rwflag,const void *,data) - -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} #endif diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index 1a095484bd32..8765a0f82aff 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -362,8 +362,6 @@ __syscall_return(type,__res); \ * some others too. */ #define __NR__exit __NR_exit -static inline _syscall0(int,pause) -static inline _syscall0(int,sync) static inline _syscall0(pid_t,setsid) static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) @@ -374,12 +372,6 @@ static inline _syscall3(int,open,const char *,file,int,flag,int,mode) 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(int,delete_module,const char *,name) - -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} #endif diff --git a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h index 76e3509417d1..8061d1f9f0cb 100644 --- a/include/asm-ia64/unistd.h +++ b/include/asm-ia64/unistd.h @@ -309,7 +309,6 @@ name (type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ struct rusage; -static inline _syscall0(int,sync) static inline _syscall0(pid_t,setsid) static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) @@ -319,7 +318,6 @@ static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) static inline _syscall3(int,open,const char *,file,int,flag,int,mode) static inline _syscall1(int,close,int,fd) static inline _syscall4(pid_t,wait4,pid_t,pid,int *,wait_stat,int,options,struct rusage*, rusage) -static inline _syscall1(int,delete_module,const char *,name) static inline _syscall2(pid_t,clone,unsigned long,flags,void*,sp); #define __NR__exit __NR_exit @@ -331,12 +329,6 @@ waitpid (int pid, int *wait_stat, int flags) return wait4(pid, wait_stat, flags, NULL); } -static inline pid_t -wait (int * wait_stat) -{ - return wait4(-1, wait_stat, 0, 0); -} - #endif /* __KERNEL_SYSCALLS__ */ /* diff --git a/include/asm-m68k/unistd.h b/include/asm-m68k/unistd.h index bb231c4b4321..c99410180fe2 100644 --- a/include/asm-m68k/unistd.h +++ b/include/asm-m68k/unistd.h @@ -350,8 +350,6 @@ __syscall_return(type,__res); \ * some others too. */ #define __NR__exit __NR_exit -static inline _syscall0(int,pause) -static inline _syscall0(int,sync) static inline _syscall0(pid_t,setsid) static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) @@ -362,12 +360,6 @@ static inline _syscall3(int,open,const char *,file,int,flag,int,mode) 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(int,delete_module,const char *,name) - -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} #endif diff --git a/include/asm-mips/unistd.h b/include/asm-mips/unistd.h index bce8e78485aa..293b93991430 100644 --- a/include/asm-mips/unistd.h +++ b/include/asm-mips/unistd.h @@ -467,7 +467,6 @@ return -1; \ * some others too. */ #define __NR__exit __NR_exit -static inline _syscall0(int,sync) static inline _syscall0(pid_t,setsid) static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) @@ -478,12 +477,6 @@ static inline _syscall3(int,open,const char *,file,int,flag,int,mode) 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(int,delete_module,const char *,name) - -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} #endif /* !defined (__KERNEL_SYSCALLS__) */ #endif /* !defined (_LANGUAGE_ASSEMBLY) */ diff --git a/include/asm-mips64/unistd.h b/include/asm-mips64/unistd.h index 2bcacbf897e6..f8a7d5398807 100644 --- a/include/asm-mips64/unistd.h +++ b/include/asm-mips64/unistd.h @@ -793,7 +793,6 @@ return -1; \ * some others too. */ #define __NR__exit __NR_exit -static inline _syscall0(int,sync) static inline _syscall0(pid_t,setsid) static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) @@ -804,12 +803,6 @@ static inline _syscall3(int,open,const char *,file,int,flag,int,mode) 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(int,delete_module,const char *,name) - -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} #endif /* !defined (__KERNEL_SYSCALLS__) */ #endif /* !defined (_LANGUAGE_ASSEMBLY) */ diff --git a/include/asm-parisc/unistd.h b/include/asm-parisc/unistd.h index 45b38d1b0e18..8a89c82a6d9b 100644 --- a/include/asm-parisc/unistd.h +++ b/include/asm-parisc/unistd.h @@ -806,24 +806,6 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6 #ifdef __KERNEL_SYSCALLS__ -static inline int idle(void) -{ - extern int sys_idle(void); - return sys_idle(); -} - -static inline int pause(void) -{ - extern int sys_pause(void); - return sys_pause(); -} - -static inline int sync(void) -{ - extern int sys_sync(void); - return sys_sync(); -} - static inline pid_t setsid(void) { extern int sys_setsid(void); @@ -877,18 +859,6 @@ static inline pid_t waitpid(pid_t pid, int *wait_stat, int options) return sys_wait4((int)pid, wait_stat, options, NULL); } -static inline int delete_module(const char *name) -{ - extern int sys_delete_module(const char *name); - return sys_delete_module(name); -} - -static inline pid_t wait(int * wait_stat) -{ - extern int sys_wait4(int, int *, int, struct rusage *); - return sys_wait4(-1, wait_stat, 0, NULL); -} - static inline int execve(char *filename, char * argv [], char * envp[]) { diff --git a/include/asm-ppc/unistd.h b/include/asm-ppc/unistd.h index 9d8e3a4de7bd..a456cb464fec 100644 --- a/include/asm-ppc/unistd.h +++ b/include/asm-ppc/unistd.h @@ -436,10 +436,6 @@ extern int open(const char *file, int flag, int mode); extern int close(int fd); extern pid_t waitpid(pid_t pid, int *wait_stat, int options); -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1, wait_stat, 0); -} #endif /* __KERNEL_SYSCALLS__ */ /* diff --git a/include/asm-ppc64/unistd.h b/include/asm-ppc64/unistd.h index 8916fce9d39b..0323053d5a7d 100644 --- a/include/asm-ppc64/unistd.h +++ b/include/asm-ppc64/unistd.h @@ -431,8 +431,6 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ * System call prototypes. */ #define __NR__exit __NR_exit -static inline _syscall0(int,pause) -static inline _syscall0(int,sync) static inline _syscall0(pid_t,setsid) static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) @@ -443,12 +441,6 @@ static inline _syscall3(int,open,const char *,file,int,flag,int,mode) 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(int,delete_module,const char *,name) - -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} #endif /* __KERNEL_SYSCALLS__ */ diff --git a/include/asm-s390/unistd.h b/include/asm-s390/unistd.h index e295910bd72d..bfec140e0b89 100644 --- a/include/asm-s390/unistd.h +++ b/include/asm-s390/unistd.h @@ -378,9 +378,6 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ * some others too. */ #define __NR__exit __NR_exit -static inline _syscall0(int,idle) -static inline _syscall0(int,pause) -static inline _syscall0(int,sync) static inline _syscall0(pid_t,setsid) static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) @@ -390,7 +387,6 @@ static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) static inline _syscall3(int,open,const char *,file,int,flag,int,mode) static inline _syscall1(int,close,int,fd) static inline _syscall1(int,_exit,int,exitcode) -static inline _syscall1(int,delete_module,const char *,name) static inline _syscall2(long,stat,char *,filename,struct stat *,statbuf) struct rusage; @@ -400,11 +396,6 @@ static inline pid_t waitpid(int pid, int *wait_stat, int flags) return sys_wait4(pid, wait_stat, flags, NULL); } -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} - #endif /* diff --git a/include/asm-s390x/unistd.h b/include/asm-s390x/unistd.h index dc8edd478cd5..a3ee3ed707da 100644 --- a/include/asm-s390x/unistd.h +++ b/include/asm-s390x/unistd.h @@ -345,9 +345,6 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ * some others too. */ #define __NR__exit __NR_exit -static inline _syscall0(int,idle) -static inline _syscall0(int,pause) -static inline _syscall0(int,sync) static inline _syscall0(pid_t,setsid) static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) @@ -357,7 +354,6 @@ static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) static inline _syscall3(int,open,const char *,file,int,flag,int,mode) static inline _syscall1(int,close,int,fd) static inline _syscall1(int,_exit,int,exitcode) -static inline _syscall1(int,delete_module,const char *,name) static inline _syscall2(long,stat,char *,filename,struct stat *,statbuf) struct rusage; @@ -367,11 +363,6 @@ static inline pid_t waitpid(int pid, int *wait_stat, int flags) return sys_wait4(pid, wait_stat, flags, NULL); } -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} - #endif /* diff --git a/include/asm-sh/unistd.h b/include/asm-sh/unistd.h index afebc07eaf02..f5b8fdb49b9f 100644 --- a/include/asm-sh/unistd.h +++ b/include/asm-sh/unistd.h @@ -347,8 +347,6 @@ __syscall_return(type,__sc0); \ * some others too. */ #define __NR__exit __NR_exit -static __inline__ _syscall0(int,pause) -static __inline__ _syscall0(int,sync) static __inline__ _syscall0(pid_t,setsid) static __inline__ _syscall3(int,write,int,fd,const char *,buf,off_t,count) static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count) @@ -359,12 +357,7 @@ static __inline__ _syscall3(int,open,const char *,file,int,flag,int,mode) 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(int,delete_module,const char *,name) -static __inline__ pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} #endif /* diff --git a/include/asm-sparc/unistd.h b/include/asm-sparc/unistd.h index f928fde5caee..b4a785dbd9cf 100644 --- a/include/asm-sparc/unistd.h +++ b/include/asm-sparc/unistd.h @@ -416,8 +416,6 @@ return -1; \ * some others too. */ #define __NR__exit __NR_exit -static __inline__ _syscall0(int,pause) -static __inline__ _syscall0(int,sync) static __inline__ _syscall0(pid_t,setsid) static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count) static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count) @@ -428,12 +426,6 @@ static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode) 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(int,delete_module,const char *,name) - -static __inline__ pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} #endif /* __KERNEL_SYSCALLS__ */ diff --git a/include/asm-sparc64/unistd.h b/include/asm-sparc64/unistd.h index 2959d9071240..588f2a577fa7 100644 --- a/include/asm-sparc64/unistd.h +++ b/include/asm-sparc64/unistd.h @@ -406,8 +406,6 @@ return -1; \ * some others too. */ #define __NR__exit __NR_exit -static __inline__ _syscall0(int,pause) -static __inline__ _syscall0(int,sync) static __inline__ _syscall0(pid_t,setsid) static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count) static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count) @@ -418,12 +416,6 @@ static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode) 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(int,delete_module,const char *,name) - -static __inline__ pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} #endif /* __KERNEL_SYSCALLS__ */ diff --git a/include/asm-um/unistd.h b/include/asm-um/unistd.h index 6c5e913cbf9c..2ceb9c226c32 100644 --- a/include/asm-um/unistd.h +++ b/include/asm-um/unistd.h @@ -61,11 +61,6 @@ static inline long waitpid(pid_t pid, unsigned int *status, int options) KERNEL_CALL(pid_t, sys_wait4, pid, status, options, NULL) } -static inline pid_t wait(int *status) -{ - KERNEL_CALL(pid_t, sys_wait4, -1, status, 0, NULL) -} - static inline pid_t setsid(void) { KERNEL_CALL(pid_t, sys_setsid) diff --git a/include/asm-x86_64/unistd.h b/include/asm-x86_64/unistd.h index 5b1c2bbf4f91..88450cf78c8a 100644 --- a/include/asm-x86_64/unistd.h +++ b/include/asm-x86_64/unistd.h @@ -573,18 +573,6 @@ __syscall_return(type,__res); \ */ #define __NR__exit __NR_exit -extern long sys_pause(void); -static inline long pause(void) -{ - return sys_pause(); -} - -extern long sys_sync(void); -static inline long sync(void) -{ - return sys_sync(); -} - extern pid_t sys_setsid(void); static inline pid_t setsid(void) { @@ -636,12 +624,6 @@ extern inline long exit(int error_code) sys_exit(error_code); } -extern long sys_delete_module(const char *); -static inline long delete_module(const char *name_user) -{ - return sys_delete_module(name_user); -} - struct rusage; asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru); @@ -650,11 +632,6 @@ static inline pid_t waitpid(int pid, int * wait_stat, int flags) return sys_wait4(pid, wait_stat, flags, NULL); } -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} - #endif /* __KERNEL_SYSCALLS__ */ #endif /* __NO_STUBS */ diff --git a/init/do_mounts.c b/init/do_mounts.c index 27719ab80256..1e4fe1ed4439 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -710,7 +710,7 @@ static void __init handle_initrd(void) pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD); if (pid > 0) { - while (pid != wait(&i)) + while (pid != waitpid(-1, &i, 0)) yield(); } -- cgit v1.2.3 From 1af169808f7b745c64a429fbcdccc5e8fc82fb31 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sun, 6 Oct 2002 01:57:53 -0700 Subject: [PATCH] 6x4 font headers Oops forgot this in the first patch set --- include/video/font.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/video/font.h b/include/video/font.h index 01d3c87d4309..007d97f9b3ef 100644 --- a/include/video/font.h +++ b/include/video/font.h @@ -28,6 +28,7 @@ struct fbcon_font_desc { #define SUN8x16_IDX 4 #define SUN12x22_IDX 5 #define ACORN8x8_IDX 6 +#define MINI4x6_IDX 7 extern struct fbcon_font_desc font_vga_8x8, font_vga_8x16, @@ -35,7 +36,8 @@ extern struct fbcon_font_desc font_vga_8x8, font_vga_6x11, font_sun_8x16, font_sun_12x22, - font_acorn_8x8; + font_acorn_8x8, + font_mini_4x6; /* Find a font with a specific name */ -- cgit v1.2.3