From b7b0f33ff804ab7ee14ee6f2a4eac2023aff75ce Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Thu, 22 Jan 2004 22:58:38 -0800 Subject: [SCTP] Add sysctl parameters to update socket send/receive buffers. --- include/linux/sysctl.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index f2f4a8a613c4..0ea4f81689e9 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -578,6 +578,8 @@ enum { NET_SCTP_PRESERVE_ENABLE = 11, NET_SCTP_MAX_BURST = 12, NET_SCTP_ADDIP_ENABLE = 13, + NET_SCTP_RMEM = 14, + NET_SCTP_WMEM = 15, }; /* /proc/sys/net/bridge */ -- cgit v1.2.3 From a2e1b35b1ccdd67e9f1b7a0c83d39968059286ca Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Tue, 27 Jan 2004 13:54:34 -0500 Subject: [ACPI] Check for overflow when parsing MADT entries from Jes Sorensen --- arch/i386/kernel/acpi/boot.c | 19 ++++++++++--------- arch/ia64/kernel/acpi.c | 22 +++++++--------------- arch/ia64/kernel/iosapic.c | 2 +- arch/x86_64/kernel/acpi/boot.c | 20 +++++++++++--------- drivers/acpi/numa.c | 13 ++++++++----- drivers/acpi/tables.c | 28 +++++++++++++++++----------- include/asm-ia64/iosapic.h | 3 +++ include/linux/acpi.h | 4 ++-- 8 files changed, 59 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/arch/i386/kernel/acpi/boot.c b/arch/i386/kernel/acpi/boot.c index 8aac4a6b9606..d14ddab92d87 100644 --- a/arch/i386/kernel/acpi/boot.c +++ b/arch/i386/kernel/acpi/boot.c @@ -43,8 +43,8 @@ int acpi_noirq __initdata = 0; /* skip ACPI IRQ initialization */ int acpi_ht __initdata = 1; /* enable HT */ -int acpi_lapic = 0; -int acpi_ioapic = 0; +int acpi_lapic; +int acpi_ioapic; /* -------------------------------------------------------------------------- Boot-time Configuration @@ -456,7 +456,7 @@ acpi_boot_init (void) * and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value). */ - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr, 0); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n"); return result; @@ -464,7 +464,8 @@ acpi_boot_init (void) mp_register_lapic_address(acpi_lapic_addr); - result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic, + MAX_APICS); if (!result) { printk(KERN_ERR PREFIX "No LAPIC entries present\n"); /* TBD: Cleanup to allow fallback to MPS */ @@ -476,7 +477,7 @@ acpi_boot_init (void) return result; } - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi, 0); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n"); /* TBD: Cleanup to allow fallback to MPS */ @@ -513,8 +514,8 @@ acpi_boot_init (void) return 1; } - result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic); - if (!result) { + result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic, MAX_IO_APICS); + if (!result) { printk(KERN_ERR PREFIX "No IOAPIC entries present\n"); return -ENODEV; } @@ -526,14 +527,14 @@ acpi_boot_init (void) /* Build a default routing table for legacy (ISA) interrupts. */ mp_config_acpi_legacy_irqs(); - result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr); + result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr, NR_IRQ_VECTORS); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n"); /* TBD: Cleanup to allow fallback to MPS */ return result; } - result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src); + result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src, NR_IRQ_VECTORS); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); /* TBD: Cleanup to allow fallback to MPS */ diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index d757134a2175..b3ceb1a018eb 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -189,8 +189,6 @@ acpi_parse_lsapic (acpi_table_entry_header *header) if (!lsapic->flags.enabled) printk(" disabled"); - else if (available_cpus >= NR_CPUS) - printk(" ignored (increase NR_CPUS)"); else { printk(" enabled"); #ifdef CONFIG_SMP @@ -393,12 +391,6 @@ acpi_numa_memory_affinity_init (struct acpi_table_memory_affinity *ma) size = ma->length_hi; size = (size << 32) | ma->length_lo; - if (num_memblks >= NR_MEMBLKS) { - printk(KERN_ERR "Too many mem chunks in SRAT. Ignoring %ld MBytes at %lx\n", - size/(1024*1024), paddr); - return; - } - /* Ignore disabled entries */ if (!ma->flags.enabled) return; @@ -550,29 +542,29 @@ acpi_boot_init (void) /* Local APIC */ - if (acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr) < 0) + if (acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr, 0) < 0) printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n"); - if (acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic) < 1) + if (acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic, NR_CPUS) < 1) printk(KERN_ERR PREFIX "Error parsing MADT - no LAPIC entries\n"); - if (acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi) < 0) + if (acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi, 0) < 0) printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n"); /* I/O APIC */ - if (acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic) < 1) + if (acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic, NR_IOSAPICS) < 1) printk(KERN_ERR PREFIX "Error parsing MADT - no IOSAPIC entries\n"); /* System-Level Interrupt Routing */ - if (acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src) < 0) + if (acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src, ACPI_MAX_PLATFORM_INTERRUPTS) < 0) printk(KERN_ERR PREFIX "Error parsing platform interrupt source entry\n"); - if (acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr) < 0) + if (acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr, 0) < 0) printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n"); - if (acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src) < 0) + if (acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src, 0) < 0) printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); skip_madt: diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c index 9e30b08ba40e..879f566dd1b1 100644 --- a/arch/ia64/kernel/iosapic.c +++ b/arch/ia64/kernel/iosapic.c @@ -114,7 +114,7 @@ static struct iosapic { char *addr; /* base address of IOSAPIC */ unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */ unsigned short num_rte; /* number of RTE in this IOSAPIC */ -} iosapic_lists[256]; +} iosapic_lists[NR_IOSAPICS]; static int num_iosapic; diff --git a/arch/x86_64/kernel/acpi/boot.c b/arch/x86_64/kernel/acpi/boot.c index 33a40edea70b..d46c2d41b759 100644 --- a/arch/x86_64/kernel/acpi/boot.c +++ b/arch/x86_64/kernel/acpi/boot.c @@ -51,8 +51,8 @@ int acpi_noirq __initdata = 0; /* skip ACPI IRQ initialization */ int acpi_ht __initdata = 1; /* enable HT */ -int acpi_lapic = 0; -int acpi_ioapic = 0; +int acpi_lapic; +int acpi_ioapic; /* -------------------------------------------------------------------------- Boot-time Configuration @@ -439,7 +439,7 @@ acpi_boot_init (void) * and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value). */ - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr, 0); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n"); return result; @@ -447,7 +447,8 @@ acpi_boot_init (void) mp_register_lapic_address(acpi_lapic_addr); - result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC, acpi_parse_lapic, + MAX_APICS); if (!result) { printk(KERN_ERR PREFIX "No LAPIC entries present\n"); /* TBD: Cleanup to allow fallback to MPS */ @@ -459,7 +460,7 @@ acpi_boot_init (void) return result; } - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi); + result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi, 0); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n"); /* TBD: Cleanup to allow fallback to MPS */ @@ -496,8 +497,8 @@ acpi_boot_init (void) return 1; } - result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic); - if (!result) { + result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic, MAX_IO_APICS); + if (!result) { printk(KERN_ERR PREFIX "No IOAPIC entries present\n"); return -ENODEV; } @@ -509,14 +510,15 @@ acpi_boot_init (void) /* Build a default routing table for legacy (ISA) interrupts. */ mp_config_acpi_legacy_irqs(); - result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr); + result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr, NR_IRQ_VECTORS); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n"); /* TBD: Cleanup to allow fallback to MPS */ return result; } - result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src); + result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src, + NR_IRQ_VECTORS); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); /* TBD: Cleanup to allow fallback to MPS */ diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 62a484e4f6f8..ad63798aa625 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -31,7 +31,7 @@ #include #include -extern int __init acpi_table_parse_madt_family (enum acpi_table_id id, unsigned long madt_size, int entry_id, acpi_madt_entry_handler handler); +extern int __init acpi_table_parse_madt_family (enum acpi_table_id id, unsigned long madt_size, int entry_id, acpi_madt_entry_handler handler, unsigned int max_entries); void __init acpi_table_print_srat_entry ( @@ -149,10 +149,11 @@ acpi_parse_srat (unsigned long phys_addr, unsigned long size) int __init acpi_table_parse_srat ( enum acpi_srat_entry_id id, - acpi_madt_entry_handler handler) + acpi_madt_entry_handler handler, + unsigned int max_entries) { return acpi_table_parse_madt_family(ACPI_SRAT, sizeof(struct acpi_table_srat), - id, handler); + id, handler, max_entries); } @@ -166,9 +167,11 @@ acpi_numa_init() if (result > 0) { result = acpi_table_parse_srat(ACPI_SRAT_PROCESSOR_AFFINITY, - acpi_parse_processor_affinity); + acpi_parse_processor_affinity, + NR_CPUS); result = acpi_table_parse_srat(ACPI_SRAT_MEMORY_AFFINITY, - acpi_parse_memory_affinity); + acpi_parse_memory_affinity, + NR_MEMBLKS); } else { /* FIXME */ printk("Warning: acpi_table_parse(ACPI_SRAT) returned %d!\n",result); diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index e997203d754b..2583a0fcf2ce 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -302,13 +302,14 @@ acpi_table_parse_madt_family ( enum acpi_table_id id, unsigned long madt_size, int entry_id, - acpi_madt_entry_handler handler) + acpi_madt_entry_handler handler, + unsigned int max_entries) { void *madt = NULL; - acpi_table_entry_header *entry = NULL; - unsigned long count = 0; - unsigned long madt_end = 0; - unsigned int i = 0; + acpi_table_entry_header *entry; + unsigned int count = 0; + unsigned long madt_end; + unsigned int i; if (!handler) return -EINVAL; @@ -342,13 +343,18 @@ acpi_table_parse_madt_family ( ((unsigned long) madt + madt_size); while (((unsigned long) entry) < madt_end) { - if (entry->type == entry_id) { - count++; + if (entry->type == entry_id && + (!max_entries || count++ < max_entries)) handler(entry); - } + entry = (acpi_table_entry_header *) ((unsigned long) entry + entry->length); } + if (max_entries && count > max_entries) { + printk(KERN_WARNING PREFIX "[%s:0x%02x] ignored %i entries of " + "%i found\n", acpi_table_signatures[id], entry_id, + count - max_entries, count); + } return count; } @@ -357,10 +363,11 @@ acpi_table_parse_madt_family ( int __init acpi_table_parse_madt ( enum acpi_madt_entry_id id, - acpi_madt_entry_handler handler) + acpi_madt_entry_handler handler, + unsigned int max_entries) { return acpi_table_parse_madt_family(ACPI_APIC, sizeof(struct acpi_table_madt), - id, handler); + id, handler, max_entries); } @@ -585,4 +592,3 @@ acpi_table_init (void) return 0; } - diff --git a/include/asm-ia64/iosapic.h b/include/asm-ia64/iosapic.h index 68fa169e7a5b..ba8a35f418a2 100644 --- a/include/asm-ia64/iosapic.h +++ b/include/asm-ia64/iosapic.h @@ -52,6 +52,9 @@ #ifndef __ASSEMBLY__ #ifdef CONFIG_IOSAPIC + +#define NR_IOSAPICS 256 + extern void __init iosapic_system_init (int pcat_compat); extern void __init iosapic_init (unsigned long address, unsigned int gsi_base); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 8e4eba21e7ea..f851b34903e3 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -355,8 +355,8 @@ int acpi_numa_init (void); int acpi_table_init (void); int acpi_table_parse (enum acpi_table_id id, acpi_table_handler handler); int acpi_get_table_header_early (enum acpi_table_id id, struct acpi_table_header **header); -int acpi_table_parse_madt (enum acpi_madt_entry_id id, acpi_madt_entry_handler handler); -int acpi_table_parse_srat (enum acpi_srat_entry_id id, acpi_madt_entry_handler handler); +int acpi_table_parse_madt (enum acpi_madt_entry_id id, acpi_madt_entry_handler handler, unsigned int max_entries); +int acpi_table_parse_srat (enum acpi_srat_entry_id id, acpi_madt_entry_handler handler, unsigned int max_entries); void acpi_table_print (struct acpi_table_header *header, unsigned long phys_addr); void acpi_table_print_madt_entry (acpi_table_entry_header *madt); void acpi_table_print_srat_entry (acpi_table_entry_header *srat); -- cgit v1.2.3 From 93976ea58265ef4d5253e5484817db8d497e8876 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 29 Jan 2004 00:09:20 -0800 Subject: [PATCH] USB: remove unused usb-debug.c file (moving the one used function into the usb.c file.) --- drivers/usb/core/Makefile | 2 +- drivers/usb/core/usb-debug.c | 201 ----------------------------------------- drivers/usb/core/usb.c | 13 +++ drivers/usb/serial/kobil_sct.c | 4 - include/linux/usb.h | 10 -- 5 files changed, 14 insertions(+), 216 deletions(-) delete mode 100644 drivers/usb/core/usb-debug.c (limited to 'include/linux') diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index cbbf426352bd..e3907e097fd1 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -2,7 +2,7 @@ # Makefile for USB Core files and filesystem # -usbcore-objs := usb.o usb-debug.o hub.o hcd.o urb.o message.o \ +usbcore-objs := usb.o hub.o hcd.o urb.o message.o \ config.o file.o buffer.o driverfs.o ifeq ($(CONFIG_PCI),y) diff --git a/drivers/usb/core/usb-debug.c b/drivers/usb/core/usb-debug.c deleted file mode 100644 index b7683f917fa0..000000000000 --- a/drivers/usb/core/usb-debug.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * debug.c - USB debug helper routines. - * - * I just want these out of the way where they aren't in your - * face, but so that you can still use them.. - */ -#include -#include -#include -#include -#ifdef CONFIG_USB_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif -#include - -static void usb_show_endpoint(struct usb_host_endpoint *endpoint) -{ - usb_show_endpoint_descriptor(&endpoint->desc); -} - -static void usb_show_interface(struct usb_host_interface *altsetting) -{ - int i; - - usb_show_interface_descriptor(&altsetting->desc); - - for (i = 0; i < altsetting->desc.bNumEndpoints; i++) - usb_show_endpoint(altsetting->endpoint + i); -} - -static void usb_show_config(struct usb_host_config *config) -{ - int i, j; - struct usb_interface *ifp; - - usb_show_config_descriptor(&config->desc); - for (i = 0; i < config->desc.bNumInterfaces; i++) { - ifp = config->interface[i]; - - if (!ifp) - break; - - printk("\n Interface: %d\n", i); - for (j = 0; j < ifp->num_altsetting; j++) - usb_show_interface(ifp->altsetting + j); - } -} - -void usb_show_device(struct usb_device *dev) -{ - int i; - - usb_show_device_descriptor(&dev->descriptor); - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) - usb_show_config(dev->config + i); -} - -/* - * Parse and show the different USB descriptors. - */ -void usb_show_device_descriptor(struct usb_device_descriptor *desc) -{ - if (!desc) - { - printk("Invalid USB device descriptor (NULL POINTER)\n"); - return; - } - printk(" Length = %2d%s\n", desc->bLength, - desc->bLength == USB_DT_DEVICE_SIZE ? "" : " (!!!)"); - printk(" DescriptorType = %02x\n", desc->bDescriptorType); - - printk(" USB version = %x.%02x\n", - desc->bcdUSB >> 8, desc->bcdUSB & 0xff); - printk(" Vendor:Product = %04x:%04x\n", - desc->idVendor, desc->idProduct); - printk(" MaxPacketSize0 = %d\n", desc->bMaxPacketSize0); - printk(" NumConfigurations = %d\n", desc->bNumConfigurations); - printk(" Device version = %x.%02x\n", - desc->bcdDevice >> 8, desc->bcdDevice & 0xff); - - printk(" Device Class:SubClass:Protocol = %02x:%02x:%02x\n", - desc->bDeviceClass, desc->bDeviceSubClass, desc->bDeviceProtocol); - switch (desc->bDeviceClass) { - case 0: - printk(" Per-interface classes\n"); - break; - case USB_CLASS_AUDIO: - printk(" Audio device class\n"); - break; - case USB_CLASS_COMM: - printk(" Communications class\n"); - break; - case USB_CLASS_HID: - printk(" Human Interface Devices class\n"); - break; - case USB_CLASS_PRINTER: - printk(" Printer device class\n"); - break; - case USB_CLASS_MASS_STORAGE: - printk(" Mass Storage device class\n"); - break; - case USB_CLASS_HUB: - printk(" Hub device class\n"); - break; - case USB_CLASS_VENDOR_SPEC: - printk(" Vendor class\n"); - break; - default: - printk(" Unknown class\n"); - } -} - -void usb_show_config_descriptor(struct usb_config_descriptor *desc) -{ - printk("Configuration:\n"); - printk(" bLength = %4d%s\n", desc->bLength, - desc->bLength == USB_DT_CONFIG_SIZE ? "" : " (!!!)"); - printk(" bDescriptorType = %02x\n", desc->bDescriptorType); - printk(" wTotalLength = %04x\n", desc->wTotalLength); - printk(" bNumInterfaces = %02x\n", desc->bNumInterfaces); - printk(" bConfigurationValue = %02x\n", desc->bConfigurationValue); - printk(" iConfiguration = %02x\n", desc->iConfiguration); - printk(" bmAttributes = %02x\n", desc->bmAttributes); - printk(" bMaxPower = %4dmA\n", desc->bMaxPower * 2); -} - -void usb_show_interface_descriptor(struct usb_interface_descriptor *desc) -{ - printk(" Alternate Setting: %2d\n", desc->bAlternateSetting); - printk(" bLength = %4d%s\n", desc->bLength, - desc->bLength == USB_DT_INTERFACE_SIZE ? "" : " (!!!)"); - printk(" bDescriptorType = %02x\n", desc->bDescriptorType); - printk(" bInterfaceNumber = %02x\n", desc->bInterfaceNumber); - printk(" bAlternateSetting = %02x\n", desc->bAlternateSetting); - printk(" bNumEndpoints = %02x\n", desc->bNumEndpoints); - printk(" bInterface Class:SubClass:Protocol = %02x:%02x:%02x\n", - desc->bInterfaceClass, desc->bInterfaceSubClass, desc->bInterfaceProtocol); - printk(" iInterface = %02x\n", desc->iInterface); -} - -void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *desc) -{ - char *LengthCommentString = (desc->bLength == - USB_DT_ENDPOINT_AUDIO_SIZE) ? " (Audio)" : (desc->bLength == - USB_DT_ENDPOINT_SIZE) ? "" : " (!!!)"; - char *EndpointType[4] = { "Control", "Isochronous", "Bulk", "Interrupt" }; - - printk(" Endpoint:\n"); - printk(" bLength = %4d%s\n", - desc->bLength, LengthCommentString); - printk(" bDescriptorType = %02x\n", desc->bDescriptorType); - printk(" bEndpointAddress = %02x (%s)\n", desc->bEndpointAddress, - (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == - USB_ENDPOINT_XFER_CONTROL ? "i/o" : - (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? "in" : "out"); - printk(" bmAttributes = %02x (%s)\n", desc->bmAttributes, - EndpointType[USB_ENDPOINT_XFERTYPE_MASK & desc->bmAttributes]); - printk(" wMaxPacketSize = %04x\n", desc->wMaxPacketSize); - printk(" bInterval = %02x\n", desc->bInterval); - - /* Audio extensions to the endpoint descriptor */ - if (desc->bLength == USB_DT_ENDPOINT_AUDIO_SIZE) { - printk(" bRefresh = %02x\n", desc->bRefresh); - printk(" bSynchAddress = %02x\n", desc->bSynchAddress); - } -} - -void usb_show_string(struct usb_device *dev, char *id, int index) -{ - char *buf; - - if (!index) - return; - if (!(buf = kmalloc(256, GFP_KERNEL))) - return; - if (usb_string(dev, index, buf, 256) > 0) - dev_printk(KERN_INFO, &dev->dev, "%s: %s\n", id, buf); - kfree(buf); -} - -void usb_dump_urb (struct urb *urb) -{ - printk ("urb :%p\n", urb); - printk ("dev :%p\n", urb->dev); - printk ("pipe :%08X\n", urb->pipe); - printk ("status :%d\n", urb->status); - printk ("transfer_flags :%08X\n", urb->transfer_flags); - printk ("transfer_buffer :%p\n", urb->transfer_buffer); - printk ("transfer_buffer_length:%d\n", urb->transfer_buffer_length); - printk ("actual_length :%d\n", urb->actual_length); - printk ("setup_packet :%p\n", urb->setup_packet); - printk ("start_frame :%d\n", urb->start_frame); - printk ("number_of_packets :%d\n", urb->number_of_packets); - printk ("interval :%d\n", urb->interval); - printk ("error_count :%d\n", urb->error_count); - printk ("context :%p\n", urb->context); - printk ("complete :%p\n", urb->complete); -} - diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index e3b10d895a51..378d51ce2374 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -984,6 +984,19 @@ int usb_set_address(struct usb_device *dev) return retval; } +static inline void usb_show_string(struct usb_device *dev, char *id, int index) +{ + char *buf; + + if (!index) + return; + if (!(buf = kmalloc(256, GFP_KERNEL))) + return; + if (usb_string(dev, index, buf, 256) > 0) + dev_printk(KERN_INFO, &dev->dev, "%s: %s\n", id, buf); + kfree(buf); +} + /* * By the time we get here, we chose a new device address * and is in the default state. We need to identify the thing and diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 2bf6eb109524..1ed1d7aa64a7 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -409,8 +409,6 @@ static void kobil_read_int_callback( struct urb *purb, struct pt_regs *regs) // someone sets the dev to 0 if the close method has been called port->interrupt_in_urb->dev = port->serial->dev; - // usb_dump_urb(port->interrupt_in_urb); - result = usb_submit_urb( port->interrupt_in_urb, GFP_ATOMIC ); dbg("%s - port %d Send read URB returns: %i", __FUNCTION__, port->number, result); } @@ -496,8 +494,6 @@ static int kobil_write (struct usb_serial_port *port, int from_user, port->interrupt_in_urb->dev = port->serial->dev; // start reading - //usb_dump_urb(port->interrupt_in_urb); - result = usb_submit_urb( port->interrupt_in_urb, GFP_ATOMIC ); dbg("%s - port %d Send read URB returns: %i", __FUNCTION__, port->number, result); } diff --git a/include/linux/usb.h b/include/linux/usb.h index c405f81be765..c16b0125baa0 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1016,16 +1016,6 @@ static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int en /* -------------------------------------------------------------------------- */ -/* - * Debugging and troubleshooting/diagnostic helpers. - */ -void usb_show_device_descriptor(struct usb_device_descriptor *); -void usb_show_config_descriptor(struct usb_config_descriptor *); -void usb_show_interface_descriptor(struct usb_interface_descriptor *); -void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *); -void usb_show_device(struct usb_device *); -void usb_show_string(struct usb_device *dev, char *id, int index); - #ifdef DEBUG #define dbg(format, arg...) printk(KERN_DEBUG "%s: " format "\n" , __FILE__ , ## arg) #else -- cgit v1.2.3 From 2451c4f6cbf081a98ab5068cf42fa0559f3fff5b Mon Sep 17 00:00:00 2001 From: Adam Belay Date: Fri, 30 Jan 2004 00:34:48 -0800 Subject: [PATCH] PCI: Remove uneeded resource structures from pci_dev The following patch remove irq_resource and dma_resource from pci_dev. It appears that the serial pci driver depends on irq_resource, however, it may be broken portions of an old quirk. I attempted to maintain the existing behavior while removing irq_resource. I changed FL_IRQRESOURCE to FL_NOIRQ. Russell, could you provide any comments? irq_resource and dma_resource are most likely remnants from when pci_dev was shared with pnp. --- drivers/serial/8250_pci.c | 27 ++++++--------------------- include/linux/pci.h | 2 -- 2 files changed, 6 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index bb13d16c8a2f..16937439bd29 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -43,20 +43,12 @@ #define FL_BASE4 0x0004 #define FL_GET_BASE(x) (x & FL_BASE_MASK) -#define FL_IRQ_MASK (0x0007 << 4) -#define FL_IRQBASE0 (0x0000 << 4) -#define FL_IRQBASE1 (0x0001 << 4) -#define FL_IRQBASE2 (0x0002 << 4) -#define FL_IRQBASE3 (0x0003 << 4) -#define FL_IRQBASE4 (0x0004 << 4) -#define FL_GET_IRQBASE(x) ((x & FL_IRQ_MASK) >> 4) - /* Use successive BARs (PCI base address registers), else use offset into some specified BAR */ #define FL_BASE_BARS 0x0008 -/* Use the irq resource table instead of dev->irq */ -#define FL_IRQRESOURCE 0x0080 +/* do not assign an irq */ +#define FL_NOIRQ 0x0080 /* Use the Base address register size to cap number of ports */ #define FL_REGION_SZ_CAP 0x0100 @@ -850,17 +842,10 @@ static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) static _INLINE_ int get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) { - int base_idx; - - if ((board->flags & FL_IRQRESOURCE) == 0) - return dev->irq; - - base_idx = FL_GET_IRQBASE(board->flags); - - if (base_idx > DEVICE_COUNT_IRQ) + if (board->flags & FL_NOIRQ) return 0; - - return dev->irq_resource[base_idx].start; + else + return dev->irq; } /* @@ -1314,7 +1299,7 @@ static struct pci_board pci_boards[] __devinitdata = { .first_offset = 0x10000, }, [pbn_sgi_ioc3] = { - .flags = FL_BASE0|FL_IRQRESOURCE, + .flags = FL_BASE0|FL_NOIRQ, .num_ports = 1, .base_baud = 458333, .uart_offset = 8, diff --git a/include/linux/pci.h b/include/linux/pci.h index bc51ca9edef1..612d14e90587 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -416,8 +416,6 @@ struct pci_dev { */ unsigned int irq; struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */ - struct resource dma_resource[DEVICE_COUNT_DMA]; - struct resource irq_resource[DEVICE_COUNT_IRQ]; char * slot_name; /* pointer to dev.bus_id */ -- cgit v1.2.3 From c884ee809940c086a8a11a63f88ce7cd891cb881 Mon Sep 17 00:00:00 2001 From: Deepak Saxena Date: Sun, 1 Feb 2004 19:05:17 -0800 Subject: [PATCH] PCI: Replace pci_pool with generic dma_pool, part 2 include/linux changes: - Add dma_pools member to struct device - Add include/linux/dmapool.h - Remove pools memober from struct pci_dev - Replace pci_pool_* functions with macros that map to dma_pool_* functions --- include/linux/device.h | 1 + include/linux/dmapool.h | 27 +++++++++++++++++++++++++++ include/linux/pci.h | 14 ++++++++------ 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 include/linux/dmapool.h (limited to 'include/linux') diff --git a/include/linux/device.h b/include/linux/device.h index 367b21888ad4..4cf6e0d490d5 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -284,6 +284,7 @@ struct device { detached from its driver. */ u64 *dma_mask; /* dma mask (if dma'able device) */ + struct list_head dma_pools; /* dma pools (if dma'ble) */ void (*release)(struct device * dev); }; diff --git a/include/linux/dmapool.h b/include/linux/dmapool.h new file mode 100644 index 000000000000..e60bfdac348d --- /dev/null +++ b/include/linux/dmapool.h @@ -0,0 +1,27 @@ +/* + * include/linux/dmapool.h + * + * Allocation pools for DMAable (coherent) memory. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef LINUX_DMAPOOL_H +#define LINUX_DMAPOOL_H + +#include +#include + +struct dma_pool *dma_pool_create(const char *name, struct device *dev, + size_t size, size_t align, size_t allocation); + +void dma_pool_destroy(struct dma_pool *pool); + +void *dma_pool_alloc(struct dma_pool *pool, int mem_flags, dma_addr_t *handle); + +void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr); + +#endif + diff --git a/include/linux/pci.h b/include/linux/pci.h index 612d14e90587..bde9c5e8dbf8 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -393,7 +393,6 @@ struct pci_dev { 0xffffffff. You only need to change this if your device has broken DMA or supports 64-bit transfers. */ - struct list_head pools; /* pci_pools tied to this device */ u64 consistent_dma_mask;/* Like dma_mask, but for pci_alloc_consistent mappings as @@ -692,12 +691,15 @@ const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, co int pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max, int pass); /* kmem_cache style wrapper around pci_alloc_consistent() */ -struct pci_pool *pci_pool_create (const char *name, struct pci_dev *dev, - size_t size, size_t align, size_t allocation); -void pci_pool_destroy (struct pci_pool *pool); -void *pci_pool_alloc (struct pci_pool *pool, int flags, dma_addr_t *handle); -void pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t addr); +#include + +#define pci_pool dma_pool +#define pci_pool_create(name, pdev, size, align, allocation) \ + dma_pool_create(name, &pdev->dev, size, align, allocation) +#define pci_pool_destroy(pool) dma_pool_destroy(pool) +#define pci_pool_alloc(pool, flags, handle) dma_pool_alloc(pool, flags, handle) +#define pci_pool_free(pool, vaddr, addr) dma_pool_free(pool, vaddr, addr) #if defined(CONFIG_ISA) || defined(CONFIG_EISA) extern struct pci_dev *isa_bridge; -- cgit v1.2.3 From 0eb9b18eb108a9d169084ee154155041f413e945 Mon Sep 17 00:00:00 2001 From: Jon Smirl Date: Sun, 1 Feb 2004 23:39:32 -0800 Subject: [PATCH] Driver core: add hotplug support for class_simple This is needed by the DRI code. --- drivers/base/class_simple.c | 18 ++++++++++++++++++ include/linux/device.h | 2 ++ 2 files changed, 20 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/class_simple.c b/drivers/base/class_simple.c index bb29e34bd2e5..53548e0ed544 100644 --- a/drivers/base/class_simple.c +++ b/drivers/base/class_simple.c @@ -169,6 +169,24 @@ error: } EXPORT_SYMBOL(class_simple_device_add); +/** + * class_simple_set_hotplug - set the hotplug callback in the embedded struct class + * @cs: pointer to the struct class_simple to hold the pointer + * @hotplug: function pointer to the hotplug function + * + * Implement and set a hotplug function to add environment variables specific to this + * class on the hotplug event. + */ +int class_simple_set_hotplug(struct class_simple *cs, + int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size)) +{ + if ((cs == NULL) || (IS_ERR(cs))) + return -ENODEV; + cs->class.hotplug = hotplug; + return 0; +} +EXPORT_SYMBOL(class_simple_set_hotplug); + /** * class_simple_device_remove - removes a class device that was created with class_simple_device_add() * @dev: the dev_t of the device that was previously registered. diff --git a/include/linux/device.h b/include/linux/device.h index 367b21888ad4..c8f5aa7d9f5a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -253,6 +253,8 @@ extern struct class_simple *class_simple_create(struct module *owner, char *name extern void class_simple_destroy(struct class_simple *cs); extern struct class_device *class_simple_device_add(struct class_simple *cs, dev_t dev, struct device *device, const char *fmt, ...) __attribute__((format(printf,4,5))); +extern int class_simple_set_hotplug(struct class_simple *, + int (*hotplug)(struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size)); extern void class_simple_device_remove(dev_t dev); -- cgit v1.2.3 From 9d6cc8f23f0e0d00a29faabf059e12cdd2c1becb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 3 Feb 2004 21:48:53 -0800 Subject: [PATCH] Driver core: remove device_unregister_wait() as it's a very bad idea. --- drivers/base/core.c | 23 ----------------------- include/linux/device.h | 2 -- 2 files changed, 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index 84bc01e0ce59..6c84ff06026b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -76,7 +76,6 @@ static struct sysfs_ops dev_sysfs_ops = { static void device_release(struct kobject * kobj) { struct device * dev = to_dev(kobj); - struct completion * c = dev->complete; if (dev->release) dev->release(dev); @@ -86,8 +85,6 @@ static void device_release(struct kobject * kobj) dev->bus_id); WARN_ON(1); } - if (c) - complete(c); } static struct kobj_type ktype_device = { @@ -354,25 +351,6 @@ void device_unregister(struct device * dev) } -/** - * device_unregister_wait - Unregister device and wait for it to be freed. - * @dev: Device to unregister. - * - * For the cases where the caller needs to wait for all references to - * be dropped from the device before continuing (e.g. modules with - * statically allocated devices), this function uses a completion struct - * to wait, along with a matching complete() in device_release() above. - */ - -void device_unregister_wait(struct device * dev) -{ - struct completion c; - init_completion(&c); - dev->complete = &c; - device_unregister(dev); - wait_for_completion(&c); -} - /** * device_for_each_child - device child iterator. * @dev: parent struct device. @@ -421,7 +399,6 @@ EXPORT_SYMBOL(device_register); EXPORT_SYMBOL(device_del); EXPORT_SYMBOL(device_unregister); -EXPORT_SYMBOL(device_unregister_wait); EXPORT_SYMBOL(get_device); EXPORT_SYMBOL(put_device); EXPORT_SYMBOL(device_find); diff --git a/include/linux/device.h b/include/linux/device.h index c8f5aa7d9f5a..6da63dac06b0 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -265,7 +265,6 @@ struct device { struct list_head children; struct device * parent; - struct completion * complete; /* Notification for freeing device. */ struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ @@ -313,7 +312,6 @@ dev_set_drvdata (struct device *dev, void *data) */ extern int device_register(struct device * dev); extern void device_unregister(struct device * dev); -extern void device_unregister_wait(struct device * dev); extern void device_initialize(struct device * dev); extern int device_add(struct device * dev); extern void device_del(struct device * dev); -- cgit v1.2.3 From d4f63c8a506cf37ea05681f824cdd407ea8aeaf8 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 5 Feb 2004 22:07:24 -0800 Subject: [PATCH] Char drivers: cdev_unmap() To recap my argument: the current cdev implementation keeps an uncounted reference to every cdev in cdev_map. Creators of cdevs must know to call cdev_unmap() with the same arguments they passed to cdev_add() before releasing the device, or that reference will remain and will oops the kernel should user space attempt to open the (missing) device. It's an easy mistake to make, and, IMO, entirely unnecessary; the cdev code should be able to do its own bookkeeping. --- drivers/char/tty_io.c | 1 - drivers/ieee1394/amdtp.c | 1 - drivers/ieee1394/dv1394.c | 1 - drivers/ieee1394/raw1394.c | 1 - drivers/ieee1394/video1394.c | 1 - drivers/scsi/sg.c | 1 - drivers/scsi/st.c | 4 ---- fs/char_dev.c | 7 ++++--- include/linux/cdev.h | 4 ++-- 9 files changed, 6 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 963f6fb2b5dd..33a9c5cb4ecd 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2264,7 +2264,6 @@ int tty_unregister_driver(struct tty_driver *driver) if (driver->refcount) return -EBUSY; - cdev_unmap(MKDEV(driver->major, driver->minor_start), driver->num); unregister_chrdev_region(MKDEV(driver->major, driver->minor_start), driver->num); diff --git a/drivers/ieee1394/amdtp.c b/drivers/ieee1394/amdtp.c index 3fc004d47884..94630dcaac2d 100644 --- a/drivers/ieee1394/amdtp.c +++ b/drivers/ieee1394/amdtp.c @@ -1308,7 +1308,6 @@ static void __exit amdtp_exit_module (void) hpsb_unregister_highlevel(&amdtp_highlevel); devfs_remove("amdtp"); - cdev_unmap(IEEE1394_AMDTP_DEV, 16); cdev_del(&amdtp_cdev); HPSB_INFO("Unloaded AMDTP driver"); diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c index dd16cccc2eb1..881340aa52ac 100644 --- a/drivers/ieee1394/dv1394.c +++ b/drivers/ieee1394/dv1394.c @@ -2609,7 +2609,6 @@ static void __exit dv1394_exit_module(void) hpsb_unregister_protocol(&dv1394_driver); hpsb_unregister_highlevel(&dv1394_highlevel); - cdev_unmap(IEEE1394_DV1394_DEV, 16); cdev_del(&dv1394_cdev); devfs_remove("ieee1394/dv"); } diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 83e5a7c5149b..405c6d59bcd9 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -2682,7 +2682,6 @@ static int __init init_raw1394(void) static void __exit cleanup_raw1394(void) { hpsb_unregister_protocol(&raw1394_driver); - cdev_unmap(IEEE1394_RAW1394_DEV, 1); cdev_del(&raw1394_cdev); devfs_remove(RAW1394_DEVICE_NAME); hpsb_unregister_highlevel(&raw1394_highlevel); diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c index 6353ef25628d..d66d517d0187 100644 --- a/drivers/ieee1394/video1394.c +++ b/drivers/ieee1394/video1394.c @@ -1447,7 +1447,6 @@ static void __exit video1394_exit_module (void) hpsb_unregister_highlevel(&video1394_highlevel); devfs_remove(VIDEO1394_DRIVER_NAME); - cdev_unmap(IEEE1394_VIDEO1394_DEV, 16); cdev_del(&video1394_cdev); PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module"); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 6c210f52c5e0..9141152f33ce 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1521,7 +1521,6 @@ sg_remove(struct class_device *cl_dev) if (sdp) { sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic"); class_simple_device_remove(MKDEV(SCSI_GENERIC_MAJOR, k)); - cdev_unmap(MKDEV(SCSI_GENERIC_MAJOR, k), 1); cdev_del(sdp->cdev); sdp->cdev = NULL; devfs_remove("%s/generic", scsidp->devfs_name); diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index fc5b890e6f80..d566897fecbe 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -3946,8 +3946,6 @@ out_free_tape: if (cdev == STm->cdevs[j]) cdev = NULL; sysfs_remove_link(&STm->cdevs[j]->kobj, "device"); - cdev_unmap(MKDEV(SCSI_TAPE_MAJOR, - TAPE_MINOR(dev_num, mode, j)), 1); cdev_del(STm->cdevs[j]); } } @@ -3990,8 +3988,6 @@ static int st_remove(struct device *dev) for (j=0; j < 2; j++) { sysfs_remove_link(&tpnt->modes[mode].cdevs[j]->kobj, "device"); - cdev_unmap(MKDEV(SCSI_TAPE_MAJOR, - TAPE_MINOR(i, mode, j)), 1); cdev_del(tpnt->modes[mode].cdevs[j]); tpnt->modes[mode].cdevs[j] = NULL; } diff --git a/fs/char_dev.c b/fs/char_dev.c index 50ab4cf8e8e1..7a1d47c3157a 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -240,7 +240,6 @@ void unregister_chrdev_region(dev_t from, unsigned count) int unregister_chrdev(unsigned int major, const char *name) { struct char_device_struct *cd; - cdev_unmap(MKDEV(major, 0), 256); cd = __unregister_chrdev_region(major, 0, 256); if (cd && cd->cdev) cdev_del(cd->cdev); @@ -347,16 +346,19 @@ int cdev_add(struct cdev *p, dev_t dev, unsigned count) err = kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); if (err) kobject_del(&p->kobj); + p->dev = dev; + p->count = count; return err; } -void cdev_unmap(dev_t dev, unsigned count) +static void cdev_unmap(dev_t dev, unsigned count) { kobj_unmap(cdev_map, dev, count); } void cdev_del(struct cdev *p) { + cdev_unmap(p->dev, p->count); kobject_del(&p->kobj); kobject_put(&p->kobj); } @@ -458,6 +460,5 @@ EXPORT_SYMBOL(cdev_get); EXPORT_SYMBOL(cdev_put); EXPORT_SYMBOL(cdev_del); EXPORT_SYMBOL(cdev_add); -EXPORT_SYMBOL(cdev_unmap); EXPORT_SYMBOL(register_chrdev); EXPORT_SYMBOL(unregister_chrdev); diff --git a/include/linux/cdev.h b/include/linux/cdev.h index 191c800fa127..f1996ec09e96 100644 --- a/include/linux/cdev.h +++ b/include/linux/cdev.h @@ -7,6 +7,8 @@ struct cdev { struct module *owner; struct file_operations *ops; struct list_head list; + dev_t dev; + unsigned int count; }; void cdev_init(struct cdev *, struct file_operations *); @@ -21,8 +23,6 @@ int cdev_add(struct cdev *, dev_t, unsigned); void cdev_del(struct cdev *); -void cdev_unmap(dev_t, unsigned); - void cd_forget(struct inode *); #endif -- cgit v1.2.3 From 2724a14b455e66ddac226b2955e15eb2ddef1721 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Sat, 7 Feb 2004 04:48:53 -0800 Subject: [PATCH] fix duplication of DMA {black,white}list in icside.c Always compile ide-dma.c if CONFIG_BLK_DEV_IDEDMA=y, mark PCI specific code with CONFIG_BLK_DEV_IDEDMA_PCI for now (it should migrate to ide_pcidma.c over a time). This fixes a small bug - in_drive_list() from icside.c used !strstr() instead of strstr() so it was missing two entries from a blacklist. --- drivers/ide/Makefile | 2 +- drivers/ide/arm/icside.c | 73 ++---------------------------------------------- drivers/ide/ide-dma.c | 19 ++++++++----- include/linux/ide.h | 8 ++++-- 4 files changed, 21 insertions(+), 81 deletions(-) (limited to 'include/linux') diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile index 488c784ce670..7cc48f1f4c6a 100644 --- a/drivers/ide/Makefile +++ b/drivers/ide/Makefile @@ -20,7 +20,7 @@ ide-core-$(CONFIG_BLK_DEV_CMD640) += pci/cmd640.o # Core IDE code - must come before legacy ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o -ide-core-$(CONFIG_BLK_DEV_IDEDMA_PCI) += ide-dma.o +ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o ide-core-$(CONFIG_BLK_DEV_IDE_TCQ) += ide-tcq.o ide-core-$(CONFIG_PROC_FS) += ide-proc.o ide-core-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 8f582564fbb2..09d3f9414e8f 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -330,72 +330,6 @@ static int icside_set_speed(ide_drive_t *drive, u8 xfer_mode) return on; } -/* - * The following is a sick duplication from ide-dma.c ;( - * - * This should be defined in one place only. - */ -struct drive_list_entry { - const char * id_model; - const char * id_firmware; -}; - -static const struct drive_list_entry drive_whitelist [] = { - { "Micropolis 2112A", "ALL" }, - { "CONNER CTMA 4000", "ALL" }, - { "CONNER CTT8000-A", "ALL" }, - { "ST34342A", "ALL" }, - { NULL, NULL } -}; - -static struct drive_list_entry drive_blacklist [] = { - { "WDC AC11000H", "ALL" }, - { "WDC AC22100H", "ALL" }, - { "WDC AC32500H", "ALL" }, - { "WDC AC33100H", "ALL" }, - { "WDC AC31600H", "ALL" }, - { "WDC AC32100H", "24.09P07" }, - { "WDC AC23200L", "21.10N21" }, - { "Compaq CRD-8241B", "ALL" }, - { "CRD-8400B", "ALL" }, - { "CRD-8480B", "ALL" }, - { "CRD-8480C", "ALL" }, - { "CRD-8482B", "ALL" }, - { "CRD-84", "ALL" }, - { "SanDisk SDP3B", "ALL" }, - { "SanDisk SDP3B-64", "ALL" }, - { "SANYO CD-ROM CRD", "ALL" }, - { "HITACHI CDR-8", "ALL" }, - { "HITACHI CDR-8335", "ALL" }, - { "HITACHI CDR-8435", "ALL" }, - { "Toshiba CD-ROM XM-6202B", "ALL" }, - { "CD-532E-A", "ALL" }, - { "E-IDE CD-ROM CR-840", "ALL" }, - { "CD-ROM Drive/F5A", "ALL" }, - { "RICOH CD-R/RW MP7083A", "ALL" }, - { "WPI CDD-820", "ALL" }, - { "SAMSUNG CD-ROM SC-148C", "ALL" }, - { "SAMSUNG CD-ROM SC-148F", "ALL" }, - { "SAMSUNG CD-ROM SC", "ALL" }, - { "SanDisk SDP3B-64", "ALL" }, - { "SAMSUNG CD-ROM SN-124", "ALL" }, - { "PLEXTOR CD-R PX-W8432T", "ALL" }, - { "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" }, - { "_NEC DV5800A", "ALL" }, - { NULL, NULL } -}; - -static int -in_drive_list(struct hd_driveid *id, const struct drive_list_entry *drive_table) -{ - for ( ; drive_table->id_model ; drive_table++) - if ((!strcmp(drive_table->id_model, id->model)) && - ((!strstr(drive_table->id_firmware, id->fw_rev)) || - (!strcmp(drive_table->id_firmware, "ALL")))) - return 1; - return 0; -} - static int icside_dma_host_off(ide_drive_t *drive) { return 0; @@ -437,11 +371,8 @@ static int icside_dma_check(ide_drive_t *drive) /* * Consult the list of known "bad" drives */ - if (in_drive_list(id, drive_blacklist)) { - printk("%s: Disabling DMA for %s (blacklisted)\n", - drive->name, id->model); + if (__ide_dma_bad_drive(drive)) goto out; - } /* * Enable DMA on any drive that has multiword DMA @@ -454,7 +385,7 @@ static int icside_dma_check(ide_drive_t *drive) /* * Consult the list of known "good" drives */ - if (in_drive_list(id, drive_whitelist)) { + if (__ide_dma_good_drive(drive)) { if (id->eide_dma_time > 150) goto out; xfer_mode = XFER_MW_DMA_1; diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index acf29b5c430a..205c28225ad9 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -90,11 +90,11 @@ #include struct drive_list_entry { - char * id_model; - char * id_firmware; + const char *id_model; + const char *id_firmware; }; -struct drive_list_entry drive_whitelist [] = { +static const struct drive_list_entry drive_whitelist [] = { { "Micropolis 2112A" , "ALL" }, { "CONNER CTMA 4000" , "ALL" }, @@ -103,7 +103,7 @@ struct drive_list_entry drive_whitelist [] = { { 0 , 0 } }; -struct drive_list_entry drive_blacklist [] = { +static const struct drive_list_entry drive_blacklist [] = { { "WDC AC11000H" , "ALL" }, { "WDC AC22100H" , "ALL" }, @@ -151,7 +151,7 @@ struct drive_list_entry drive_blacklist [] = { * Returns 1 if the drive is found in the table. */ -static int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table) +static int in_drive_list(struct hd_driveid *id, const struct drive_list_entry *drive_table) { for ( ; drive_table->id_model ; drive_table++) if ((!strcmp(drive_table->id_model, id->model)) && @@ -161,6 +161,7 @@ static int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_ return 0; } +#ifdef CONFIG_BLK_DEV_IDEDMA_PCI /** * ide_dma_intr - IDE DMA interrupt handler * @drive: the drive the interrupt is for @@ -764,6 +765,7 @@ int __ide_dma_test_irq (ide_drive_t *drive) } EXPORT_SYMBOL(__ide_dma_test_irq); +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ int __ide_dma_bad_drive (ide_drive_t *drive) { @@ -771,8 +773,9 @@ int __ide_dma_bad_drive (ide_drive_t *drive) int blacklist = in_drive_list(id, drive_blacklist); if (blacklist) { - printk(KERN_WARNING "%s: Disabling (U)DMA for %s\n", drive->name, id->model); - return(blacklist); + printk(KERN_WARNING "%s: Disabling (U)DMA for %s (blacklisted)\n", + drive->name, id->model); + return blacklist; } return 0; } @@ -787,6 +790,7 @@ int __ide_dma_good_drive (ide_drive_t *drive) EXPORT_SYMBOL(__ide_dma_good_drive); +#ifdef CONFIG_BLK_DEV_IDEDMA_PCI /* * Used for HOST FIFO counters for VDMA * PIO over DMA, effective ATA-Bridge operator. @@ -1104,3 +1108,4 @@ void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_p } EXPORT_SYMBOL_GPL(ide_setup_dma); +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ diff --git a/include/linux/ide.h b/include/linux/ide.h index f5ee5b62adee..d247474de882 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1597,6 +1597,10 @@ extern void ide_setup_pci_devices(struct pci_dev *, struct pci_dev *, ide_pci_de #define BAD_DMA_DRIVE 0 #define GOOD_DMA_DRIVE 1 +#ifdef CONFIG_BLK_DEV_IDEDMA +int __ide_dma_bad_drive(ide_drive_t *); +int __ide_dma_good_drive(ide_drive_t *); + #ifdef CONFIG_BLK_DEV_IDEDMA_PCI extern int ide_build_sglist(ide_drive_t *, struct request *); extern int ide_raw_build_sglist(ide_drive_t *, struct request *); @@ -1618,8 +1622,6 @@ extern int __ide_dma_write(ide_drive_t *); extern int __ide_dma_begin(ide_drive_t *); extern int __ide_dma_end(ide_drive_t *); extern int __ide_dma_test_irq(ide_drive_t *); -extern int __ide_dma_bad_drive(ide_drive_t *); -extern int __ide_dma_good_drive(ide_drive_t *); extern int __ide_dma_count(ide_drive_t *); extern int __ide_dma_verbose(ide_drive_t *); extern int __ide_dma_lostirq(ide_drive_t *); @@ -1637,6 +1639,8 @@ extern ide_startstop_t __ide_dma_queued_start(ide_drive_t *drive); static inline void ide_release_dma(ide_hwif_t *drive) {;} #endif +#endif /* CONFIG_BLK_DEV_IDEDMA */ + extern int ide_hwif_request_regions(ide_hwif_t *hwif); extern void ide_hwif_release_regions(ide_hwif_t* hwif); extern void ide_unregister (unsigned int index); -- cgit v1.2.3 From 8758819af6d985c6a7f2b7c7297b4c90c77561ab Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:23:52 +0100 Subject: NFSv4/RPCSEC_GSS: Ensure that RPC userland upcalls time out correctly if the corresponding userland daemon is not up and running. --- fs/nfs/idmap.c | 1 + include/linux/sunrpc/rpc_pipe_fs.h | 1 + net/sunrpc/auth_gss/auth_gss.c | 1 + net/sunrpc/clnt.c | 1 + net/sunrpc/rpc_pipe.c | 29 +++++++++++++++++++++++++++-- net/sunrpc/sunrpc_syms.c | 1 + 6 files changed, 32 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 537380371957..f984e0156fa2 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -43,6 +43,7 @@ #include #include +#include #include #include diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index eabb2ebf2289..c3752710c74c 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -27,6 +27,7 @@ struct rpc_inode { #define RPC_PIPE_WAIT_FOR_OPEN 1 int flags; struct rpc_pipe_ops *ops; + struct work_struct queue_timeout; }; static inline struct rpc_inode * diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 79dbcca25f1e..ee2438bf57a8 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index fdb5554eb246..badaa121f29c 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -30,6 +30,7 @@ #include #include +#include #include #include diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index d6cf592d6c6c..1841ac8cb0da 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -25,6 +25,7 @@ #include #include +#include #include static struct vfsmount *rpc_mount; @@ -35,6 +36,8 @@ static struct file_system_type rpc_pipe_fs_type; static kmem_cache_t *rpc_inode_cachep; +#define RPC_UPCALL_TIMEOUT (30*HZ) + static void __rpc_purge_upcall(struct inode *inode, int err) { @@ -59,6 +62,18 @@ rpc_purge_upcall(struct inode *inode, int err) up(&inode->i_sem); } +static void +rpc_timeout_upcall_queue(void *data) +{ + struct rpc_inode *rpci = (struct rpc_inode *)data; + struct inode *inode = &rpci->vfs_inode; + + down(&inode->i_sem); + if (rpci->nreaders == 0 && !list_empty(&rpci->pipe)) + __rpc_purge_upcall(inode, -ETIMEDOUT); + up(&inode->i_sem); +} + int rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) { @@ -66,7 +81,13 @@ rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) int res = 0; down(&inode->i_sem); - if (rpci->nreaders || (rpci->flags & RPC_PIPE_WAIT_FOR_OPEN)) { + if (rpci->nreaders) { + list_add_tail(&msg->list, &rpci->pipe); + rpci->pipelen += msg->len; + } else if (rpci->flags & RPC_PIPE_WAIT_FOR_OPEN) { + if (list_empty(&rpci->pipe)) + schedule_delayed_work(&rpci->queue_timeout, + RPC_UPCALL_TIMEOUT); list_add_tail(&msg->list, &rpci->pipe); rpci->pipelen += msg->len; } else @@ -80,6 +101,9 @@ void rpc_inode_setowner(struct inode *inode, void *private) { struct rpc_inode *rpci = RPC_I(inode); + + cancel_delayed_work(&rpci->queue_timeout); + flush_scheduled_work(); down(&inode->i_sem); rpci->private = private; if (!private) @@ -133,7 +157,7 @@ rpc_pipe_release(struct inode *inode, struct file *filp) down(&inode->i_sem); if (filp->f_mode & FMODE_READ) rpci->nreaders --; - if (!rpci->nreaders && !(rpci->flags & RPC_PIPE_WAIT_FOR_OPEN)) + if (!rpci->nreaders) __rpc_purge_upcall(inode, -EPIPE); up(&inode->i_sem); return 0; @@ -769,6 +793,7 @@ init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) INIT_LIST_HEAD(&rpci->pipe); rpci->pipelen = 0; init_waitqueue_head(&rpci->waitq); + INIT_WORK(&rpci->queue_timeout, rpc_timeout_upcall_queue, rpci); rpci->ops = NULL; } } diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index bb05bd79a133..ff8d2bb7bb18 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -21,6 +21,7 @@ #include #include #include +#include #include -- cgit v1.2.3 From 62d3ffc42969a84db8d1e045c79a5dbb7ad5f7ce Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:25:51 +0100 Subject: RPCSEC_GSS: Make the upcalls detect if the userland daemon dies while processing a request. --- include/linux/sunrpc/rpc_pipe_fs.h | 4 ++- net/sunrpc/auth_gss/auth_gss.c | 28 ++++++++++++++++ net/sunrpc/rpc_pipe.c | 67 ++++++++++++++++++++++++++------------ 3 files changed, 78 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h index c3752710c74c..63929349571f 100644 --- a/include/linux/sunrpc/rpc_pipe_fs.h +++ b/include/linux/sunrpc/rpc_pipe_fs.h @@ -14,6 +14,7 @@ struct rpc_pipe_msg { struct rpc_pipe_ops { ssize_t (*upcall)(struct file *, struct rpc_pipe_msg *, char __user *, size_t); ssize_t (*downcall)(struct file *, const char __user *, size_t); + void (*release_pipe)(struct inode *); void (*destroy_msg)(struct rpc_pipe_msg *); }; @@ -21,8 +22,10 @@ struct rpc_inode { struct inode vfs_inode; void *private; struct list_head pipe; + struct list_head in_upcall; int pipelen; int nreaders; + int nwriters; wait_queue_head_t waitq; #define RPC_PIPE_WAIT_FOR_OPEN 1 int flags; @@ -36,7 +39,6 @@ RPC_I(struct inode *inode) return container_of(inode, struct rpc_inode, vfs_inode); } -extern void rpc_inode_setowner(struct inode *, void *); extern int rpc_queue_upcall(struct inode *, struct rpc_pipe_msg *); extern struct dentry *rpc_mkdir(char *, struct rpc_clnt *); diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 6892bb79d8da..af67459e1cc4 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -482,6 +482,33 @@ err: return err; } +static void +gss_pipe_release(struct inode *inode) +{ + struct rpc_inode *rpci = RPC_I(inode); + struct rpc_clnt *clnt; + struct rpc_auth *auth; + struct gss_auth *gss_auth; + + clnt = rpci->private; + auth = clnt->cl_auth; + gss_auth = container_of(auth, struct gss_auth, rpc_auth); + spin_lock(&gss_auth->lock); + while (!list_empty(&gss_auth->upcalls)) { + struct gss_upcall_msg *gss_msg; + + gss_msg = list_entry(gss_auth->upcalls.next, + struct gss_upcall_msg, list); + gss_msg->msg.errno = -EPIPE; + atomic_inc(&gss_msg->count); + __gss_unhash_msg(gss_msg); + spin_unlock(&gss_auth->lock); + gss_release_msg(gss_msg); + spin_lock(&gss_auth->lock); + } + spin_unlock(&gss_auth->lock); +} + void gss_pipe_destroy_msg(struct rpc_pipe_msg *msg) { @@ -774,6 +801,7 @@ static struct rpc_pipe_ops gss_upcall_ops = { .upcall = gss_pipe_upcall, .downcall = gss_pipe_downcall, .destroy_msg = gss_pipe_destroy_msg, + .release_pipe = gss_pipe_release, }; /* diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 1841ac8cb0da..409e6aef143d 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -50,18 +50,16 @@ __rpc_purge_upcall(struct inode *inode, int err) msg->errno = err; rpci->ops->destroy_msg(msg); } + while (!list_empty(&rpci->in_upcall)) { + msg = list_entry(rpci->pipe.next, struct rpc_pipe_msg, list); + list_del_init(&msg->list); + msg->errno = err; + rpci->ops->destroy_msg(msg); + } rpci->pipelen = 0; wake_up(&rpci->waitq); } -void -rpc_purge_upcall(struct inode *inode, int err) -{ - down(&inode->i_sem); - __rpc_purge_upcall(inode, err); - up(&inode->i_sem); -} - static void rpc_timeout_upcall_queue(void *data) { @@ -97,20 +95,31 @@ rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg) return res; } -void -rpc_inode_setowner(struct inode *inode, void *private) +static void +rpc_close_pipes(struct inode *inode) { struct rpc_inode *rpci = RPC_I(inode); cancel_delayed_work(&rpci->queue_timeout); flush_scheduled_work(); down(&inode->i_sem); - rpci->private = private; - if (!private) + if (rpci->ops != NULL) { + rpci->nreaders = 0; __rpc_purge_upcall(inode, -EPIPE); + rpci->nwriters = 0; + if (rpci->ops->release_pipe) + rpci->ops->release_pipe(inode); + rpci->ops = NULL; + } up(&inode->i_sem); } +static inline void +rpc_inode_setowner(struct inode *inode, void *private) +{ + RPC_I(inode)->private = private; +} + static struct inode * rpc_alloc_inode(struct super_block *sb) { @@ -134,9 +143,11 @@ rpc_pipe_open(struct inode *inode, struct file *filp) int res = -ENXIO; down(&inode->i_sem); - if (rpci->private != NULL) { + if (rpci->ops != NULL) { if (filp->f_mode & FMODE_READ) rpci->nreaders ++; + if (filp->f_mode & FMODE_WRITE) + rpci->nwriters ++; res = 0; } up(&inode->i_sem); @@ -149,16 +160,24 @@ rpc_pipe_release(struct inode *inode, struct file *filp) struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode); struct rpc_pipe_msg *msg; + down(&inode->i_sem); + if (rpci->ops == NULL) + goto out; msg = (struct rpc_pipe_msg *)filp->private_data; if (msg != NULL) { msg->errno = -EPIPE; + list_del_init(&msg->list); rpci->ops->destroy_msg(msg); } - down(&inode->i_sem); + if (filp->f_mode & FMODE_WRITE) + rpci->nwriters --; if (filp->f_mode & FMODE_READ) rpci->nreaders --; if (!rpci->nreaders) __rpc_purge_upcall(inode, -EPIPE); + if (rpci->ops->release_pipe) + rpci->ops->release_pipe(inode); +out: up(&inode->i_sem); return 0; } @@ -172,7 +191,7 @@ rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) int res = 0; down(&inode->i_sem); - if (!rpci->private) { + if (rpci->ops == NULL) { res = -EPIPE; goto out_unlock; } @@ -182,7 +201,7 @@ rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) msg = list_entry(rpci->pipe.next, struct rpc_pipe_msg, list); - list_del_init(&msg->list); + list_move(&msg->list, &rpci->in_upcall); rpci->pipelen -= msg->len; filp->private_data = msg; msg->copied = 0; @@ -194,6 +213,7 @@ rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) res = rpci->ops->upcall(filp, msg, buf, len); if (res < 0 || msg->len == msg->copied) { filp->private_data = NULL; + list_del_init(&msg->list); rpci->ops->destroy_msg(msg); } out_unlock: @@ -210,7 +230,7 @@ rpc_pipe_write(struct file *filp, const char __user *buf, size_t len, loff_t *of down(&inode->i_sem); res = -EPIPE; - if (rpci->private != NULL) + if (rpci->ops != NULL) res = rpci->ops->downcall(filp, buf, len); up(&inode->i_sem); return res; @@ -226,7 +246,7 @@ rpc_pipe_poll(struct file *filp, struct poll_table_struct *wait) poll_wait(filp, &rpci->waitq, wait); mask = POLLOUT | POLLWRNORM; - if (rpci->private == NULL) + if (rpci->ops == NULL) mask |= POLLERR | POLLHUP; if (!list_empty(&rpci->pipe)) mask |= POLLIN | POLLRDNORM; @@ -242,7 +262,7 @@ rpc_pipe_ioctl(struct inode *ino, struct file *filp, switch (cmd) { case FIONREAD: - if (!rpci->private) + if (rpci->ops == NULL) return -EPIPE; len = rpci->pipelen; if (filp->private_data) { @@ -484,6 +504,7 @@ repeat: do { dentry = dvec[--n]; if (dentry->d_inode) { + rpc_close_pipes(dentry->d_inode); rpc_inode_setowner(dentry->d_inode, NULL); simple_unlink(dir, dentry); } @@ -563,7 +584,10 @@ __rpc_rmdir(struct inode *dir, struct dentry *dentry) int error; shrink_dcache_parent(dentry); - rpc_inode_setowner(dentry->d_inode, NULL); + if (dentry->d_inode) { + rpc_close_pipes(dentry->d_inode); + rpc_inode_setowner(dentry->d_inode, NULL); + } if ((error = simple_rmdir(dir, dentry)) != 0) return error; if (!error) { @@ -715,6 +739,7 @@ rpc_unlink(char *path) } d_drop(dentry); if (dentry->d_inode) { + rpc_close_pipes(dentry->d_inode); rpc_inode_setowner(dentry->d_inode, NULL); error = simple_unlink(dir, dentry); } @@ -790,6 +815,8 @@ init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) inode_init_once(&rpci->vfs_inode); rpci->private = NULL; rpci->nreaders = 0; + rpci->nwriters = 0; + INIT_LIST_HEAD(&rpci->in_upcall); INIT_LIST_HEAD(&rpci->pipe); rpci->pipelen = 0; init_waitqueue_head(&rpci->waitq); -- cgit v1.2.3 From b26ef9fe3e350db44fa93605316de62b742fc13b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:29:20 +0100 Subject: NFSv4: Bugfixes and cleanups for the NFSv4 client name to uid mapper. Includes a fix by Tim Woods to deal with a caching bug in the case where a user and a group share the same numerical id and/or name. --- fs/nfs/idmap.c | 372 ++++++++++++++++++++++++---------------------- fs/nfs/nfs4xdr.c | 32 ++-- include/linux/nfs_idmap.h | 17 ++- 3 files changed, 219 insertions(+), 202 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 155eb0debd83..bd1d1335561c 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -52,14 +52,16 @@ #include #define IDMAP_HASH_SZ 128 -#define IDMAP_HASH_TYPE_NAME 0x01 -#define IDMAP_HASH_TYPE_ID 0x02 -#define IDMAP_HASH_TYPE_INSERT 0x04 struct idmap_hashent { - uid_t ih_id; - char ih_name[IDMAP_NAMESZ]; - u_int32_t ih_namelen; + __u32 ih_id; + int ih_namelen; + char ih_name[IDMAP_NAMESZ]; +}; + +struct idmap_hashtable { + __u8 h_type; + struct idmap_hashent h_entries[IDMAP_HASH_SZ]; }; struct idmap { @@ -67,12 +69,10 @@ struct idmap { struct dentry *idmap_dentry; wait_queue_head_t idmap_wq; struct idmap_msg idmap_im; - struct nfs_server *idmap_server; - struct semaphore idmap_lock; - struct semaphore idmap_im_lock; - struct semaphore idmap_hash_lock; - struct idmap_hashent idmap_id_hash[IDMAP_HASH_SZ]; - struct idmap_hashent idmap_name_hash[IDMAP_HASH_SZ]; + struct semaphore idmap_lock; /* Serializes upcalls */ + struct semaphore idmap_im_lock; /* Protects the hashtable */ + struct idmap_hashtable idmap_user_hash; + struct idmap_hashtable idmap_group_hash; }; static ssize_t idmap_pipe_upcall(struct file *, struct rpc_pipe_msg *, char *, @@ -80,10 +80,7 @@ static ssize_t idmap_pipe_upcall(struct file *, struct rpc_pipe_msg *, char *, static ssize_t idmap_pipe_downcall(struct file *, const char *, size_t); void idmap_pipe_destroy_msg(struct rpc_pipe_msg *); -static int validate_ascii(char *, u_int32_t); - -static u_int32_t fnvhash32(void *, u_int32_t); -static int idmap_cache_lookup(struct idmap *, int, char *, u_int32_t *, uid_t *); +static unsigned int fnvhash32(const void *, size_t); static struct rpc_pipe_ops idmap_upcall_ops = { .upcall = idmap_pipe_upcall, @@ -101,20 +98,19 @@ nfs_idmap_new(struct nfs_server *server) memset(idmap, 0, sizeof(*idmap)); - idmap->idmap_server = server; - snprintf(idmap->idmap_path, sizeof(idmap->idmap_path), - "%s/idmap", idmap->idmap_server->client->cl_pathname); + "%s/idmap", server->client->cl_pathname); idmap->idmap_dentry = rpc_mkpipe(idmap->idmap_path, - idmap->idmap_server, &idmap_upcall_ops, 0); + idmap, &idmap_upcall_ops, 0); if (IS_ERR(idmap->idmap_dentry)) goto err_free; init_MUTEX(&idmap->idmap_lock); init_MUTEX(&idmap->idmap_im_lock); - init_MUTEX(&idmap->idmap_hash_lock); init_waitqueue_head(&idmap->idmap_wq); + idmap->idmap_user_hash.h_type = IDMAP_TYPE_USER; + idmap->idmap_group_hash.h_type = IDMAP_TYPE_GROUP; return (idmap); @@ -135,35 +131,102 @@ nfs_idmap_delete(struct nfs_server *server) kfree(idmap); } +/* + * Helper routines for manipulating the hashtable + */ +static inline struct idmap_hashent * +idmap_name_hash(struct idmap_hashtable* h, const char *name, size_t len) +{ + return &h->h_entries[fnvhash32(name, len) % IDMAP_HASH_SZ]; +} + +static struct idmap_hashent * +idmap_lookup_name(struct idmap_hashtable *h, const char *name, size_t len) +{ + struct idmap_hashent *he = idmap_name_hash(h, name, len); + + if (he->ih_namelen != len || memcmp(he->ih_name, name, len) != 0) + return NULL; + return he; +} + +static inline struct idmap_hashent * +idmap_id_hash(struct idmap_hashtable* h, __u32 id) +{ + return &h->h_entries[fnvhash32(&id, sizeof(id)) % IDMAP_HASH_SZ]; +} + +static struct idmap_hashent * +idmap_lookup_id(struct idmap_hashtable *h, __u32 id) +{ + struct idmap_hashent *he = idmap_id_hash(h, id); + if (he->ih_id != id || he->ih_namelen == 0) + return NULL; + return he; +} + +/* + * Routines for allocating new entries in the hashtable. + * For now, we just have 1 entry per bucket, so it's all + * pretty trivial. + */ +static inline struct idmap_hashent * +idmap_alloc_name(struct idmap_hashtable *h, char *name, unsigned len) +{ + return idmap_name_hash(h, name, len); +} + +static inline struct idmap_hashent * +idmap_alloc_id(struct idmap_hashtable *h, __u32 id) +{ + return idmap_id_hash(h, id); +} + +static void +idmap_update_entry(struct idmap_hashent *he, const char *name, + size_t namelen, __u32 id) +{ + he->ih_id = id; + memcpy(he->ih_name, name, namelen); + he->ih_name[namelen] = '\0'; + he->ih_namelen = namelen; +} + /* * Name -> ID */ -int -nfs_idmap_id(struct nfs_server *server, u_int8_t type, char *name, - u_int namelen, uid_t *id) +static int +nfs_idmap_id(struct idmap *idmap, struct idmap_hashtable *h, + const char *name, size_t namelen, __u32 *id) { struct rpc_pipe_msg msg; - struct idmap *idmap = server->idmap; struct idmap_msg *im; + struct idmap_hashent *he; DECLARE_WAITQUEUE(wq, current); - int ret = -1, hashtype = IDMAP_HASH_TYPE_NAME; - u_int xnamelen = namelen; - - if (idmap == NULL) - return (-1); + int ret = -EIO; im = &idmap->idmap_im; - if (namelen > IDMAP_NAMESZ || namelen == 0) - return (-1); + /* + * String sanity checks + * Note that the userland daemon expects NUL terminated strings + */ + for (;;) { + if (namelen == 0) + return -EINVAL; + if (name[namelen-1] != '\0') + break; + namelen--; + } + if (namelen >= IDMAP_NAMESZ) + return -EINVAL; down(&idmap->idmap_lock); down(&idmap->idmap_im_lock); - if (name[xnamelen - 1] == '\0') - xnamelen--; - - if (idmap_cache_lookup(idmap, hashtype, name, &xnamelen, id) == 0) { + he = idmap_lookup_name(h, name, namelen); + if (he != NULL) { + *id = he->ih_id; ret = 0; goto out; } @@ -171,7 +234,7 @@ nfs_idmap_id(struct nfs_server *server, u_int8_t type, char *name, memset(im, 0, sizeof(*im)); memcpy(im->im_name, name, namelen); - im->im_type = type; + im->im_type = h->h_type; im->im_conv = IDMAP_CONV_NAMETOID; memset(&msg, 0, sizeof(msg)); @@ -191,16 +254,9 @@ nfs_idmap_id(struct nfs_server *server, u_int8_t type, char *name, remove_wait_queue(&idmap->idmap_wq, &wq); down(&idmap->idmap_im_lock); - /* - * XXX Race condition here, with testing for status. Go ahead - * and and do the cace lookup anyway. - */ if (im->im_status & IDMAP_STATUS_SUCCESS) { - ret = 0; *id = im->im_id; - - hashtype |= IDMAP_HASH_TYPE_INSERT; - ret = idmap_cache_lookup(idmap, hashtype, name, &xnamelen, id); + ret = 0; } out: @@ -213,35 +269,31 @@ nfs_idmap_id(struct nfs_server *server, u_int8_t type, char *name, /* * ID -> Name */ -int -nfs_idmap_name(struct nfs_server *server, u_int8_t type, uid_t id, - char *name, u_int *namelen) +static int +nfs_idmap_name(struct idmap *idmap, struct idmap_hashtable *h, + __u32 id, char *name) { struct rpc_pipe_msg msg; - struct idmap *idmap = server->idmap; struct idmap_msg *im; + struct idmap_hashent *he; DECLARE_WAITQUEUE(wq, current); - int ret = -1, hashtype = IDMAP_HASH_TYPE_ID; - u_int len; - - if (idmap == NULL) - return (-1); + int ret = -EIO; + unsigned int len; im = &idmap->idmap_im; - if (*namelen < IDMAP_NAMESZ || *namelen == 0) - return (-1); - down(&idmap->idmap_lock); down(&idmap->idmap_im_lock); - if (idmap_cache_lookup(idmap, hashtype, name, namelen, &id) == 0) { - ret = 0; + he = idmap_lookup_id(h, id); + if (he != 0) { + memcpy(name, he->ih_name, he->ih_namelen); + ret = he->ih_namelen; goto out; } memset(im, 0, sizeof(*im)); - im->im_type = type; + im->im_type = h->h_type; im->im_conv = IDMAP_CONV_IDTONAME; im->im_id = id; @@ -256,9 +308,6 @@ nfs_idmap_name(struct nfs_server *server, u_int8_t type, uid_t id, goto out; } - /* - * XXX add timeouts here - */ set_current_state(TASK_UNINTERRUPTIBLE); up(&idmap->idmap_im_lock); schedule(); @@ -267,23 +316,20 @@ nfs_idmap_name(struct nfs_server *server, u_int8_t type, uid_t id, down(&idmap->idmap_im_lock); if (im->im_status & IDMAP_STATUS_SUCCESS) { - if ((len = validate_ascii(im->im_name, IDMAP_NAMESZ)) == -1) + if ((len = strnlen(im->im_name, IDMAP_NAMESZ)) == 0) goto out; - ret = 0; memcpy(name, im->im_name, len); - *namelen = len; - - hashtype |= IDMAP_HASH_TYPE_INSERT; - ret = idmap_cache_lookup(idmap, hashtype, name, namelen, &id); + ret = len; } out: memset(im, 0, sizeof(*im)); up(&idmap->idmap_im_lock); up(&idmap->idmap_lock); - return (ret); + return ret; } +/* RPC pipefs upcall/downcall routines */ static ssize_t idmap_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg, char *dst, size_t buflen) @@ -310,10 +356,12 @@ static ssize_t idmap_pipe_downcall(struct file *filp, const char *src, size_t mlen) { struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode); - struct nfs_server *server = rpci->private; - struct idmap *idmap = server->idmap; + struct idmap *idmap = (struct idmap *)rpci->private; struct idmap_msg im_in, *im = &idmap->idmap_im; - int match = 0, hashtype, badmsg = 0, namelen_in, namelen; + struct idmap_hashtable *h; + struct idmap_hashent *he = NULL; + int namelen_in; + int ret; if (mlen != sizeof(im_in)) return (-ENOSPC); @@ -323,39 +371,66 @@ idmap_pipe_downcall(struct file *filp, const char *src, size_t mlen) down(&idmap->idmap_im_lock); - namelen_in = validate_ascii(im_in.im_name, IDMAP_NAMESZ); - namelen = validate_ascii(im->im_name, IDMAP_NAMESZ); + ret = mlen; + im->im_status = im_in.im_status; + /* If we got an error, terminate now, and wake up pending upcalls */ + if (!(im_in.im_status & IDMAP_STATUS_SUCCESS)) { + wake_up(&idmap->idmap_wq); + goto out; + } + + /* Sanity checking of strings */ + ret = -EINVAL; + namelen_in = strnlen(im_in.im_name, IDMAP_NAMESZ); + if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) + goto out; - badmsg = !(im_in.im_status & IDMAP_STATUS_SUCCESS) || namelen_in <= 0; + switch (im_in.im_type) { + case IDMAP_TYPE_USER: + h = &idmap->idmap_user_hash; + break; + case IDMAP_TYPE_GROUP: + h = &idmap->idmap_group_hash; + break; + default: + goto out; + } switch (im_in.im_conv) { case IDMAP_CONV_IDTONAME: - match = im->im_id == im_in.im_id; + /* Did we match the current upcall? */ + if (im->im_conv == IDMAP_CONV_IDTONAME + && im->im_type == im_in.im_type + && im->im_id == im_in.im_id) { + /* Yes: copy string, including the terminating '\0' */ + memcpy(im->im_name, im_in.im_name, namelen_in); + im->im_name[namelen_in] = '\0'; + wake_up(&idmap->idmap_wq); + } + he = idmap_alloc_id(h, im_in.im_id); break; case IDMAP_CONV_NAMETOID: - match = namelen == namelen_in && - memcmp(im->im_name, im_in.im_name, namelen) == 0; + /* Did we match the current upcall? */ + if (im->im_conv == IDMAP_CONV_NAMETOID + && im->im_type == im_in.im_type + && strnlen(im->im_name, IDMAP_NAMESZ) == namelen_in + && memcmp(im->im_name, im_in.im_name, namelen_in) == 0) { + im->im_id = im_in.im_id; + wake_up(&idmap->idmap_wq); + } + he = idmap_alloc_name(h, im_in.im_name, namelen_in); break; default: - badmsg = 1; - break; - } - - match = match && im->im_type == im_in.im_type; - - if (match) { - memcpy(im, &im_in, sizeof(*im)); - wake_up(&idmap->idmap_wq); - } else if (!badmsg) { - hashtype = im_in.im_conv == IDMAP_CONV_IDTONAME ? - IDMAP_HASH_TYPE_ID : IDMAP_HASH_TYPE_NAME; - hashtype |= IDMAP_HASH_TYPE_INSERT; - idmap_cache_lookup(idmap, hashtype, im_in.im_name, &namelen_in, - &im_in.im_id); + goto out; } + /* If the entry is valid, also copy it to the cache */ + if (he != NULL) + idmap_update_entry(he, im_in.im_name, namelen_in, im_in.im_id); + ret = mlen; +out: up(&idmap->idmap_im_lock); - return (mlen); + return ret; } void @@ -372,108 +447,51 @@ idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg) up(&idmap->idmap_im_lock); } -static int -validate_ascii(char *string, u_int32_t len) -{ - int i; - - for (i = 0; i < len; i++) { - if (string[i] == '\0') - break; - - if (string[i] & 0x80) - return (-1); - } - - if (string[i] != '\0') - return (-1); - - return (i); -} - /* * Fowler/Noll/Vo hash * http://www.isthe.com/chongo/tech/comp/fnv/ */ -#define FNV_P_32 ((u_int32_t)0x01000193) /* 16777619 */ -#define FNV_1_32 ((u_int32_t)0x811c9dc5) /* 2166136261 */ +#define FNV_P_32 ((unsigned int)0x01000193) /* 16777619 */ +#define FNV_1_32 ((unsigned int)0x811c9dc5) /* 2166136261 */ -static u_int32_t -fnvhash32(void *buf, u_int32_t buflen) +static unsigned int fnvhash32(const void *buf, size_t buflen) { - u_char *p, *end = (u_char *)buf + buflen; - u_int32_t hash = FNV_1_32; + const unsigned char *p, *end = (const unsigned char *)buf + buflen; + unsigned int hash = FNV_1_32; for (p = buf; p < end; p++) { hash *= FNV_P_32; - hash ^= (u_int32_t)*p; + hash ^= (unsigned int)*p; } return (hash); } -/* - * ->ih_namelen == 0 indicates negative entry - */ -static int -idmap_cache_lookup(struct idmap *idmap, int type, char *name, u_int32_t *namelen, - uid_t *id) +int nfs_map_name_to_uid(struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) { - u_int32_t hash; - struct idmap_hashent *he = NULL; - int insert = type & IDMAP_HASH_TYPE_INSERT; - int ret = -1; - - /* - * XXX technically, this is not needed, since we will always - * hold idmap_im_lock when altering the hash tables. but - * semantically that just hurts. - * - * XXX cache negative responses - */ - down(&idmap->idmap_hash_lock); + struct idmap *idmap = server->idmap; - if (*namelen > IDMAP_NAMESZ || *namelen == 0) - goto out; + return nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid); +} - if (type & IDMAP_HASH_TYPE_NAME) { - hash = fnvhash32(name, *namelen) % IDMAP_HASH_SZ; - he = &idmap->idmap_name_hash[hash]; - - /* - * Testing he->ih_namelen == *namelen implicitly tests - * namelen != 0, and thus a non-negative entry. - */ - if (!insert && he->ih_namelen == *namelen && - memcmp(he->ih_name, name, *namelen) == 0) { - *id = he->ih_id; - ret = 0; - goto out; - } - } +int nfs_map_group_to_gid(struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) +{ + struct idmap *idmap = server->idmap; - if (type & IDMAP_HASH_TYPE_ID) { - hash = fnvhash32(id, sizeof(*id)) % IDMAP_HASH_SZ; - he = &idmap->idmap_id_hash[hash]; + return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); +} - if (!insert && *id == he->ih_id && he->ih_namelen != 0 && - *namelen >= he->ih_namelen) { - memcpy(name, he->ih_name, he->ih_namelen); - *namelen = he->ih_namelen; - ret = 0; - goto out; - } - } +int nfs_map_uid_to_name(struct nfs_server *server, __u32 uid, char *buf) +{ + struct idmap *idmap = server->idmap; - if (insert && he != NULL) { - he->ih_id = *id; - memcpy(he->ih_name, name, *namelen); - he->ih_namelen = *namelen; - ret = 0; - } + return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); +} +int nfs_map_gid_to_group(struct nfs_server *server, __u32 uid, char *buf) +{ + struct idmap *idmap = server->idmap; - out: - up(&idmap->idmap_hash_lock); - return (ret); + return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); } + diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index f2a909d75468..35a28e9e7d21 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -239,10 +239,10 @@ static int encode_attrs(struct xdr_stream *xdr, struct iattr *iap, struct nfs_server *server) { - char owner_name[256]; - char owner_group[256]; - int owner_namelen = sizeof(owner_name); - int owner_grouplen = sizeof(owner_group); + char owner_name[IDMAP_NAMESZ]; + char owner_group[IDMAP_NAMESZ]; + int owner_namelen = 0; + int owner_grouplen = 0; uint32_t *p; uint32_t *q; int len; @@ -265,9 +265,8 @@ encode_attrs(struct xdr_stream *xdr, struct iattr *iap, if (iap->ia_valid & ATTR_MODE) len += 4; if (iap->ia_valid & ATTR_UID) { - status = nfs_idmap_name(server, IDMAP_TYPE_USER, - iap->ia_uid, owner_name, &owner_namelen); - if (status < 0) { + owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name); + if (owner_namelen < 0) { printk(KERN_WARNING "nfs: couldn't resolve uid %d to string\n", iap->ia_uid); /* XXX */ @@ -278,9 +277,8 @@ encode_attrs(struct xdr_stream *xdr, struct iattr *iap, len += 4 + (XDR_QUADLEN(owner_namelen) << 2); } if (iap->ia_valid & ATTR_GID) { - status = nfs_idmap_name(server, IDMAP_TYPE_GROUP, - iap->ia_gid, owner_group, &owner_grouplen); - if (status < 0) { + owner_grouplen = nfs_map_gid_to_group(server, iap->ia_gid, owner_group); + if (owner_grouplen < 0) { printk(KERN_WARNING "nfs4: couldn't resolve gid %d to string\n", iap->ia_gid); strcpy(owner_group, "nobody"); @@ -1475,10 +1473,9 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, } READ_BUF(dummy32); len += (XDR_QUADLEN(dummy32) << 2); - if ((status = nfs_idmap_id(server, IDMAP_TYPE_USER, - (char *)p, dummy32, &nfp->uid)) == -1) { - dprintk("read_attrs: gss_get_num failed!\n"); - /* goto out; */ + if ((status = nfs_map_name_to_uid(server, (char *)p, dummy32, + &nfp->uid)) < 0) { + dprintk("read_attrs: name-to-uid mapping failed!\n"); nfp->uid = -2; } dprintk("read_attrs: uid=%d\n", (int)nfp->uid); @@ -1493,11 +1490,10 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, } READ_BUF(dummy32); len += (XDR_QUADLEN(dummy32) << 2); - if ((status = nfs_idmap_id(server, IDMAP_TYPE_GROUP, - (char *)p, dummy32, &nfp->gid)) == -1) { - dprintk("read_attrs: gss_get_num failed!\n"); + if ((status = nfs_map_group_to_gid(server, (char *)p, dummy32, + &nfp->gid)) < 0) { + dprintk("read_attrs: group-to-gid mapping failed!\n"); nfp->gid = -2; - /* goto out; */ } dprintk("read_attrs: gid=%d\n", (int)nfp->gid); } diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 248adf707071..c95076e5941b 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -52,18 +52,21 @@ #define IDMAP_STATUS_SUCCESS 0x08 struct idmap_msg { - u_int8_t im_type; - u_int8_t im_conv; - char im_name[IDMAP_NAMESZ]; - u_int32_t im_id; - u_int8_t im_status; + __u8 im_type; + __u8 im_conv; + char im_name[IDMAP_NAMESZ]; + __u32 im_id; + __u8 im_status; }; #ifdef __KERNEL__ void *nfs_idmap_new(struct nfs_server *); void nfs_idmap_delete(struct nfs_server *); -int nfs_idmap_id(struct nfs_server *, u_int8_t, char *, u_int, uid_t *); -int nfs_idmap_name(struct nfs_server *, u_int8_t, uid_t, char *, u_int *); + +int nfs_map_name_to_uid(struct nfs_server *, const char *, size_t, __u32 *); +int nfs_map_group_to_gid(struct nfs_server *, const char *, size_t, __u32 *); +int nfs_map_uid_to_name(struct nfs_server *, __u32, char *); +int nfs_map_gid_to_group(struct nfs_server *, __u32, char *); #endif /* __KERNEL__ */ #endif /* NFS_IDMAP_H */ -- cgit v1.2.3 From 3f13d9aac2492d1f1c543453785f25bff0255aab Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:34:13 +0100 Subject: RPCSEC_GSS: Miscellaneous cleanups of the krb5 code required for the integrity checksumming mode. --- include/linux/sunrpc/gss_krb5.h | 20 +---- net/sunrpc/auth_gss/gss_krb5_crypto.c | 44 +++++------ net/sunrpc/auth_gss/gss_krb5_mech.c | 28 +++---- net/sunrpc/auth_gss/gss_krb5_seal.c | 43 +++-------- net/sunrpc/auth_gss/gss_krb5_unseal.c | 134 +++++++++------------------------- 5 files changed, 84 insertions(+), 185 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h index 8db6d1e13a69..aac2ad4f7d56 100644 --- a/include/linux/sunrpc/gss_krb5.h +++ b/include/linux/sunrpc/gss_krb5.h @@ -50,7 +50,6 @@ struct krb5_ctx { struct crypto_tfm *seq; s32 endtime; u32 seq_send; - u32 seq_recv; struct xdr_netobj mech_used; }; @@ -73,7 +72,7 @@ enum seal_alg { SEAL_ALG_DES3KD = 0x0002 }; -#define RSA_MD5_CKSUM_LENGTH 16 +#define KRB5_CKSUM_LENGTH 8 #define CKSUMTYPE_CRC32 0x0001 #define CKSUMTYPE_RSA_MD4 0x0002 @@ -100,16 +99,6 @@ enum seal_alg { #define KG_EMPTY_CCACHE (39756044L) #define KG_NO_CTYPES (39756045L) -#define KV5M_PRINCIPAL (-1760647423L) -#define KV5M_KEYBLOCK (-1760647421L) -#define KV5M_CHECKSUM (-1760647420L) -#define KV5M_ADDRESS (-1760647390L) -#define KV5M_AUTHENTICATOR (-1760647410L) -#define KV5M_AUTH_CONTEXT (-1760647383L) -#define KV5M_AUTHDATA (-1760647414L) -#define KV5M_GSS_OID (-1760647372L) -#define KV5M_GSS_QUEUE (-1760647371L) - /* per Kerberos v5 protocol spec crypto types from the wire. * these get mapped to linux kernel crypto routines. */ @@ -126,14 +115,13 @@ enum seal_alg { #define ENCTYPE_UNKNOWN 0x01ff s32 -krb5_make_checksum(s32 cksumtype, - struct xdr_netobj *input, +krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, struct xdr_netobj *cksum); u32 krb5_make_token(struct krb5_ctx *context_handle, int qop_req, - struct xdr_netobj * input_message_buffer, - struct xdr_netobj * output_message_buffer, int toktype); + struct xdr_netobj *input_message_buffer, + struct xdr_netobj *output_message_buffer, int toktype); u32 krb5_read_token(struct krb5_ctx *context_handle, diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index c3a8f548a482..9894d83ddf6f 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c @@ -122,14 +122,23 @@ out: return(ret); } +void +buf_to_sg(struct scatterlist *sg, char *ptr, int len) { + sg->page = virt_to_page(ptr); + sg->offset = offset_in_page(ptr); + sg->length = len; +} + +/* checksum the plaintext data and the first 8 bytes of the krb5 token header, + * as specified by the rfc: */ s32 -krb5_make_checksum(s32 cksumtype, struct xdr_netobj *input, +krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, struct xdr_netobj *cksum) { - s32 ret = -EINVAL; - struct scatterlist sg[1]; - char *cksumname; - struct crypto_tfm *tfm; + char *cksumname; + struct crypto_tfm *tfm = NULL; /* XXX add to ctx? */ + struct scatterlist sg[2]; + u32 code = GSS_S_FAILURE; switch (cksumtype) { case CKSUMTYPE_RSA_MD5: @@ -143,24 +152,17 @@ krb5_make_checksum(s32 cksumtype, struct xdr_netobj *input, if (!(tfm = crypto_alloc_tfm(cksumname, 0))) goto out; cksum->len = crypto_tfm_alg_digestsize(tfm); + if ((cksum->data = kmalloc(cksum->len, GFP_KERNEL)) == NULL) + goto out; - if ((cksum->data = kmalloc(cksum->len, GFP_KERNEL)) == NULL) { - ret = -ENOMEM; - goto out_free_tfm; - } - sg[0].page = virt_to_page(input->data); - sg[0].offset = offset_in_page(input->data); - sg[0].length = input->len; - + buf_to_sg(&sg[0], header, 8); + buf_to_sg(&sg[1], body, body_len); crypto_digest_init(tfm); - crypto_digest_update(tfm, sg, 1); + crypto_digest_update(tfm, sg, 2); crypto_digest_final(tfm, cksum->data); - - ret = 0; - -out_free_tfm: - crypto_free_tfm(tfm); + code = 0; out: - dprintk("RPC: gss_k5cksum: returning %d\n", ret); - return (ret); + if (tfm) + crypto_free_tfm(tfm); + return code; } diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index ce31a89684e4..61282b4d9c3b 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -98,7 +98,7 @@ get_key(char **p, char *end, struct crypto_tfm **res) alg_mode = CRYPTO_TFM_MODE_CBC; break; default: - dprintk("RPC: get_key: unsupported algorithm %d", alg); + dprintk("RPC: get_key: unsupported algorithm %d\n", alg); goto out_err_free_key; } if (!(*res = crypto_alloc_tfm(alg_name, alg_mode))) @@ -168,7 +168,7 @@ out_err: return GSS_S_FAILURE; } -void +static void gss_delete_sec_context_kerberos(void *internal_ctx) { struct krb5_ctx *kctx = internal_ctx; @@ -181,16 +181,16 @@ gss_delete_sec_context_kerberos(void *internal_ctx) { kfree(kctx); } -u32 +static u32 gss_verify_mic_kerberos(struct gss_ctx *ctx, - struct xdr_netobj *signbuf, - struct xdr_netobj *checksum, - u32 *qstate) { + struct xdr_netobj *message, + struct xdr_netobj *mic_token, + u32 *qstate) { u32 maj_stat = 0; int qop_state; struct krb5_ctx *kctx = ctx->internal_ctx_id; - maj_stat = krb5_read_token(kctx, checksum, signbuf, &qop_state, + maj_stat = krb5_read_token(kctx, mic_token, message, &qop_state, KG_TOK_MIC_MSG); if (!maj_stat && qop_state) *qstate = qop_state; @@ -199,21 +199,17 @@ gss_verify_mic_kerberos(struct gss_ctx *ctx, return maj_stat; } -u32 +static u32 gss_get_mic_kerberos(struct gss_ctx *ctx, u32 qop, - struct xdr_netobj *message_buffer, - struct xdr_netobj *message_token) { + struct xdr_netobj *message, + struct xdr_netobj *mic_token) { u32 err = 0; struct krb5_ctx *kctx = ctx->internal_ctx_id; - if (!message_buffer->data) return GSS_S_FAILURE; - - dprintk("RPC: gss_get_mic_kerberos:" - " message_buffer->len %d\n",message_buffer->len); + if (!message->data) return GSS_S_FAILURE; - err = krb5_make_token(kctx, qop, message_buffer, - message_token, KG_TOK_MIC_MSG); + err = krb5_make_token(kctx, qop, message, mic_token, KG_TOK_MIC_MSG); dprintk("RPC: gss_get_mic_kerberos returning %d\n",err); diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index 280d82d7c6dc..e664d3ea98ce 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c @@ -63,14 +63,13 @@ #include #include #include +#include #include #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_AUTH #endif -#define CKSUM_SIZE 8 - static inline int gss_krb5_padding(int blocksize, int length) { /* Most of the code is block-size independent but in practice we @@ -79,29 +78,6 @@ gss_krb5_padding(int blocksize, int length) { return 8 - (length & 7); } -/* checksum the plaintext data and the first 8 bytes of the krb5 token header, - * as specified by the rfc: */ -static u32 -compute_checksum(s32 checksum_type, char *header, char *body, int body_len, - struct xdr_netobj *md5cksum) { - char *data_ptr; - struct xdr_netobj plaind; - u32 code = GSS_S_FAILURE; - - if (!(data_ptr = kmalloc(8 + body_len, GFP_KERNEL))) - goto out; - memcpy(data_ptr, header, 8); - memcpy(data_ptr + 8, body, body_len); - plaind.len = 8 + body_len; - plaind.data = data_ptr; - code = krb5_make_checksum(checksum_type, &plaind, md5cksum); - kfree(data_ptr); - code = 0; - -out: - return code; -} - u32 krb5_make_token(struct krb5_ctx *ctx, int qop_req, struct xdr_netobj * text, struct xdr_netobj * token, @@ -113,10 +89,12 @@ krb5_make_token(struct krb5_ctx *ctx, int qop_req, unsigned char *ptr, *krb5_hdr, *msg_start; s32 now; - dprintk("RPC: gss_krb5_seal"); + dprintk("RPC: gss_krb5_seal\n"); now = jiffies; + token->data = NULL; + if (qop_req != 0) goto out_err; @@ -167,8 +145,8 @@ krb5_make_token(struct krb5_ctx *ctx, int qop_req, memset(msg_start + blocksize + text->len, pad, pad); - if (compute_checksum(checksum_type, krb5_hdr, msg_start, - tmsglen, &md5cksum)) + if (krb5_make_checksum(checksum_type, krb5_hdr, msg_start, + tmsglen, &md5cksum)) goto out_err; if (krb5_encrypt(ctx->enc, NULL, msg_start, msg_start, @@ -176,8 +154,8 @@ krb5_make_token(struct krb5_ctx *ctx, int qop_req, goto out_err; } else { /* Sign only. */ - if (compute_checksum(checksum_type, krb5_hdr, text->data, - text->len, &md5cksum)) + if (krb5_make_checksum(checksum_type, krb5_hdr, text->data, + text->len, &md5cksum)) goto out_err; } @@ -187,10 +165,11 @@ krb5_make_token(struct krb5_ctx *ctx, int qop_req, md5cksum.data, md5cksum.len)) goto out_err; memcpy(krb5_hdr + 16, - md5cksum.data + md5cksum.len - CKSUM_SIZE, CKSUM_SIZE); + md5cksum.data + md5cksum.len - KRB5_CKSUM_LENGTH, + KRB5_CKSUM_LENGTH); dprintk("make_seal_token: cksum data: \n"); - print_hexl((u32 *) (krb5_hdr + 16), CKSUM_SIZE, 0); + print_hexl((u32 *) (krb5_hdr + 16), KRB5_CKSUM_LENGTH, 0); break; default: BUG(); diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c index 836c683777f2..8b2795d701db 100644 --- a/net/sunrpc/auth_gss/gss_krb5_unseal.c +++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c @@ -68,7 +68,12 @@ #endif -/* message_buffer is an input if MIC and an output if WRAP. */ +/* message_buffer is an input if toktype is MIC and an output if it is WRAP: + * If toktype is MIC: read_token is a mic token, and message_buffer is the + * data that the mic was supposedly taken over. + * If toktype is WRAP: read_token is a wrap token, and message_buffer is used + * to return the decrypted data. + */ u32 krb5_read_token(struct krb5_ctx *ctx, @@ -76,20 +81,13 @@ krb5_read_token(struct krb5_ctx *ctx, struct xdr_netobj *message_buffer, int *qop_state, int toktype) { - s32 code; - int tmsglen = 0; - int conflen = 0; int signalg; int sealalg; struct xdr_netobj token = {.len = 0, .data = NULL}; s32 checksum_type; - struct xdr_netobj cksum; struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; - struct xdr_netobj plaind; - char *data_ptr; s32 now; unsigned char *plain = NULL; - int cksum_len = 0; int plainlen = 0; int direction; s32 seqnum; @@ -97,10 +95,9 @@ krb5_read_token(struct krb5_ctx *ctx, int bodysize; u32 ret = GSS_S_DEFECTIVE_TOKEN; - dprintk("RPC: krb5_read_token\n"); + dprintk("RPC: krb5_read_token\n"); - if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used, - &bodysize, &ptr, toktype, + if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr, toktype, read_token->len)) goto out; @@ -138,40 +135,22 @@ krb5_read_token(struct krb5_ctx *ctx, signalg != SGN_ALG_HMAC_SHA1_DES3_KD)) goto out; - /* starting with a single alg */ - switch (signalg) { - case SGN_ALG_DES_MAC_MD5: - cksum_len = 8; - break; - default: - goto out; - } - - if (toktype == KG_TOK_WRAP_MSG) - tmsglen = bodysize - (14 + cksum_len); - - /* get the token parameters */ - - /* decode the message, if WRAP */ - if (toktype == KG_TOK_WRAP_MSG) { - dprintk("RPC: krb5_read_token KG_TOK_WRAP_MSG\n"); + int conflen = crypto_tfm_alg_blocksize(ctx->enc); + int padlen; - plain = kmalloc(tmsglen, GFP_KERNEL); - ret = GSS_S_FAILURE; - if (plain == NULL) - goto out; + plainlen = bodysize - (14 + KRB5_CKSUM_LENGTH); + plain = ptr + 14 + KRB5_CKSUM_LENGTH; - code = krb5_decrypt(ctx->enc, NULL, - ptr + 14 + cksum_len, plain, - tmsglen); - if (code) + ret = krb5_decrypt(ctx->enc, NULL, plain, plain, plainlen); + if (ret) goto out; - plainlen = tmsglen; - - conflen = crypto_tfm_alg_blocksize(ctx->enc); - token.len = tmsglen - conflen - plain[tmsglen - 1]; + ret = GSS_S_FAILURE; + padlen = plain[plainlen -1]; + if ((padlen < 1) || (padlen > 8)) + goto out; + token.len = plainlen - conflen - padlen; if (token.len) { token.data = kmalloc(token.len, GFP_KERNEL); @@ -181,15 +160,13 @@ krb5_read_token(struct krb5_ctx *ctx, } } else if (toktype == KG_TOK_MIC_MSG) { - dprintk("RPC: krb5_read_token KG_TOK_MIC_MSG\n"); token = *message_buffer; plain = token.data; plainlen = token.len; } else { - token.len = 0; - token.data = NULL; - plain = token.data; - plainlen = token.len; + printk("RPC: bad toktype in krb5_read_token"); + ret = GSS_S_FAILURE; + goto out; } dprintk("RPC krb5_read_token: token.len %d plainlen %d\n", token.len, @@ -209,67 +186,26 @@ krb5_read_token(struct krb5_ctx *ctx, switch (signalg) { case SGN_ALG_DES_MAC_MD5: - dprintk("RPC krb5_read_token SGN_ALG_DES_MAC_MD5\n"); - /* compute the checksum of the message. - * 8 = bytes of token body to be checksummed according to spec - */ - - data_ptr = kmalloc(8 + plainlen, GFP_KERNEL); - ret = GSS_S_FAILURE; - if (!data_ptr) + ret = krb5_make_checksum(checksum_type, ptr - 2, plain, + plainlen, &md5cksum); + if (ret) goto out; - memcpy(data_ptr, ptr - 2, 8); - memcpy(data_ptr + 8, plain, plainlen); - - plaind.len = 8 + plainlen; - plaind.data = data_ptr; - - code = krb5_make_checksum(checksum_type, - &plaind, &md5cksum); - - kfree(data_ptr); - - if (code) + ret = krb5_encrypt(ctx->seq, NULL, md5cksum.data, + md5cksum.data, 16); + if (ret) goto out; - code = krb5_encrypt(ctx->seq, NULL, md5cksum.data, - md5cksum.data, 16); - if (code) + if (memcmp(md5cksum.data + 8, ptr + 14, 8)) { + ret = GSS_S_BAD_SIG; goto out; - - if (signalg == 0) - cksum.len = 8; - else - cksum.len = 16; - cksum.data = md5cksum.data + 16 - cksum.len; - - dprintk - ("RPC: krb5_read_token: memcmp digest cksum.len %d:\n", - cksum.len); - dprintk(" md5cksum.data\n"); - print_hexl((u32 *) md5cksum.data, 16, 0); - dprintk(" cksum.data:\n"); - print_hexl((u32 *) cksum.data, cksum.len, 0); - { - u32 *p; - - (u8 *) p = ptr + 14; - dprintk(" ptr+14:\n"); - print_hexl(p, cksum.len, 0); } - - code = memcmp(cksum.data, ptr + 14, cksum.len); break; default: ret = GSS_S_DEFECTIVE_TOKEN; goto out; } - ret = GSS_S_BAD_SIG; - if (code) - goto out; - /* it got through unscathed. Make sure the context is unexpired */ if (toktype == KG_TOK_WRAP_MSG) @@ -287,8 +223,8 @@ krb5_read_token(struct krb5_ctx *ctx, /* do sequencing checks */ ret = GSS_S_BAD_SIG; - if ((code = krb5_get_seq_num(ctx->seq, ptr + 14, ptr + 6, &direction, - &seqnum))) + if ((ret = krb5_get_seq_num(ctx->seq, ptr + 14, ptr + 6, &direction, + &seqnum))) goto out; if ((ctx->initiate && direction != 0xff) || @@ -298,9 +234,7 @@ krb5_read_token(struct krb5_ctx *ctx, ret = GSS_S_COMPLETE; out: if (md5cksum.data) kfree(md5cksum.data); - if (toktype == KG_TOK_WRAP_MSG) { - if (plain) kfree(plain); - if (ret && token.data) kfree(token.data); - } + if ((toktype == KG_TOK_WRAP_MSG) && ret && token.data) + kfree(token.data); return ret; } -- cgit v1.2.3 From 955ac3514fc5eb754827bd69edbe09012b95beb8 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:44:01 +0100 Subject: RPCSEC_GSS: Client-side only support for rpcsec_gss integrity protection. Since this requires checksumming an entire request, instead of just the header, and since the request may include, for example, pages with write data, we modify the gss_api routines to pass xdr_bufs instead of xdr_netobjs where necessary. We add rpcauth_wrap_req and rpcauth_unwrap_resp to rpcauth.c, wrappers for the new rpc cred ops crwrap_req and crunwrap_req, which are called just before encoding, and just after decoding, respectively. --- include/linux/sunrpc/auth.h | 6 + include/linux/sunrpc/gss_api.h | 9 +- include/linux/sunrpc/gss_krb5.h | 6 +- include/linux/sunrpc/xdr.h | 4 + net/sunrpc/auth.c | 29 +++++ net/sunrpc/auth_gss/auth_gss.c | 231 ++++++++++++++++++++++++++++------ net/sunrpc/auth_gss/gss_krb5_crypto.c | 51 ++++++-- net/sunrpc/auth_gss/gss_krb5_mech.c | 8 +- net/sunrpc/auth_gss/gss_krb5_seal.c | 23 +--- net/sunrpc/auth_gss/gss_krb5_unseal.c | 58 ++------- net/sunrpc/auth_gss/gss_mech_switch.c | 4 +- net/sunrpc/clnt.c | 6 +- net/sunrpc/sunrpc_syms.c | 3 + net/sunrpc/xdr.c | 144 ++++++++++++++++++++- 14 files changed, 450 insertions(+), 132 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index 70cc3360e608..a68f18bf0a46 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -102,6 +102,10 @@ struct rpc_credops { u32 * (*crmarshal)(struct rpc_task *, u32 *, int); int (*crrefresh)(struct rpc_task *); u32 * (*crvalidate)(struct rpc_task *, u32 *); + int (*crwrap_req)(struct rpc_task *, kxdrproc_t, + void *, u32 *, void *); + int (*crunwrap_resp)(struct rpc_task *, kxdrproc_t, + void *, u32 *, void *); }; extern struct rpc_authops authunix_ops; @@ -124,6 +128,8 @@ void put_rpccred(struct rpc_cred *); void rpcauth_unbindcred(struct rpc_task *); u32 * rpcauth_marshcred(struct rpc_task *, u32 *); u32 * rpcauth_checkverf(struct rpc_task *, u32 *); +int rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, u32 *data, void *obj); +int rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, u32 *data, void *obj); int rpcauth_refreshcred(struct rpc_task *); void rpcauth_invalcred(struct rpc_task *); int rpcauth_uptodatecred(struct rpc_task *); diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h index 35988e7bfb77..cbb60ac22fd4 100644 --- a/include/linux/sunrpc/gss_api.h +++ b/include/linux/sunrpc/gss_api.h @@ -16,6 +16,7 @@ #ifdef __KERNEL__ #include +#include /* The mechanism-independent gss-api context: */ struct gss_ctx { @@ -39,11 +40,11 @@ u32 gss_import_sec_context( u32 gss_get_mic( struct gss_ctx *ctx_id, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token); u32 gss_verify_mic( struct gss_ctx *ctx_id, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate); u32 gss_delete_sec_context( @@ -95,11 +96,11 @@ struct gss_api_ops { u32 (*gss_get_mic)( struct gss_ctx *ctx_id, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token); u32 (*gss_verify_mic)( struct gss_ctx *ctx_id, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate); void (*gss_delete_sec_context)( diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h index aac2ad4f7d56..9616746407f3 100644 --- a/include/linux/sunrpc/gss_krb5.h +++ b/include/linux/sunrpc/gss_krb5.h @@ -115,18 +115,18 @@ enum seal_alg { #define ENCTYPE_UNKNOWN 0x01ff s32 -krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, +krb5_make_checksum(s32 cksumtype, char *header, struct xdr_buf *body, struct xdr_netobj *cksum); u32 krb5_make_token(struct krb5_ctx *context_handle, int qop_req, - struct xdr_netobj *input_message_buffer, + struct xdr_buf *input_message_buffer, struct xdr_netobj *output_message_buffer, int toktype); u32 krb5_read_token(struct krb5_ctx *context_handle, struct xdr_netobj *input_token_buffer, - struct xdr_netobj *message_buffer, + struct xdr_buf *message_buffer, int *qop_state, int toktype); u32 diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 2c6f76d1cc14..8082a0029100 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -141,6 +141,10 @@ void xdr_shift_iovec(struct iovec *, int, size_t); extern int xdr_kmap(struct iovec *, struct xdr_buf *, size_t); extern void xdr_kunmap(struct xdr_buf *, size_t); extern void xdr_shift_buf(struct xdr_buf *, size_t); +extern void _copy_from_pages(char *, struct page **, size_t, size_t); +extern void xdr_buf_from_iov(struct iovec *, struct xdr_buf *); +extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, int, int); +extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, int); /* * Helper structure for copying from an sk_buff. diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index f1a73646e6ed..a89b30cadff3 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -339,6 +339,35 @@ rpcauth_checkverf(struct rpc_task *task, u32 *p) return cred->cr_ops->crvalidate(task, p); } +int +rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, + u32 *data, void *obj) +{ + struct rpc_cred *cred = task->tk_msg.rpc_cred; + + dprintk("RPC: %4d using %s cred %p to wrap rpc data\n", + task->tk_pid, cred->cr_auth->au_ops->au_name, cred); + if (cred->cr_ops->crwrap_req) + return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); + /* By default, we encode the arguments normally. */ + return encode(rqstp, data, obj); +} + +int +rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, + u32 *data, void *obj) +{ + struct rpc_cred *cred = task->tk_msg.rpc_cred; + + dprintk("RPC: %4d using %s cred %p to unwrap rpc data\n", + task->tk_pid, cred->cr_auth->au_ops->au_name, cred); + if (cred->cr_ops->crunwrap_resp) + return cred->cr_ops->crunwrap_resp(task, decode, rqstp, + data, obj); + /* By default, we decode the arguments normally. */ + return decode(rqstp, data, obj); +} + int rpcauth_refreshcred(struct rpc_task *task) { diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 61618c6bed5e..1a4b9f504aa2 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -51,6 +51,7 @@ #include #include #include +#include #include static struct rpc_authops authgss_ops; @@ -65,7 +66,9 @@ static struct rpc_credops gss_credops; #define GSS_CRED_EXPIRE (60 * HZ) /* XXX: reasonable? */ #define GSS_CRED_SLACK 1024 /* XXX: unused */ -#define GSS_VERF_SLACK 48 /* length of a krb5 verifier.*/ +/* length of a krb5 verifier (48), plus data added before arguments when + * using integrity (two 4-byte integers): */ +#define GSS_VERF_SLACK 56 /* XXX this define must match the gssd define * as it is passed to gssd to signal the use of @@ -669,21 +672,14 @@ gss_marshal(struct rpc_task *task, u32 *p, int ruid) struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); u32 *cred_len; struct rpc_rqst *req = task->tk_rqstp; - struct rpc_clnt *clnt = task->tk_client; - struct rpc_xprt *xprt = clnt->cl_xprt; - u32 *verfbase = req->rq_svec[0].iov_base; u32 maj_stat = 0; - struct xdr_netobj bufin,bufout; + struct xdr_netobj mic; + struct iovec iov; + struct xdr_buf verf_buf; u32 service; dprintk("RPC: gss_marshal\n"); - /* We compute the checksum for the verifier over the xdr-encoded bytes - * starting with the xid (which verfbase points to) and ending at - * the end of the credential. */ - if (xprt->stream) - verfbase++; /* See clnt.c:call_header() */ - *p++ = htonl(RPC_AUTH_GSS); cred_len = p++; @@ -704,24 +700,28 @@ gss_marshal(struct rpc_task *task, u32 *p, int ruid) p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); *cred_len = htonl((p - (cred_len + 1)) << 2); - /* Marshal verifier. */ - bufin.data = (u8 *)verfbase; - bufin.len = (p - verfbase) << 2; + /* We compute the checksum for the verifier over the xdr-encoded bytes + * starting with the xid and ending at the end of the credential: */ + iov.iov_base = req->rq_snd_buf.head[0].iov_base; + if (task->tk_client->cl_xprt->stream) + /* See clnt.c:call_header() */ + iov.iov_base += 4; + iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; + xdr_buf_from_iov(&iov, &verf_buf); /* set verifier flavor*/ *p++ = htonl(RPC_AUTH_GSS); - bufout.data = (u8 *)(p + 1); + mic.data = (u8 *)(p + 1); maj_stat = gss_get_mic(ctx->gc_gss_ctx, GSS_C_QOP_DEFAULT, - &bufin, &bufout); + &verf_buf, &mic); if(maj_stat != 0){ - printk("gss_marshal: gss_get_mic FAILED (%d)\n", - maj_stat); + printk("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat); goto out_put_ctx; } - *p++ = htonl(bufout.len); - p += XDR_QUADLEN(bufout.len); + *p++ = htonl(mic.len); + p += XDR_QUADLEN(mic.len); gss_put_ctx(ctx); return p; out_put_ctx: @@ -749,35 +749,45 @@ static u32 * gss_validate(struct rpc_task *task, u32 *p) { struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, + gc_base); struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); u32 seq, qop_state; - struct xdr_netobj bufin; - struct xdr_netobj bufout; + struct iovec iov; + struct xdr_buf verf_buf; + struct xdr_netobj mic; u32 flav,len; + u32 service; dprintk("RPC: gss_validate\n"); flav = ntohl(*p++); - if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE) { - printk("RPC: giant verf size: %ld\n", (unsigned long) len); + if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE) goto out_bad; - } - dprintk("RPC: gss_validate: verifier flavor %d, len %d\n", flav, len); - - if (flav != RPC_AUTH_GSS) { - printk("RPC: bad verf flavor: %ld\n", (unsigned long)flav); + if (flav != RPC_AUTH_GSS) goto out_bad; - } seq = htonl(task->tk_gss_seqno); - bufin.data = (u8 *) &seq; - bufin.len = sizeof(seq); - bufout.data = (u8 *) p; - bufout.len = len; - - if (gss_verify_mic(ctx->gc_gss_ctx, &bufin, &bufout, &qop_state) != 0) - goto out_bad; - task->tk_auth->au_rslack = XDR_QUADLEN(len) + 2; - dprintk("RPC: GSS gss_validate: gss_verify_mic succeeded.\n"); + iov.iov_base = &seq; + iov.iov_len = sizeof(seq); + xdr_buf_from_iov(&iov, &verf_buf); + mic.data = (u8 *)p; + mic.len = len; + + if (gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic, &qop_state)) + goto out_bad; + service = gss_pseudoflavor_to_service(gss_cred->gc_flavor); + switch (service) { + case RPC_GSS_SVC_NONE: + /* verifier data, flavor, length: */ + task->tk_auth->au_rslack = XDR_QUADLEN(len) + 2; + break; + case RPC_GSS_SVC_INTEGRITY: + /* verifier data, flavor, length, length, sequence number: */ + task->tk_auth->au_rslack = XDR_QUADLEN(len) + 4; + break; + default: + goto out_bad; + } gss_put_ctx(ctx); return p + XDR_QUADLEN(len); out_bad: @@ -785,6 +795,147 @@ out_bad: return NULL; } +static int +gss_wrap_req(struct rpc_task *task, + kxdrproc_t encode, void *rqstp, u32 *p, void *obj) +{ + struct rpc_rqst *req = (struct rpc_rqst *)rqstp; + struct xdr_buf *snd_buf = &req->rq_snd_buf; + struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, + gc_base); + struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); + u32 *integ_len = NULL; + int status = -EIO; + u32 maj_stat = 0; + struct xdr_buf integ_buf; + struct xdr_netobj mic; + u32 service; + u32 offset, *q; + struct iovec *iov; + + dprintk("RPC: gss_wrap_body\n"); + BUG_ON(!ctx); + if (ctx->gc_proc != RPC_GSS_PROC_DATA) { + /* The spec seems a little ambiguous here, but I think that not + * wrapping context destruction requests makes the most sense. + */ + status = encode(rqstp, p, obj); + goto out; + } + service = gss_pseudoflavor_to_service(gss_cred->gc_flavor); + switch (service) { + case RPC_GSS_SVC_NONE: + status = encode(rqstp, p, obj); + goto out; + case RPC_GSS_SVC_INTEGRITY: + + integ_len = p++; + offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; + *p++ = htonl(task->tk_gss_seqno); + + status = encode(rqstp, p, obj); + if (status) + goto out; + + if (xdr_buf_subsegment(snd_buf, &integ_buf, + offset, snd_buf->len - offset)) + goto out; + *integ_len = htonl(integ_buf.len); + + /* guess whether we're in the head or the tail: */ + if (snd_buf->page_len || snd_buf->tail[0].iov_len) + iov = snd_buf->tail; + else + iov = snd_buf->head; + p = iov->iov_base + iov->iov_len; + mic.data = (u8 *)(p + 1); + + maj_stat = gss_get_mic(ctx->gc_gss_ctx, + GSS_C_QOP_DEFAULT, &integ_buf, &mic); + status = -EIO; /* XXX? */ + if (maj_stat) + goto out; + q = p; + *q++ = htonl(mic.len); + q += XDR_QUADLEN(mic.len); + + offset = (u8 *)q - (u8 *)p; + iov->iov_len += offset; + snd_buf->len += offset; + break; + case RPC_GSS_SVC_PRIVACY: + default: + goto out; + } + status = 0; +out: + gss_put_ctx(ctx); + dprintk("RPC: gss_wrap_req returning %d\n", status); + return status; +} + +static int +gss_unwrap_resp(struct rpc_task *task, + kxdrproc_t decode, void *rqstp, u32 *p, void *obj) +{ + struct rpc_rqst *req = (struct rpc_rqst *)rqstp; + struct xdr_buf *rcv_buf = &req->rq_rcv_buf; + struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, + gc_base); + struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); + struct xdr_buf integ_buf; + struct xdr_netobj mic; + int status = -EIO; + u32 maj_stat = 0; + u32 service; + u32 data_offset, mic_offset; + u32 integ_len; + + BUG_ON(!ctx); + + if (ctx->gc_proc != RPC_GSS_PROC_DATA) + goto out_decode; + service = gss_pseudoflavor_to_service(gss_cred->gc_flavor); + switch (service) { + case RPC_GSS_SVC_NONE: + goto out_decode; + case RPC_GSS_SVC_INTEGRITY: + integ_len = ntohl(*p++); + if (integ_len & 3) + goto out; + data_offset = (u8 *)p - (u8 *)rcv_buf->head[0].iov_base; + mic_offset = integ_len + data_offset; + if (mic_offset > rcv_buf->len) + goto out; + if (ntohl(*p++) != task->tk_gss_seqno) + goto out; + + if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, + mic_offset - data_offset)) + goto out; + + if (xdr_buf_read_netobj(rcv_buf, &mic, mic_offset)) + goto out; + + maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, + &mic, NULL); + if (maj_stat != GSS_S_COMPLETE) + goto out; + break; + case RPC_GSS_SVC_PRIVACY: + default: + goto out; + } +out_decode: + status = decode(rqstp, p, obj); +out: + gss_put_ctx(ctx); + dprintk("RPC: gss_unwrap_resp returning %d\n", status); + return status; +} + static struct rpc_authops authgss_ops = { .owner = THIS_MODULE, .au_flavor = RPC_AUTH_GSS, @@ -802,6 +953,8 @@ static struct rpc_credops gss_credops = { .crmarshal = gss_marshal, .crrefresh = gss_refresh, .crvalidate = gss_validate, + .crwrap_req = gss_wrap_req, + .crunwrap_resp = gss_unwrap_resp, }; static struct rpc_pipe_ops gss_upcall_ops = { diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index 9894d83ddf6f..5a5f859ad628 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #ifdef RPC_DEBUG @@ -57,7 +58,7 @@ krb5_encrypt( struct scatterlist sg[1]; u8 local_iv[16] = {0}; - dprintk("RPC: gss_k5encrypt: TOP in %p out %p\nin data:\n", out, in); + dprintk("RPC: krb5_encrypt: input data:\n"); print_hexl((u32 *)in, length, 0); if (length % crypto_tfm_alg_blocksize(tfm) != 0) @@ -79,8 +80,10 @@ krb5_encrypt( ret = crypto_cipher_encrypt_iv(tfm, sg, sg, length, local_iv); + dprintk("RPC: krb5_encrypt: output data:\n"); + print_hexl((u32 *)out, length, 0); out: - dprintk("gss_k5encrypt returns %d\n",ret); + dprintk("krb5_encrypt returns %d\n",ret); return(ret); } @@ -96,8 +99,8 @@ krb5_decrypt( struct scatterlist sg[1]; u8 local_iv[16] = {0}; - dprintk("RPC: gss_k5decrypt: TOP in %p out %p\nin data:\n", in, out); - print_hexl((u32 *)in,length,0); + dprintk("RPC: krb5_decrypt: input data:\n"); + print_hexl((u32 *)in, length, 0); if (length % crypto_tfm_alg_blocksize(tfm) != 0) goto out; @@ -117,6 +120,8 @@ krb5_decrypt( ret = crypto_cipher_decrypt_iv(tfm, sg, sg, length, local_iv); + dprintk("RPC: krb5_decrypt: output_data:\n"); + print_hexl((u32 *)out, length, 0); out: dprintk("gss_k5decrypt returns %d\n",ret); return(ret); @@ -132,13 +137,15 @@ buf_to_sg(struct scatterlist *sg, char *ptr, int len) { /* checksum the plaintext data and the first 8 bytes of the krb5 token header, * as specified by the rfc: */ s32 -krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, +krb5_make_checksum(s32 cksumtype, char *header, struct xdr_buf *body, struct xdr_netobj *cksum) { char *cksumname; struct crypto_tfm *tfm = NULL; /* XXX add to ctx? */ - struct scatterlist sg[2]; + struct scatterlist sg[1]; u32 code = GSS_S_FAILURE; + int len, thislen, offset; + int i; switch (cksumtype) { case CKSUMTYPE_RSA_MD5: @@ -155,10 +162,36 @@ krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, if ((cksum->data = kmalloc(cksum->len, GFP_KERNEL)) == NULL) goto out; - buf_to_sg(&sg[0], header, 8); - buf_to_sg(&sg[1], body, body_len); crypto_digest_init(tfm); - crypto_digest_update(tfm, sg, 2); + buf_to_sg(sg, header, 8); + crypto_digest_update(tfm, sg, 1); + if (body->head[0].iov_len) { + buf_to_sg(sg, body->head[0].iov_base, body->head[0].iov_len); + crypto_digest_update(tfm, sg, 1); + } + + len = body->page_len; + offset = body->page_base; + i = 0; + while (len) { + sg->page = body->pages[i]; + sg->offset = offset; + offset = 0; + if (PAGE_SIZE > len) + thislen = len; + else + thislen = PAGE_SIZE; + sg->length = thislen; + kmap(sg->page); /* XXX kmap_atomic? */ + crypto_digest_update(tfm, sg, 1); + kunmap(sg->page); + len -= thislen; + i++; + } + if (body->tail[0].iov_len) { + buf_to_sg(sg, body->tail[0].iov_base, body->tail[0].iov_len); + crypto_digest_update(tfm, sg, 1); + } crypto_digest_final(tfm, cksum->data); code = 0; out: diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 61282b4d9c3b..9913dac0f415 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -183,7 +183,7 @@ gss_delete_sec_context_kerberos(void *internal_ctx) { static u32 gss_verify_mic_kerberos(struct gss_ctx *ctx, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate) { u32 maj_stat = 0; @@ -202,13 +202,11 @@ gss_verify_mic_kerberos(struct gss_ctx *ctx, static u32 gss_get_mic_kerberos(struct gss_ctx *ctx, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token) { u32 err = 0; struct krb5_ctx *kctx = ctx->internal_ctx_id; - if (!message->data) return GSS_S_FAILURE; - err = krb5_make_token(kctx, qop, message, mic_token, KG_TOK_MIC_MSG); dprintk("RPC: gss_get_mic_kerberos returning %d\n",err); @@ -233,12 +231,14 @@ static int __init init_kerberos_module(void) printk("Failed to register kerberos gss mechanism!\n"); gm = gss_mech_get_by_OID(&gss_mech_krb5_oid); gss_register_triple(RPC_AUTH_GSS_KRB5 , gm, 0, RPC_GSS_SVC_NONE); + gss_register_triple(RPC_AUTH_GSS_KRB5I, gm, 0, RPC_GSS_SVC_INTEGRITY); gss_mech_put(gm); return 0; } static void __exit cleanup_kerberos_module(void) { + gss_unregister_triple(RPC_AUTH_GSS_KRB5I); gss_unregister_triple(RPC_AUTH_GSS_KRB5); } diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index eaf19d7c8e25..52d4e78e117f 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c @@ -80,7 +80,7 @@ gss_krb5_padding(int blocksize, int length) { u32 krb5_make_token(struct krb5_ctx *ctx, int qop_req, - struct xdr_netobj * text, struct xdr_netobj * token, + struct xdr_buf *text, struct xdr_netobj *token, int toktype) { s32 checksum_type; @@ -134,24 +134,11 @@ krb5_make_token(struct krb5_ctx *ctx, int qop_req, *(u16 *)(krb5_hdr + 4) = htons(ctx->sealalg); if (toktype == KG_TOK_WRAP_MSG) { - unsigned char pad = gss_krb5_padding(blocksize, text->len); - - get_random_bytes(msg_start, blocksize); /* "confounder" */ - memcpy(msg_start + blocksize, text->data, text->len); - - memset(msg_start + blocksize + text->len, pad, pad); - - if (krb5_make_checksum(checksum_type, krb5_hdr, msg_start, - tmsglen, &md5cksum)) - goto out_err; - - if (krb5_encrypt(ctx->enc, NULL, msg_start, msg_start, - tmsglen)) - goto out_err; - + /* XXX removing support for now */ + goto out_err; } else { /* Sign only. */ - if (krb5_make_checksum(checksum_type, krb5_hdr, text->data, - text->len, &md5cksum)) + if (krb5_make_checksum(checksum_type, krb5_hdr, text, + &md5cksum)) goto out_err; } diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c index 8b2795d701db..0e1c7f70f841 100644 --- a/net/sunrpc/auth_gss/gss_krb5_unseal.c +++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c @@ -75,20 +75,19 @@ * to return the decrypted data. */ +/* XXX will need to change prototype and/or just split into a separate function + * when we add privacy (because read_token will be in pages too). */ u32 krb5_read_token(struct krb5_ctx *ctx, struct xdr_netobj *read_token, - struct xdr_netobj *message_buffer, + struct xdr_buf *message_buffer, int *qop_state, int toktype) { int signalg; int sealalg; - struct xdr_netobj token = {.len = 0, .data = NULL}; s32 checksum_type; struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; s32 now; - unsigned char *plain = NULL; - int plainlen = 0; int direction; s32 seqnum; unsigned char *ptr = (unsigned char *)read_token->data; @@ -100,10 +99,11 @@ krb5_read_token(struct krb5_ctx *ctx, if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr, toktype, read_token->len)) goto out; + /* XXX sanity-check bodysize?? */ if (toktype == KG_TOK_WRAP_MSG) { - message_buffer->len = 0; - message_buffer->data = NULL; + /* XXX gone */ + goto out; } /* get the sign and seal algorithms */ @@ -135,43 +135,6 @@ krb5_read_token(struct krb5_ctx *ctx, signalg != SGN_ALG_HMAC_SHA1_DES3_KD)) goto out; - if (toktype == KG_TOK_WRAP_MSG) { - int conflen = crypto_tfm_alg_blocksize(ctx->enc); - int padlen; - - plainlen = bodysize - (14 + KRB5_CKSUM_LENGTH); - plain = ptr + 14 + KRB5_CKSUM_LENGTH; - - ret = krb5_decrypt(ctx->enc, NULL, plain, plain, plainlen); - if (ret) - goto out; - - ret = GSS_S_FAILURE; - padlen = plain[plainlen -1]; - if ((padlen < 1) || (padlen > 8)) - goto out; - token.len = plainlen - conflen - padlen; - - if (token.len) { - token.data = kmalloc(token.len, GFP_KERNEL); - if (token.data == NULL) - goto out; - memcpy(token.data, plain + conflen, token.len); - } - - } else if (toktype == KG_TOK_MIC_MSG) { - token = *message_buffer; - plain = token.data; - plainlen = token.len; - } else { - printk("RPC: bad toktype in krb5_read_token"); - ret = GSS_S_FAILURE; - goto out; - } - - dprintk("RPC krb5_read_token: token.len %d plainlen %d\n", token.len, - plainlen); - /* compute the checksum of the message */ /* initialize the the cksum */ @@ -186,8 +149,8 @@ krb5_read_token(struct krb5_ctx *ctx, switch (signalg) { case SGN_ALG_DES_MAC_MD5: - ret = krb5_make_checksum(checksum_type, ptr - 2, plain, - plainlen, &md5cksum); + ret = krb5_make_checksum(checksum_type, ptr - 2, + message_buffer, &md5cksum); if (ret) goto out; @@ -208,9 +171,6 @@ krb5_read_token(struct krb5_ctx *ctx, /* it got through unscathed. Make sure the context is unexpired */ - if (toktype == KG_TOK_WRAP_MSG) - *message_buffer = token; - if (qop_state) *qop_state = GSS_C_QOP_DEFAULT; @@ -234,7 +194,5 @@ krb5_read_token(struct krb5_ctx *ctx, ret = GSS_S_COMPLETE; out: if (md5cksum.data) kfree(md5cksum.data); - if ((toktype == KG_TOK_WRAP_MSG) && ret && token.data) - kfree(token.data); return ret; } diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c index b384cae37052..b360460defcd 100644 --- a/net/sunrpc/auth_gss/gss_mech_switch.c +++ b/net/sunrpc/auth_gss/gss_mech_switch.c @@ -196,7 +196,7 @@ gss_import_sec_context(struct xdr_netobj *input_token, u32 gss_get_mic(struct gss_ctx *context_handle, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token) { return context_handle->mech_type->gm_ops @@ -210,7 +210,7 @@ gss_get_mic(struct gss_ctx *context_handle, u32 gss_verify_mic(struct gss_ctx *context_handle, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate) { diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index badaa121f29c..cc4bfb201807 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -568,7 +568,8 @@ call_encode(struct rpc_task *task) rpc_exit(task, -EIO); return; } - if (encode && (status = encode(req, p, task->tk_msg.rpc_argp)) < 0) { + if (encode && (status = rpcauth_wrap_req(task, encode, req, p, + task->tk_msg.rpc_argp)) < 0) { printk(KERN_WARNING "%s: can't encode arguments: %d\n", clnt->cl_protname, -status); rpc_exit(task, status); @@ -827,7 +828,8 @@ call_decode(struct rpc_task *task) task->tk_action = NULL; if (decode) - task->tk_status = decode(req, p, task->tk_msg.rpc_resp); + task->tk_status = rpcauth_unwrap_resp(task, decode, req, p, + task->tk_msg.rpc_resp); dprintk("RPC: %4d call_decode result %d\n", task->tk_pid, task->tk_status); return; diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index ff8d2bb7bb18..f6bde71024e5 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -126,6 +126,9 @@ EXPORT_SYMBOL(xdr_inline_pages); EXPORT_SYMBOL(xdr_shift_buf); EXPORT_SYMBOL(xdr_write_pages); EXPORT_SYMBOL(xdr_read_pages); +EXPORT_SYMBOL(xdr_buf_from_iov); +EXPORT_SYMBOL(xdr_buf_subsegment); +EXPORT_SYMBOL(xdr_buf_read_netobj); /* Debugging symbols */ #ifdef RPC_DEBUG diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 078dad8a90e5..00e0082704ba 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -538,7 +538,7 @@ _copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len) * Copies data into an arbitrary memory location from an array of pages * The copy is assumed to be non-overlapping. */ -static void +void _copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len) { struct page **pgfrom; @@ -731,3 +731,145 @@ xdr_read_pages(struct xdr_stream *xdr, unsigned int len) xdr->p = (uint32_t *)((char *)iov->iov_base + padding); xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len); } + +static struct iovec empty_iov = {.iov_base = NULL, .iov_len = 0}; + +void +xdr_buf_from_iov(struct iovec *iov, struct xdr_buf *buf) +{ + buf->head[0] = *iov; + buf->tail[0] = empty_iov; + buf->page_len = 0; + buf->len = iov->iov_len; +} + +/* Sets subiov to the intersection of iov with the buffer of length len + * starting base bytes after iov. Indicates empty intersection by setting + * length of subiov to zero. Decrements len by length of subiov, sets base + * to zero (or decrements it by length of iov if subiov is empty). */ +static void +iov_subsegment(struct iovec *iov, struct iovec *subiov, int *base, int *len) +{ + if (*base > iov->iov_len) { + subiov->iov_base = NULL; + subiov->iov_len = 0; + *base -= iov->iov_len; + } else { + subiov->iov_base = iov->iov_base + *base; + subiov->iov_len = min(*len, (int)iov->iov_len - *base); + *base = 0; + } + *len -= subiov->iov_len; +} + +/* Sets subbuf to the portion of buf of length len beginning base bytes + * from the start of buf. Returns -1 if base of length are out of bounds. */ +int +xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, + int base, int len) +{ + int i; + + subbuf->len = len; + iov_subsegment(buf->head, subbuf->head, &base, &len); + + if (base < buf->page_len) { + i = (base + buf->page_base) >> PAGE_CACHE_SHIFT; + subbuf->pages = &buf->pages[i]; + subbuf->page_base = (base + buf->page_base) & ~PAGE_CACHE_MASK; + subbuf->page_len = min((int)buf->page_len - base, len); + len -= subbuf->page_len; + base = 0; + } else { + base -= buf->page_len; + subbuf->page_len = 0; + } + + iov_subsegment(buf->tail, subbuf->tail, &base, &len); + if (base || len) + return -1; + return 0; +} + +/* obj is assumed to point to allocated memory of size at least len: */ +static int +read_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len) +{ + struct xdr_buf subbuf; + int this_len; + int status; + + status = xdr_buf_subsegment(buf, &subbuf, base, len); + if (status) + goto out; + this_len = min(len, (int)subbuf.head[0].iov_len); + memcpy(obj, subbuf.head[0].iov_base, this_len); + len -= this_len; + obj += this_len; + this_len = min(len, (int)subbuf.page_len); + if (this_len) + _copy_from_pages(obj, subbuf.pages, subbuf.page_base, this_len); + len -= this_len; + obj += this_len; + this_len = min(len, (int)subbuf.tail[0].iov_len); + memcpy(obj, subbuf.tail[0].iov_base, this_len); +out: + return status; +} + +static int +read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) +{ + u32 raw; + int status; + + status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); + if (status) + return status; + *obj = ntohl(raw); + return 0; +} + +/* If the netobj starting offset bytes from the start of xdr_buf is contained + * entirely in the head or the tail, set object to point to it; otherwise + * try to find space for it at the end of the tail, copy it there, and + * set obj to point to it. */ +int +xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset) +{ + u32 tail_offset = buf->head[0].iov_len + buf->page_len; + u32 obj_end_offset; + + if (read_u32_from_xdr_buf(buf, offset, &obj->len)) + goto out; + obj_end_offset = offset + 4 + obj->len; + + if (obj_end_offset <= buf->head[0].iov_len) { + /* The obj is contained entirely in the head: */ + obj->data = buf->head[0].iov_base + offset + 4; + } else if (offset + 4 >= tail_offset) { + if (obj_end_offset - tail_offset + > buf->tail[0].iov_len) + goto out; + /* The obj is contained entirely in the tail: */ + obj->data = buf->tail[0].iov_base + + offset - tail_offset + 4; + } else { + /* use end of tail as storage for obj: + * (We don't copy to the beginning because then we'd have + * to worry about doing a potentially overlapping copy. + * This assumes the object is at most half the length of the + * tail.) */ + if (obj->len > buf->tail[0].iov_len) + goto out; + obj->data = buf->tail[0].iov_base + buf->tail[0].iov_len - + obj->len; + if (read_bytes_from_xdr_buf(buf, offset + 4, + obj->data, obj->len)) + goto out; + + } + return 0; +out: + return -1; +} -- cgit v1.2.3 From cb21a7182f9d73ad544f33f5cdec6d2be9a8cd4f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:45:27 +0100 Subject: RPCSEC_GSS: Move the gss sequence number history from the task structure to the request structure, where it makes more sense. In particular, when we start storing more sequence number history (necessary to process responses to resent requests correctly), this will make it easier to initialize the necessary data structure in the right place (in xprt_request_init). --- include/linux/sunrpc/sched.h | 2 -- include/linux/sunrpc/xprt.h | 1 + net/sunrpc/auth_gss/auth_gss.c | 10 +++++----- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 5e2d23e0ce6c..1113d7f3df13 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -48,8 +48,6 @@ struct rpc_task { __u8 tk_garb_retry, tk_cred_retry, tk_suid_retry; - u32 tk_gss_seqno; /* rpcsec_gss sequence number - used on this request */ /* * timeout_fn to be executed by timer bottom half diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index e29381edeaea..8472b1c5ad2e 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -95,6 +95,7 @@ struct rpc_rqst { struct rpc_rqst * rq_next; /* free list */ int rq_cong; /* has incremented xprt->cong */ int rq_received; /* receive completed */ + u32 rq_seqno; /* gss seq no. used on req. */ struct list_head rq_list; diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 1a4b9f504aa2..ef57f1942ea4 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -690,12 +690,12 @@ gss_marshal(struct rpc_task *task, u32 *p, int ruid) goto out_put_ctx; } spin_lock(&ctx->gc_seq_lock); - task->tk_gss_seqno = ctx->gc_seq++; + req->rq_seqno = ctx->gc_seq++; spin_unlock(&ctx->gc_seq_lock); *p++ = htonl((u32) RPC_GSS_VERSION); *p++ = htonl((u32) ctx->gc_proc); - *p++ = htonl((u32) task->tk_gss_seqno); + *p++ = htonl((u32) req->rq_seqno); *p++ = htonl((u32) service); p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); *cred_len = htonl((p - (cred_len + 1)) << 2); @@ -766,7 +766,7 @@ gss_validate(struct rpc_task *task, u32 *p) goto out_bad; if (flav != RPC_AUTH_GSS) goto out_bad; - seq = htonl(task->tk_gss_seqno); + seq = htonl(task->tk_rqstp->rq_seqno); iov.iov_base = &seq; iov.iov_len = sizeof(seq); xdr_buf_from_iov(&iov, &verf_buf); @@ -832,7 +832,7 @@ gss_wrap_req(struct rpc_task *task, integ_len = p++; offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; - *p++ = htonl(task->tk_gss_seqno); + *p++ = htonl(req->rq_seqno); status = encode(rqstp, p, obj); if (status) @@ -909,7 +909,7 @@ gss_unwrap_resp(struct rpc_task *task, mic_offset = integ_len + data_offset; if (mic_offset > rcv_buf->len) goto out; - if (ntohl(*p++) != task->tk_gss_seqno) + if (ntohl(*p++) != req->rq_seqno) goto out; if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, -- cgit v1.2.3 From 8d51075e1a0b96f9b787df3e851fec5c9bb2ba4a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:49:04 +0100 Subject: RPC: Add support for sharing the same RPC transport and credential caches between different mountpoints by allowing cloning of the rpc_client struct. --- include/linux/sunrpc/auth.h | 1 + include/linux/sunrpc/clnt.h | 23 ++++++++++------- net/sunrpc/auth.c | 13 ++++++++-- net/sunrpc/clnt.c | 61 ++++++++++++++++++++++++++++++++++++++++++--- net/sunrpc/pmap_clnt.c | 17 +++++++------ net/sunrpc/sunrpc_syms.c | 2 ++ net/sunrpc/xprt.c | 8 +++--- 7 files changed, 98 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index a68f18bf0a46..1f83e0f5b9d3 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -73,6 +73,7 @@ struct rpc_auth { * differ from the flavor in * au_ops->au_flavor in gss * case) */ + atomic_t au_count; /* Reference counter */ /* per-flavor data */ }; diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 99d57fec03a9..917ec29d789b 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -26,6 +26,8 @@ struct rpc_portmap { __u32 pm_vers; __u32 pm_prot; __u16 pm_port; + unsigned char pm_binding : 1; /* doing a getport() */ + struct rpc_wait_queue pm_bindwait; /* waiting on getport() */ }; struct rpc_inode; @@ -34,6 +36,7 @@ struct rpc_inode; * The high-level client handle */ struct rpc_clnt { + atomic_t cl_count; /* Number of clones */ atomic_t cl_users; /* number of references */ struct rpc_xprt * cl_xprt; /* transport */ struct rpc_procinfo * cl_procinfo; /* procedure info */ @@ -48,26 +51,27 @@ struct rpc_clnt { cl_intr : 1,/* interruptible */ cl_chatty : 1,/* be verbose */ cl_autobind : 1,/* use getport() */ - cl_binding : 1,/* doing a getport() */ cl_droppriv : 1,/* enable NFS suid hack */ cl_oneshot : 1,/* dispose after use */ cl_dead : 1;/* abandoned */ - struct rpc_rtt cl_rtt; /* RTO estimator data */ - - struct rpc_portmap cl_pmap; /* port mapping */ - struct rpc_wait_queue cl_bindwait; /* waiting on getport() */ + struct rpc_rtt * cl_rtt; /* RTO estimator data */ + struct rpc_portmap * cl_pmap; /* port mapping */ int cl_nodelen; /* nodename length */ char cl_nodename[UNX_MAXNODENAME]; char cl_pathname[30];/* Path in rpc_pipe_fs */ struct dentry * cl_dentry; /* inode */ + struct rpc_clnt * cl_parent; /* Points to parent of clones */ + struct rpc_rtt cl_rtt_default; + struct rpc_portmap cl_pmap_default; + char cl_inline_name[32]; }; #define cl_timeout cl_xprt->timeout -#define cl_prog cl_pmap.pm_prog -#define cl_vers cl_pmap.pm_vers -#define cl_port cl_pmap.pm_port -#define cl_prot cl_pmap.pm_prot +#define cl_prog cl_pmap->pm_prog +#define cl_vers cl_pmap->pm_vers +#define cl_port cl_pmap->pm_port +#define cl_prot cl_pmap->pm_prot /* * General RPC program info @@ -108,6 +112,7 @@ struct rpc_procinfo { struct rpc_clnt *rpc_create_client(struct rpc_xprt *xprt, char *servname, struct rpc_program *info, u32 version, rpc_authflavor_t authflavor); +struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); int rpc_shutdown_client(struct rpc_clnt *); int rpc_destroy_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *); diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index a89b30cadff3..35900eb52b61 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -61,6 +61,7 @@ rpcauth_unregister(struct rpc_authops *ops) struct rpc_auth * rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) { + struct rpc_auth *auth; struct rpc_authops *ops; u32 flavor = pseudoflavor_to_flavor(pseudoflavor); @@ -68,13 +69,21 @@ rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) return NULL; if (!try_module_get(ops->owner)) return NULL; - clnt->cl_auth = ops->create(clnt, pseudoflavor); - return clnt->cl_auth; + auth = ops->create(clnt, pseudoflavor); + if (!auth) + return NULL; + atomic_set(&auth->au_count, 1); + if (clnt->cl_auth) + rpcauth_destroy(clnt->cl_auth); + clnt->cl_auth = auth; + return auth; } void rpcauth_destroy(struct rpc_auth *auth) { + if (!atomic_dec_and_test(&auth->au_count)) + return; auth->au_ops->destroy(auth); module_put(auth->au_ops->owner); kfree(auth); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index cc4bfb201807..6c6a8310000a 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -102,6 +102,7 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, { struct rpc_version *version; struct rpc_clnt *clnt = NULL; + int len; dprintk("RPC: creating %s client for %s (xprt %p)\n", program->name, servname, xprt); @@ -116,23 +117,37 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname, goto out_no_clnt; memset(clnt, 0, sizeof(*clnt)); atomic_set(&clnt->cl_users, 0); + atomic_set(&clnt->cl_count, 1); + clnt->cl_parent = clnt; + + clnt->cl_server = clnt->cl_inline_name; + len = strlen(servname) + 1; + if (len > sizeof(clnt->cl_inline_name)) { + char *buf = kmalloc(len, GFP_KERNEL); + if (buf != 0) + clnt->cl_server = buf; + else + len = sizeof(clnt->cl_inline_name); + } + strlcpy(clnt->cl_server, servname, len); clnt->cl_xprt = xprt; clnt->cl_procinfo = version->procs; clnt->cl_maxproc = version->nrprocs; - clnt->cl_server = servname; clnt->cl_protname = program->name; + clnt->cl_pmap = &clnt->cl_pmap_default; clnt->cl_port = xprt->addr.sin_port; clnt->cl_prog = program->number; clnt->cl_vers = version->number; clnt->cl_prot = xprt->prot; clnt->cl_stats = program->stats; - INIT_RPC_WAITQ(&clnt->cl_bindwait, "bindwait"); + INIT_RPC_WAITQ(&clnt->cl_pmap_default.pm_bindwait, "bindwait"); if (!clnt->cl_port) clnt->cl_autobind = 1; - rpc_init_rtt(&clnt->cl_rtt, xprt->timeout.to_initval); + clnt->cl_rtt = &clnt->cl_rtt_default; + rpc_init_rtt(&clnt->cl_rtt_default, xprt->timeout.to_initval); if (rpc_setup_pipedir(clnt, program->pipe_dir_name) < 0) goto out_no_path; @@ -157,11 +172,39 @@ out_no_clnt: out_no_auth: rpc_rmdir(clnt->cl_pathname); out_no_path: + if (clnt->cl_server != clnt->cl_inline_name) + kfree(clnt->cl_server); kfree(clnt); clnt = NULL; goto out; } +/* + * This function clones the RPC client structure. It allows us to share the + * same transport while varying parameters such as the authentication + * flavour. + */ +struct rpc_clnt * +rpc_clone_client(struct rpc_clnt *clnt) +{ + struct rpc_clnt *new; + + new = (struct rpc_clnt *)kmalloc(sizeof(*new), GFP_KERNEL); + if (!new) + goto out_no_clnt; + memcpy(new, clnt, sizeof(*new)); + atomic_set(&new->cl_count, 1); + atomic_set(&new->cl_users, 0); + atomic_inc(&new->cl_parent->cl_count); + if (new->cl_auth) + atomic_inc(&new->cl_auth->au_count); +out: + return new; +out_no_clnt: + printk(KERN_INFO "RPC: out of memory in %s\n", __FUNCTION__); + goto out; +} + /* * Properly shut down an RPC client, terminating all outstanding * requests. Note that we must be certain that cl_oneshot and @@ -201,19 +244,29 @@ rpc_shutdown_client(struct rpc_clnt *clnt) int rpc_destroy_client(struct rpc_clnt *clnt) { + if (!atomic_dec_and_test(&clnt->cl_count)) + return 1; + BUG_ON(atomic_read(&clnt->cl_users) != 0); + dprintk("RPC: destroying %s client for %s\n", clnt->cl_protname, clnt->cl_server); - if (clnt->cl_auth) { rpcauth_destroy(clnt->cl_auth); clnt->cl_auth = NULL; } + if (clnt->cl_parent != clnt) { + rpc_destroy_client(clnt->cl_parent); + goto out_free; + } if (clnt->cl_pathname[0]) rpc_rmdir(clnt->cl_pathname); if (clnt->cl_xprt) { xprt_destroy(clnt->cl_xprt); clnt->cl_xprt = NULL; } + if (clnt->cl_server != clnt->cl_inline_name) + kfree(clnt->cl_server); +out_free: kfree(clnt); return 0; } diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c index ed627ea885e8..4b619e002123 100644 --- a/net/sunrpc/pmap_clnt.c +++ b/net/sunrpc/pmap_clnt.c @@ -41,7 +41,7 @@ static spinlock_t pmap_lock = SPIN_LOCK_UNLOCKED; void rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) { - struct rpc_portmap *map = &clnt->cl_pmap; + struct rpc_portmap *map = clnt->cl_pmap; struct sockaddr_in *sap = &clnt->cl_xprt->addr; struct rpc_message msg = { .rpc_proc = &pmap_procedures[PMAP_GETPORT], @@ -57,12 +57,12 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) map->pm_prog, map->pm_vers, map->pm_prot); spin_lock(&pmap_lock); - if (clnt->cl_binding) { - rpc_sleep_on(&clnt->cl_bindwait, task, NULL, 0); + if (map->pm_binding) { + rpc_sleep_on(&map->pm_bindwait, task, NULL, 0); spin_unlock(&pmap_lock); return; } - clnt->cl_binding = 1; + map->pm_binding = 1; spin_unlock(&pmap_lock); task->tk_status = -EACCES; /* why set this? returns -EIO below */ @@ -85,8 +85,8 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) bailout: spin_lock(&pmap_lock); - clnt->cl_binding = 0; - rpc_wake_up(&clnt->cl_bindwait); + map->pm_binding = 0; + rpc_wake_up(&map->pm_bindwait); spin_unlock(&pmap_lock); task->tk_status = -EIO; task->tk_action = NULL; @@ -129,6 +129,7 @@ static void pmap_getport_done(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; + struct rpc_portmap *map = clnt->cl_pmap; dprintk("RPC: %4d pmap_getport_done(status %d, port %d)\n", task->tk_pid, task->tk_status, clnt->cl_port); @@ -145,8 +146,8 @@ pmap_getport_done(struct rpc_task *task) clnt->cl_xprt->addr.sin_port = clnt->cl_port; } spin_lock(&pmap_lock); - clnt->cl_binding = 0; - rpc_wake_up(&clnt->cl_bindwait); + map->pm_binding = 0; + rpc_wake_up(&map->pm_bindwait); spin_unlock(&pmap_lock); } diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index f6bde71024e5..dd8409baa09e 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -41,6 +41,7 @@ EXPORT_SYMBOL(rpc_release_task); /* RPC client functions */ EXPORT_SYMBOL(rpc_create_client); +EXPORT_SYMBOL(rpc_clone_client); EXPORT_SYMBOL(rpc_destroy_client); EXPORT_SYMBOL(rpc_shutdown_client); EXPORT_SYMBOL(rpc_release_client); @@ -66,6 +67,7 @@ EXPORT_SYMBOL(xprt_set_timeout); /* Client credential cache */ EXPORT_SYMBOL(rpcauth_register); EXPORT_SYMBOL(rpcauth_unregister); +EXPORT_SYMBOL(rpcauth_create); EXPORT_SYMBOL(rpcauth_lookupcred); EXPORT_SYMBOL(rpcauth_lookup_credcache); EXPORT_SYMBOL(rpcauth_free_credcache); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index ef616fe9c561..e6c5f7ab7968 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -584,9 +584,9 @@ xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied) __xprt_put_cong(xprt, req); if (timer) { if (req->rq_ntrans == 1) - rpc_update_rtt(&clnt->cl_rtt, timer, + rpc_update_rtt(clnt->cl_rtt, timer, (long)jiffies - req->rq_xtime); - rpc_set_timeo(&clnt->cl_rtt, timer, req->rq_ntrans - 1); + rpc_set_timeo(clnt->cl_rtt, timer, req->rq_ntrans - 1); } } @@ -1224,8 +1224,8 @@ xprt_transmit(struct rpc_task *task) spin_lock_bh(&xprt->sock_lock); if (!xprt->nocong) { int timer = task->tk_msg.rpc_proc->p_timer; - task->tk_timeout = rpc_calc_rto(&clnt->cl_rtt, timer); - task->tk_timeout <<= rpc_ntimeo(&clnt->cl_rtt, timer); + task->tk_timeout = rpc_calc_rto(clnt->cl_rtt, timer); + task->tk_timeout <<= rpc_ntimeo(clnt->cl_rtt, timer); task->tk_timeout <<= clnt->cl_timeout.to_retries - req->rq_timeout.to_retries; if (task->tk_timeout > req->rq_timeout.to_maxval) -- cgit v1.2.3 From c2f2ea78e058ca74fe2e674fd8cdfb71966b248c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:50:51 +0100 Subject: NFSv4/RPCSEC_GSS: Make Frank's server->client_sys feature use RPC cloning in order to avoid duplicating sockets etc. Make NFSv4 share a single socket for all communication to the same server. --- fs/nfs/inode.c | 67 +++++++++++++++++++++++++++++++++++++++----------- fs/nfs/nfs4proc.c | 6 +---- fs/nfs/nfs4state.c | 4 +++ include/linux/nfs_fs.h | 15 +++++------ 4 files changed, 65 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index af022760107a..5cd28cb5076a 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -493,10 +493,17 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) server->client = nfs_create_client(server, data); if (server->client == NULL) goto out_fail; - data->pseudoflavor = RPC_AUTH_UNIX; /* RFC 2623, sec 2.3.2 */ - server->client_sys = nfs_create_client(server, data); - if (server->client_sys == NULL) - goto out_shutdown; + /* RFC 2623, sec 2.3.2 */ + if (authflavor != RPC_AUTH_UNIX) { + server->client_sys = rpc_clone_client(server->client); + if (server->client_sys == NULL) + goto out_shutdown; + if (!rpcauth_create(RPC_AUTH_UNIX, server->client_sys)) + goto out_shutdown; + } else { + atomic_inc(&server->client->cl_count); + server->client_sys = server->client; + } /* Fire up rpciod if not yet running */ if (rpciod_up() != 0) { @@ -1349,6 +1356,7 @@ static struct file_system_type nfs_fs_type = { static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, int silent) { struct nfs_server *server; + struct nfs4_client *clp = NULL; struct rpc_xprt *xprt = NULL; struct rpc_clnt *clnt = NULL; struct rpc_timeout timeparms; @@ -1398,13 +1406,13 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, return -EINVAL; } - /* Now create transport and client */ - xprt = xprt_create_proto(proto, &server->addr, &timeparms); - if (xprt == NULL) { - printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); + clp = nfs4_get_client(&server->addr.sin_addr); + if (!clp) { + printk(KERN_WARNING "NFS: failed to create NFS4 client.\n"); goto out_fail; } + /* Now create transport and client */ authflavour = RPC_AUTH_UNIX; if (data->auth_flavourlen != 0) { if (data->auth_flavourlen > 1) @@ -1414,34 +1422,61 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, goto out_fail; } } - clnt = rpc_create_client(xprt, server->hostname, &nfs_program, - server->rpc_ops->version, authflavour); + + down_write(&clp->cl_sem); + if (clp->cl_rpcclient == NULL) { + xprt = xprt_create_proto(proto, &server->addr, &timeparms); + if (xprt == NULL) { + up_write(&clp->cl_sem); + printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); + goto out_fail; + } + clnt = rpc_create_client(xprt, server->hostname, &nfs_program, + server->rpc_ops->version, authflavour); + if (clnt == NULL) { + up_write(&clp->cl_sem); + printk(KERN_WARNING "NFS: cannot create RPC client.\n"); + xprt_destroy(xprt); + goto out_fail; + } + clnt->cl_chatty = 1; + clp->cl_rpcclient = clnt; + clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); + memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); + } + clnt = rpc_clone_client(clp->cl_rpcclient); + server->nfs4_state = clp; + up_write(&clp->cl_sem); + if (clnt == NULL) { printk(KERN_WARNING "NFS: cannot create RPC client.\n"); - xprt_destroy(xprt); goto out_fail; } clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0; clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0; - clnt->cl_chatty = 1; server->client = clnt; + if (clnt->cl_auth->au_flavor != authflavour) { + if (rpcauth_create(authflavour, clnt) == NULL) { + printk(KERN_WARNING "NFS: couldn't create credcache!\n"); + goto out_shutdown; + } + } + /* Fire up rpciod if not yet running */ if (rpciod_up() != 0) { printk(KERN_WARNING "NFS: couldn't start rpciod!\n"); goto out_shutdown; } - if (create_nfsv4_state(server, data)) - goto out_shutdown; - if ((server->idmap = nfs_idmap_new(server)) == NULL) printk(KERN_WARNING "NFS: couldn't start IDmap\n"); err = nfs_sb_init(sb, authflavour); if (err == 0) return 0; + clp = NULL; rpciod_down(); destroy_nfsv4_state(server); if (server->idmap != NULL) @@ -1449,6 +1484,8 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, out_shutdown: rpc_shutdown_client(server->client); out_fail: + if (clp) + nfs4_put_client(clp); return err; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f447a7a05449..2d6850d65dd3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -762,9 +762,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, struct qstr q; int status; - clp = server->nfs4_state = nfs4_get_client(&server->addr.sin_addr); - if (!clp) - return -ENOMEM; + clp = server->nfs4_state; down_write(&clp->cl_sem); /* Has the clientid already been initialized? */ @@ -850,8 +848,6 @@ no_setclientid: return status; out_unlock: up_write(&clp->cl_sem); - nfs4_put_client(clp); - server->nfs4_state = NULL; return status; } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 42a2344a2893..960899db68e2 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -93,6 +93,10 @@ nfs4_free_client(struct nfs4_client *clp) kfree(sp); } BUG_ON(!list_empty(&clp->cl_state_owners)); + if (clp->cl_cred) + put_rpccred(clp->cl_cred); + if (clp->cl_rpcclient) + rpc_shutdown_client(clp->cl_rpcclient); kfree(clp); } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 5ae592b26d63..1ba074302796 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -489,6 +489,14 @@ struct nfs4_client { int cl_nunused; spinlock_t cl_lock; atomic_t cl_count; + + struct rpc_clnt * cl_rpcclient; + struct rpc_cred * cl_cred; + + /* Our own IP address, as a null-terminated string. + * This is used to generate the clientid, and the callback address. + */ + char cl_ipaddr[16]; }; /* @@ -558,13 +566,6 @@ extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp); struct nfs4_mount_data; -static inline int -create_nfsv4_state(struct nfs_server *server, struct nfs4_mount_data *data) -{ - server->nfs4_state = NULL; - return 0; -} - static inline void destroy_nfsv4_state(struct nfs_server *server) { -- cgit v1.2.3 From acac57debb7af23d6178e319c1017b24ec847ded Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:52:03 +0100 Subject: NFSv4: Convert the RENEW operation from using nfs4_compound, to being a standalone RPC call in preparation for the renew daemon overhaul. --- fs/nfs/nfs4proc.c | 59 +++++++++----------------------------------------- fs/nfs/nfs4renewd.c | 2 +- fs/nfs/nfs4xdr.c | 46 ++++++++++++++++++++++++++++++++++----- include/linux/nfs4.h | 1 + include/linux/nfs_fs.h | 2 +- 5 files changed, 53 insertions(+), 57 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 2d6850d65dd3..e7327e3bf484 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -428,18 +428,6 @@ nfs4_setup_rename(struct nfs4_compound *cp, struct qstr *old, struct qstr *new, cp->req_nops++; } -static void -nfs4_setup_renew(struct nfs4_compound *cp) -{ - struct nfs4_client **client_state = GET_OP(cp, renew); - - *client_state = cp->server->nfs4_state; - - OPNUM(cp) = OP_RENEW; - cp->req_nops++; - cp->renew_index = cp->req_nops; -} - static void nfs4_setup_restorefh(struct nfs4_compound *cp) { @@ -1648,55 +1636,28 @@ nfs4_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) } /* - * nfs4_proc_renew(): This is not one of the nfs_rpc_ops; it is a special + * nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special * standalone procedure for queueing an asynchronous RENEW. */ -struct renew_desc { - struct rpc_task task; - struct nfs4_compound compound; - struct nfs4_op ops[1]; -}; - static void renew_done(struct rpc_task *task) { - struct nfs4_compound *cp = (struct nfs4_compound *) task->tk_msg.rpc_argp; - process_lease(cp); -} - -static void -renew_release(struct rpc_task *task) -{ - kfree(task->tk_calldata); + struct nfs_server *server = (struct nfs_server *)task->tk_msg.rpc_resp; + unsigned long timestamp = (unsigned long)task->tk_calldata; + renew_lease(server, timestamp); } int -nfs4_proc_renew(struct nfs_server *server) +nfs4_proc_async_renew(struct nfs_server *server, struct rpc_cred *cred) { - struct renew_desc *rp; - struct rpc_task *task; - struct nfs4_compound *cp; struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND], + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], + .rpc_argp = server->nfs4_state, + .rpc_resp = server, + .rpc_cred = cred, }; - rp = (struct renew_desc *) kmalloc(sizeof(*rp), GFP_KERNEL); - if (!rp) - return -ENOMEM; - cp = &rp->compound; - task = &rp->task; - - nfs4_setup_compound(cp, rp->ops, server, "renew"); - nfs4_setup_renew(cp); - - msg.rpc_argp = cp; - msg.rpc_resp = cp; - rpc_init_task(task, server->client, renew_done, RPC_TASK_ASYNC); - rpc_call_setup(task, &msg, 0); - task->tk_calldata = rp; - task->tk_release = renew_release; - - return rpc_execute(task); + return rpc_call_async(server->client, &msg, 0, renew_done, (void *)jiffies); } /* diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c index 4ba871885dbc..92176b0b0a9b 100644 --- a/fs/nfs/nfs4renewd.c +++ b/fs/nfs/nfs4renewd.c @@ -70,7 +70,7 @@ renewd(struct rpc_task *task) timeout = (2 * lease) / 3 + last - jiffies; else { /* Queue an asynchronous RENEW. */ - nfs4_proc_renew(server); + nfs4_proc_async_renew(server, NULL); timeout = (2 * lease) / 3; } diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 35a28e9e7d21..add338c9be56 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -90,6 +90,8 @@ extern int nfs_stat_to_errno(int); #define decode_pre_write_getattr_maxsz op_decode_hdr_maxsz + 5 #define encode_post_write_getattr_maxsz op_encode_hdr_maxsz + 2 #define decode_post_write_getattr_maxsz op_decode_hdr_maxsz + 13 +#define encode_renew_maxsz op_encode_hdr_maxsz + 3 +#define decode_renew_maxsz op_decode_hdr_maxsz #define NFS4_enc_compound_sz 1024 /* XXX: large enough? */ #define NFS4_dec_compound_sz 1024 /* XXX: large enough? */ @@ -159,6 +161,10 @@ extern int nfs_stat_to_errno(int); #define NFS4_dec_setattr_sz compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 3 +#define NFS4_enc_renew_sz compound_encode_hdr_maxsz + \ + encode_renew_maxsz +#define NFS4_dec_renew_sz compound_decode_hdr_maxsz + \ + decode_renew_maxsz static struct { @@ -889,9 +895,6 @@ encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs case OP_RENAME: status = encode_rename(xdr, &cp->ops[i].u.rename); break; - case OP_RENEW: - status = encode_renew(xdr, cp->ops[i].u.renew); - break; case OP_RESTOREFH: status = encode_restorefh(xdr); break; @@ -1131,6 +1134,22 @@ out: return status; } +/* + * a RENEW request + */ +static int +nfs4_xdr_enc_renew(struct rpc_rqst *req, uint32_t *p, struct nfs4_client *clp) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 1, + }; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + return encode_renew(&xdr, clp); +} + /* * START OF "GENERIC" DECODE ROUTINES. * These may look a little ugly since they are imported from a "generic" @@ -2137,9 +2156,6 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs case OP_RENAME: status = decode_rename(xdr, &op->u.rename); break; - case OP_RENEW: - status = decode_renew(xdr); - break; case OP_SAVEFH: status = decode_savefh(xdr); break; @@ -2387,6 +2403,23 @@ out: return status; } +/* + * Decode RENEW response + */ +static int +nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, uint32_t *p, void *dummy) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_renew(&xdr); + return status; +} + uint32_t * nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) { @@ -2443,6 +2476,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm), PROC(CLOSE, enc_close, dec_close), PROC(SETATTR, enc_setattr, dec_setattr), + PROC(RENEW, enc_renew, dec_renew), }; struct rpc_version nfs_version4 = { diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 8bb512eb2b43..1598d1b3c739 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -221,6 +221,7 @@ enum { NFSPROC4_CLNT_OPEN_CONFIRM, NFSPROC4_CLNT_CLOSE, NFSPROC4_CLNT_SETATTR, + NFSPROC4_CLNT_RENEW, }; #endif diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 1ba074302796..a8dc94ea12d3 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -545,7 +545,7 @@ struct nfs4_state { /* nfs4proc.c */ -extern int nfs4_proc_renew(struct nfs_server *server); +extern int nfs4_proc_async_renew(struct nfs_server *server, struct rpc_cred *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); /* nfs4renewd.c */ -- cgit v1.2.3 From 7cf3d8b799b5a018b89bc7ce553d66c12aa50ade Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:53:15 +0100 Subject: NFSv4: Convert the lease renewal daemon from being per-mountpoint to being per-server. Instead of running it on top of rpciod, convert it to use keventd. This mean we can use the struct nfs4_client semaphores for ordering purposes. --- fs/nfs/inode.c | 17 ++++++-- fs/nfs/nfs4proc.c | 41 ++++++++++-------- fs/nfs/nfs4renewd.c | 108 +++++++++++++++++++++++++++++++--------------- fs/nfs/nfs4state.c | 26 +++++++++++ include/linux/nfs_fs.h | 35 +++++++-------- include/linux/nfs_fs_sb.h | 5 ++- 6 files changed, 154 insertions(+), 78 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 5cd28cb5076a..2c1df6550feb 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -163,6 +163,8 @@ nfs_put_super(struct super_block *sb) nfs_idmap_delete(server); #endif /* CONFIG_NFS_V4 */ + nfs4_renewd_prepare_shutdown(server); + if (server->client != NULL) rpc_shutdown_client(server->client); if (server->client_sys != NULL) @@ -1281,6 +1283,8 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, if (!server) return ERR_PTR(-ENOMEM); memset(server, 0, sizeof(struct nfs_server)); + /* Zero out the NFS state stuff */ + init_nfsv4_state(server); root = &server->fh; memcpy(root, &data->root, sizeof(*root)); @@ -1444,13 +1448,15 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); } + list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); clnt = rpc_clone_client(clp->cl_rpcclient); server->nfs4_state = clp; up_write(&clp->cl_sem); + clp = NULL; if (clnt == NULL) { printk(KERN_WARNING "NFS: cannot create RPC client.\n"); - goto out_fail; + goto out_remove_list; } clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0; @@ -1476,13 +1482,16 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, err = nfs_sb_init(sb, authflavour); if (err == 0) return 0; - clp = NULL; rpciod_down(); - destroy_nfsv4_state(server); if (server->idmap != NULL) nfs_idmap_delete(server); out_shutdown: rpc_shutdown_client(server->client); +out_remove_list: + down_write(&server->nfs4_state->cl_sem); + list_del_init(&server->nfs4_siblings); + up_write(&server->nfs4_state->cl_sem); + destroy_nfsv4_state(server); out_fail: if (clp) nfs4_put_client(clp); @@ -1542,6 +1551,8 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type, if (!server) return ERR_PTR(-ENOMEM); memset(server, 0, sizeof(struct nfs_server)); + /* Zero out the NFS state stuff */ + init_nfsv4_state(server); if (data->version != NFS4_MOUNT_VERSION) { printk("nfs warning: mount version %s than kernel\n", diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e7327e3bf484..a3ac8370ba3c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -56,8 +56,6 @@ extern struct rpc_procinfo nfs4_procedures[]; extern nfs4_stateid zero_stateid; -static spinlock_t renew_lock = SPIN_LOCK_UNLOCKED; - static void nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops, struct nfs_server *server, char *tag) @@ -480,10 +478,11 @@ nfs4_setup_setclientid_confirm(struct nfs4_compound *cp) static void renew_lease(struct nfs_server *server, unsigned long timestamp) { - spin_lock(&renew_lock); - if (time_before(server->last_renewal,timestamp)) - server->last_renewal = timestamp; - spin_unlock(&renew_lock); + struct nfs4_client *clp = server->nfs4_state; + spin_lock(&clp->cl_lock); + if (time_before(clp->cl_last_renewal,timestamp)) + clp->cl_last_renewal = timestamp; + spin_unlock(&clp->cl_lock); } static inline void @@ -748,6 +747,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo fsinfo; unsigned char * p; struct qstr q; + unsigned long last_renewed; int status; clp = server->nfs4_state; @@ -773,6 +773,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, */ nfs4_setup_compound(&compound, ops, server, "setclientid"); nfs4_setup_setclientid(&compound, 0, 0); + last_renewed = jiffies; if ((status = nfs4_call_compound(&compound, NULL, 0))) goto out_unlock; @@ -786,20 +787,22 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, nfs4_setup_putrootfh(&compound); nfs4_setup_getrootattr(&compound, fattr, &fsinfo); nfs4_setup_getfh(&compound, fhandle); + last_renewed = jiffies; if ((status = nfs4_call_compound(&compound, NULL, 0))) goto out_unlock; - clp->cl_state = NFS4CLNT_OK; -no_setclientid: /* * Now that we have instantiated the clientid and determined * the lease time, we can initialize the renew daemon for this * server. * FIXME: we only need one renewd daemon per server. */ - server->lease_time = fsinfo.lease_time * HZ; - if ((status = nfs4_init_renewd(server))) - goto out_unlock; + clp->cl_lease_time = fsinfo.lease_time * HZ; + clp->cl_last_renewal = last_renewed; + nfs4_schedule_state_renewal(clp); + clp->cl_state = NFS4CLNT_OK; + +no_setclientid: up_write(&clp->cl_sem); /* @@ -1642,22 +1645,24 @@ nfs4_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) static void renew_done(struct rpc_task *task) { - struct nfs_server *server = (struct nfs_server *)task->tk_msg.rpc_resp; + struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp; unsigned long timestamp = (unsigned long)task->tk_calldata; - renew_lease(server, timestamp); + spin_lock(&clp->cl_lock); + if (time_before(clp->cl_last_renewal,timestamp)) + clp->cl_last_renewal = timestamp; + spin_unlock(&clp->cl_lock); } int -nfs4_proc_async_renew(struct nfs_server *server, struct rpc_cred *cred) +nfs4_proc_async_renew(struct nfs4_client *clp) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], - .rpc_argp = server->nfs4_state, - .rpc_resp = server, - .rpc_cred = cred, + .rpc_argp = clp, + .rpc_cred = clp->cl_cred, }; - return rpc_call_async(server->client, &msg, 0, renew_done, (void *)jiffies); + return rpc_call_async(clp->cl_rpcclient, &msg, 0, renew_done, (void *)jiffies); } /* diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c index 92176b0b0a9b..667e06f1c647 100644 --- a/fs/nfs/nfs4renewd.c +++ b/fs/nfs/nfs4renewd.c @@ -54,53 +54,91 @@ #include #include -static RPC_WAITQ(nfs4_renewd_queue, "nfs4_renewd_queue"); +#define NFSDBG_FACILITY NFSDBG_PROC -static void -renewd(struct rpc_task *task) +void +nfs4_renew_state(void *data) { - struct nfs_server *server = (struct nfs_server *)task->tk_calldata; - unsigned long lease = server->lease_time; - unsigned long last = server->last_renewal; - unsigned long timeout; + struct nfs4_client *clp = (struct nfs4_client *)data; + long lease, timeout; + unsigned long last, now; - if (!server->nfs4_state) - timeout = (2 * lease) / 3; - else if (jiffies < last + lease/3) - timeout = (2 * lease) / 3 + last - jiffies; - else { + down_read(&clp->cl_sem); + dprintk("%s: start\n", __FUNCTION__); + /* Are there any active superblocks? */ + if (list_empty(&clp->cl_superblocks)) + goto out; + spin_lock(&clp->cl_lock); + lease = clp->cl_lease_time; + last = clp->cl_last_renewal; + now = jiffies; + timeout = (2 * lease) / 3 + (long)last - (long)now; + /* Are we close to a lease timeout? */ + if (time_after(now, last + lease/3)) { + spin_unlock(&clp->cl_lock); /* Queue an asynchronous RENEW. */ - nfs4_proc_async_renew(server, NULL); + nfs4_proc_async_renew(clp); timeout = (2 * lease) / 3; - } - + spin_lock(&clp->cl_lock); + } else + dprintk("%s: failed to call renewd. Reason: lease not expired \n", + __FUNCTION__); if (timeout < 5 * HZ) /* safeguard */ timeout = 5 * HZ; - task->tk_timeout = timeout; - task->tk_action = renewd; - task->tk_exit = NULL; - rpc_sleep_on(&nfs4_renewd_queue, task, NULL, NULL); - return; + dprintk("%s: requeueing work. Lease period = %ld\n", + __FUNCTION__, (timeout + HZ - 1) / HZ); + cancel_delayed_work(&clp->cl_renewd); + schedule_delayed_work(&clp->cl_renewd, timeout); + spin_unlock(&clp->cl_lock); +out: + up_read(&clp->cl_sem); + dprintk("%s: done\n", __FUNCTION__); } -int -nfs4_init_renewd(struct nfs_server *server) +/* Must be called with clp->cl_sem locked for writes */ +void +nfs4_schedule_state_renewal(struct nfs4_client *clp) { - struct rpc_task *task; - int status; + long timeout; - lock_kernel(); - status = -ENOMEM; - task = rpc_new_task(server->client, NULL, RPC_TASK_ASYNC); - if (!task) - goto out; - task->tk_calldata = server; - task->tk_action = renewd; - status = rpc_execute(task); + spin_lock(&clp->cl_lock); + timeout = (2 * clp->cl_lease_time) / 3 + (long)clp->cl_last_renewal + - (long)jiffies; + if (timeout < 5 * HZ) + timeout = 5 * HZ; + dprintk("%s: requeueing work. Lease period = %ld\n", + __FUNCTION__, (timeout + HZ - 1) / HZ); + cancel_delayed_work(&clp->cl_renewd); + schedule_delayed_work(&clp->cl_renewd, timeout); + spin_unlock(&clp->cl_lock); +} -out: - unlock_kernel(); - return status; +void +nfs4_renewd_prepare_shutdown(struct nfs_server *server) +{ + struct nfs4_client *clp = server->nfs4_state; + + if (!clp) + return; + flush_scheduled_work(); + down_write(&clp->cl_sem); + if (!list_empty(&server->nfs4_siblings)) + list_del_init(&server->nfs4_siblings); + up_write(&clp->cl_sem); +} + +/* Must be called with clp->cl_sem locked for writes */ +void +nfs4_kill_renewd(struct nfs4_client *clp) +{ + down_read(&clp->cl_sem); + if (!list_empty(&clp->cl_superblocks)) { + up_read(&clp->cl_sem); + return; + } + cancel_delayed_work(&clp->cl_renewd); + up_read(&clp->cl_sem); + flush_scheduled_work(); } /* diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 960899db68e2..bded88b8d3eb 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -41,6 +41,7 @@ #include #include #include +#include #define OPENOWNER_POOL_SIZE 8 @@ -55,6 +56,28 @@ nfs4_stateid one_stateid = static LIST_HEAD(nfs4_clientid_list); +extern void nfs4_renew_state(void *); + +void +init_nfsv4_state(struct nfs_server *server) +{ + server->nfs4_state = NULL; + INIT_LIST_HEAD(&server->nfs4_siblings); +} + +void +destroy_nfsv4_state(struct nfs_server *server) +{ + if (server->mnt_path) { + kfree(server->mnt_path); + server->mnt_path = NULL; + } + if (server->nfs4_state) { + nfs4_put_client(server->nfs4_state); + server->nfs4_state = NULL; + } +} + /* * nfs4_get_client(): returns an empty client structure * nfs4_put_client(): drops reference to client structure @@ -75,6 +98,8 @@ nfs4_alloc_client(struct in_addr *addr) INIT_LIST_HEAD(&clp->cl_unused); spin_lock_init(&clp->cl_lock); atomic_set(&clp->cl_count, 1); + INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp); + INIT_LIST_HEAD(&clp->cl_superblocks); clp->cl_state = NFS4CLNT_NEW; } return clp; @@ -130,6 +155,7 @@ nfs4_put_client(struct nfs4_client *clp) return; list_del(&clp->cl_servers); spin_unlock(&state_spinlock); + nfs4_kill_renewd(clp); nfs4_free_client(clp); } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index a8dc94ea12d3..4996221041a4 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -28,6 +28,7 @@ #include #include #include +#include /* * Enable debugging support for nfs client. @@ -493,6 +494,12 @@ struct nfs4_client { struct rpc_clnt * cl_rpcclient; struct rpc_cred * cl_cred; + struct list_head cl_superblocks; /* List of nfs_server structs */ + + unsigned long cl_lease_time; + unsigned long cl_last_renewal; + struct work_struct cl_renewd; + /* Our own IP address, as a null-terminated string. * This is used to generate the clientid, and the callback address. */ @@ -545,13 +552,17 @@ struct nfs4_state { /* nfs4proc.c */ -extern int nfs4_proc_async_renew(struct nfs_server *server, struct rpc_cred *); +extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); /* nfs4renewd.c */ -extern int nfs4_init_renewd(struct nfs_server *server); +extern void nfs4_schedule_state_renewal(struct nfs4_client *); +extern void nfs4_renewd_prepare_shutdown(struct nfs_server *); +extern void nfs4_kill_renewd(struct nfs4_client *); /* nfs4state.c */ +extern void init_nfsv4_state(struct nfs_server *); +extern void destroy_nfsv4_state(struct nfs_server *); extern struct nfs4_client *nfs4_get_client(struct in_addr *); extern void nfs4_put_client(struct nfs4_client *clp); extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); @@ -560,29 +571,13 @@ extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp); - - - - - struct nfs4_mount_data; -static inline void -destroy_nfsv4_state(struct nfs_server *server) -{ - if (server->mnt_path) { - kfree(server->mnt_path); - server->mnt_path = NULL; - } - if (server->nfs4_state) { - nfs4_put_client(server->nfs4_state); - server->nfs4_state = NULL; - } -} #else -#define create_nfsv4_state(server, data) 0 +#define init_nfsv4_state(server) do { } while (0) #define destroy_nfsv4_state(server) do { } while (0) #define nfs4_put_state_owner(inode, owner) do { } while (0) #define nfs4_put_open_state(state) do { } while (0) +#define nfs4_renewd_prepare_shutdown(server) do { } while (0) #endif #endif /* __KERNEL__ */ diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 20ceb626cb3b..5f0b0ce3aa2c 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -35,8 +35,9 @@ struct nfs_server { char ip_addr[16]; char * mnt_path; struct nfs4_client * nfs4_state; /* all NFSv4 state starts here */ - unsigned long lease_time; /* in jiffies */ - unsigned long last_renewal; /* in jiffies */ + struct list_head nfs4_siblings; /* List of other nfs_server structs + * that share the same clientid + */ void *idmap; #endif }; -- cgit v1.2.3 From f414757e25c8122dcf6ac1b546f67b6317e161b3 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:54:20 +0100 Subject: NFSv4: Split out the code for retrieving static server information out of the GETATTR compound. --- fs/nfs/nfs4proc.c | 73 +++++----------------- fs/nfs/nfs4xdr.c | 160 ++++++++++++++++++++++++++++++++++++++---------- include/linux/nfs4.h | 1 + include/linux/nfs_xdr.h | 1 - 4 files changed, 144 insertions(+), 91 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a3ac8370ba3c..264ef919baee 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -56,6 +56,9 @@ extern struct rpc_procinfo nfs4_procedures[]; extern nfs4_stateid zero_stateid; +static int nfs4_proc_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); + + static void nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops, struct nfs_server *server, char *tag) @@ -177,44 +180,16 @@ u32 nfs4_statfs_bitmap[2] = { | FATTR4_WORD1_SPACE_TOTAL }; -u32 nfs4_fsinfo_bitmap[2] = { - FATTR4_WORD0_MAXFILESIZE - | FATTR4_WORD0_MAXREAD - | FATTR4_WORD0_MAXWRITE - | FATTR4_WORD0_LEASE_TIME, - 0 -}; - u32 nfs4_pathconf_bitmap[2] = { FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME, 0 }; -/* mount bitmap: fattr bitmap + lease time */ -u32 nfs4_mount_bitmap[2] = { - FATTR4_WORD0_TYPE - | FATTR4_WORD0_CHANGE - | FATTR4_WORD0_SIZE - | FATTR4_WORD0_FSID - | FATTR4_WORD0_FILEID - | FATTR4_WORD0_LEASE_TIME, - FATTR4_WORD1_MODE - | FATTR4_WORD1_NUMLINKS - | FATTR4_WORD1_OWNER - | FATTR4_WORD1_OWNER_GROUP - | FATTR4_WORD1_RAWDEV - | FATTR4_WORD1_SPACE_USED - | FATTR4_WORD1_TIME_ACCESS - | FATTR4_WORD1_TIME_METADATA - | FATTR4_WORD1_TIME_MODIFY -}; - static inline void __nfs4_setup_getattr(struct nfs4_compound *cp, u32 *bitmap, struct nfs_fattr *fattr, struct nfs_fsstat *fsstat, - struct nfs_fsinfo *fsinfo, struct nfs_pathconf *pathconf) { struct nfs4_getattr *getattr = GET_OP(cp, getattr); @@ -222,7 +197,6 @@ __nfs4_setup_getattr(struct nfs4_compound *cp, u32 *bitmap, getattr->gt_bmval = bitmap; getattr->gt_attrs = fattr; getattr->gt_fsstat = fsstat; - getattr->gt_fsinfo = fsinfo; getattr->gt_pathconf = pathconf; OPNUM(cp) = OP_GETATTR; @@ -234,16 +208,7 @@ nfs4_setup_getattr(struct nfs4_compound *cp, struct nfs_fattr *fattr) { __nfs4_setup_getattr(cp, nfs4_fattr_bitmap, fattr, - NULL, NULL, NULL); -} - -static void -nfs4_setup_getrootattr(struct nfs4_compound *cp, - struct nfs_fattr *fattr, - struct nfs_fsinfo *fsinfo) -{ - __nfs4_setup_getattr(cp, nfs4_mount_bitmap, - fattr, NULL, fsinfo, NULL); + NULL, NULL); } static void @@ -251,15 +216,7 @@ nfs4_setup_statfs(struct nfs4_compound *cp, struct nfs_fsstat *fsstat) { __nfs4_setup_getattr(cp, nfs4_statfs_bitmap, - NULL, fsstat, NULL, NULL); -} - -static void -nfs4_setup_fsinfo(struct nfs4_compound *cp, - struct nfs_fsinfo *fsinfo) -{ - __nfs4_setup_getattr(cp, nfs4_fsinfo_bitmap, - NULL, NULL, fsinfo, NULL); + NULL, fsstat, NULL); } static void @@ -267,7 +224,7 @@ nfs4_setup_pathconf(struct nfs4_compound *cp, struct nfs_pathconf *pathconf) { __nfs4_setup_getattr(cp, nfs4_pathconf_bitmap, - NULL, NULL, NULL, pathconf); + NULL, NULL, pathconf); } static void @@ -759,7 +716,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, fattr->valid = 0; nfs4_setup_compound(&compound, ops, server, "getrootfh"); nfs4_setup_putrootfh(&compound); - nfs4_setup_getrootattr(&compound, fattr, &fsinfo); + nfs4_setup_getattr(&compound, fattr); nfs4_setup_getfh(&compound, fhandle); if ((status = nfs4_call_compound(&compound, NULL, 0))) goto out_unlock; @@ -785,7 +742,7 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, nfs4_setup_compound(&compound, ops, server, "setclientid_confirm"); nfs4_setup_setclientid_confirm(&compound); nfs4_setup_putrootfh(&compound); - nfs4_setup_getrootattr(&compound, fattr, &fsinfo); + nfs4_setup_getattr(&compound, fattr); nfs4_setup_getfh(&compound, fhandle); last_renewed = jiffies; if ((status = nfs4_call_compound(&compound, NULL, 0))) @@ -797,6 +754,8 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, * server. * FIXME: we only need one renewd daemon per server. */ + if ((status = nfs4_proc_fsinfo(server, fhandle, &fsinfo))) + goto out_unlock; clp->cl_lease_time = fsinfo.lease_time * HZ; clp->cl_last_renewal = last_renewed; nfs4_schedule_state_renewal(clp); @@ -1434,14 +1393,14 @@ static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo) { - struct nfs4_compound compound; - struct nfs4_op ops[2]; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSINFO], + .rpc_argp = fhandle, + .rpc_resp = fsinfo, + }; memset(fsinfo, 0, sizeof(*fsinfo)); - nfs4_setup_compound(&compound, ops, server, "statfs"); - nfs4_setup_putfh(&compound, fhandle); - nfs4_setup_fsinfo(&compound, fsinfo); - return nfs4_call_compound(&compound, NULL, 0); + return rpc_call_sync(server->client, &msg, 0); } static int diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index add338c9be56..ed79661601dd 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -90,6 +90,8 @@ extern int nfs_stat_to_errno(int); #define decode_pre_write_getattr_maxsz op_decode_hdr_maxsz + 5 #define encode_post_write_getattr_maxsz op_encode_hdr_maxsz + 2 #define decode_post_write_getattr_maxsz op_decode_hdr_maxsz + 13 +#define encode_fsinfo_maxsz op_encode_hdr_maxsz + 2 +#define decode_fsinfo_maxsz op_decode_hdr_maxsz + 11 #define encode_renew_maxsz op_encode_hdr_maxsz + 3 #define decode_renew_maxsz op_decode_hdr_maxsz @@ -161,6 +163,12 @@ extern int nfs_stat_to_errno(int); #define NFS4_dec_setattr_sz compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 3 +#define NFS4_enc_fsinfo_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_fsinfo_maxsz +#define NFS4_dec_fsinfo_sz compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_fsinfo_maxsz #define NFS4_enc_renew_sz compound_encode_hdr_maxsz + \ encode_renew_maxsz #define NFS4_dec_renew_sz compound_decode_hdr_maxsz + \ @@ -506,6 +514,15 @@ encode_post_write_getattr(struct xdr_stream *xdr) FATTR4_WORD1_TIME_MODIFY); } +static int +encode_fsinfo(struct xdr_stream *xdr) +{ + return encode_getattr_one(xdr, FATTR4_WORD0_MAXFILESIZE + | FATTR4_WORD0_MAXREAD + | FATTR4_WORD0_MAXWRITE + | FATTR4_WORD0_LEASE_TIME); +} + static int encode_getfh(struct xdr_stream *xdr) { @@ -1134,6 +1151,26 @@ out: return status; } +/* + * FSINFO request + */ +static int +nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, uint32_t *p, void *fhandle) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, fhandle); + if (!status) + status = encode_fsinfo(&xdr); + return status; +} + /* * a RENEW request */ @@ -1312,7 +1349,6 @@ decode_create(struct xdr_stream *xdr, struct nfs4_create *create) } extern uint32_t nfs4_fattr_bitmap[2]; -extern uint32_t nfs4_fsinfo_bitmap[2]; extern uint32_t nfs4_fsstat_bitmap[2]; extern uint32_t nfs4_pathconf_bitmap[2]; @@ -1322,7 +1358,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, { struct nfs_fattr *nfp = getattr->gt_attrs; struct nfs_fsstat *fsstat = getattr->gt_fsstat; - struct nfs_fsinfo *fsinfo = getattr->gt_fsinfo; struct nfs_pathconf *pathconf = getattr->gt_pathconf; uint32_t attrlen, dummy32, bmlen, bmval0 = 0, @@ -1368,11 +1403,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, nfp->nlink = 1; nfp->timestamp = jiffies; } - if (fsinfo) { - fsinfo->rtmult = fsinfo->wtmult = 512; /* ??? */ - fsinfo->lease_time = 60; - } - if (bmval0 & FATTR4_WORD0_TYPE) { READ_BUF(4); len += 4; @@ -1406,12 +1436,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, (long long)nfp->fsid_u.nfs4.major, (long long)nfp->fsid_u.nfs4.minor); } - if (bmval0 & FATTR4_WORD0_LEASE_TIME) { - READ_BUF(4); - len += 4; - READ32(fsinfo->lease_time); - dprintk("read_attrs: lease_time=%d\n", fsinfo->lease_time); - } if (bmval0 & FATTR4_WORD0_FILEID) { READ_BUF(8); len += 8; @@ -1436,12 +1460,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, READ64(fsstat->tfiles); dprintk("read_attrs: files_tot=0x%Lx\n", (long long) fsstat->tfiles); } - if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { - READ_BUF(8); - len += 8; - READ64(fsinfo->maxfilesize); - dprintk("read_attrs: maxfilesize=0x%Lx\n", (long long) fsinfo->maxfilesize); - } if (bmval0 & FATTR4_WORD0_MAXLINK) { READ_BUF(4); len += 4; @@ -1454,20 +1472,6 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, READ32(pathconf->max_namelen); dprintk("read_attrs: maxname=%d\n", pathconf->max_namelen); } - if (bmval0 & FATTR4_WORD0_MAXREAD) { - READ_BUF(8); - len += 8; - READ64(fsinfo->rtmax); - fsinfo->rtpref = fsinfo->dtpref = fsinfo->rtmax; - dprintk("read_attrs: maxread=%d\n", fsinfo->rtmax); - } - if (bmval0 & FATTR4_WORD0_MAXWRITE) { - READ_BUF(8); - len += 8; - READ64(fsinfo->wtmax); - fsinfo->wtpref = fsinfo->wtmax; - dprintk("read_attrs: maxwrite=%d\n", fsinfo->wtmax); - } if (bmval1 & FATTR4_WORD1_MODE) { READ_BUF(4); @@ -1709,6 +1713,74 @@ out_bad_bitmap: } +static int +decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) +{ + uint32_t *p; + uint32_t len, attrlen, bmlen, bmval0 = 0, bmval1 = 0; + int status; + + status = decode_op_hdr(xdr, OP_GETATTR); + if (status) + return status; + READ_BUF(4); + READ32(bmlen); + if (bmlen < 1) + return -EIO; + READ_BUF(bmlen << 2); + READ32(bmval0); + if (bmval0 & ~(FATTR4_WORD0_MAXFILESIZE|FATTR4_WORD0_MAXREAD| + FATTR4_WORD0_MAXWRITE|FATTR4_WORD0_LEASE_TIME)) + goto out_bad_bitmap; + if (bmlen > 1) { + READ32(bmval1); + if (bmval1 != 0 || bmlen > 2) + goto out_bad_bitmap; + } + READ_BUF(4); + READ32(attrlen); + READ_BUF(attrlen); + fsinfo->rtmult = fsinfo->wtmult = 512; /* ??? */ + fsinfo->lease_time = 60; + len = attrlen; + + if (bmval0 & FATTR4_WORD0_LEASE_TIME) { + len -= 4; + READ32(fsinfo->lease_time); + dprintk("read_attrs: lease_time=%d\n", fsinfo->lease_time); + } + if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { + len -= 8; + READ64(fsinfo->maxfilesize); + dprintk("read_attrs: maxfilesize=0x%Lx\n", (long long) fsinfo->maxfilesize); + } + if (bmval0 & FATTR4_WORD0_MAXREAD) { + len -= 8; + READ64(fsinfo->rtmax); + fsinfo->rtpref = fsinfo->dtpref = fsinfo->rtmax; + dprintk("read_attrs: maxread=%d\n", fsinfo->rtmax); + } + if (bmval0 & FATTR4_WORD0_MAXWRITE) { + len -= 8; + READ64(fsinfo->wtmax); + fsinfo->wtpref = fsinfo->wtmax; + dprintk("read_attrs: maxwrite=%d\n", fsinfo->wtmax); + } + if (len != 0) + goto out_bad_attrlen; + return 0; +out_bad_attrlen: + printk(KERN_NOTICE "%s: server attribute length %u does not match bitmap 0x%x/0x%x\n", + __FUNCTION__, (unsigned int)attrlen, + (unsigned int) bmval0, (unsigned int)bmval1); + return -EIO; +out_bad_bitmap: + printk(KERN_NOTICE "%s: server returned bad attribute bitmap 0x%x/0x%x\n", + __FUNCTION__, + (unsigned int)bmval0, (unsigned int)bmval1); + return -EIO; +} + static int decode_getfh(struct xdr_stream *xdr, struct nfs4_getfh *getfh) { @@ -2403,6 +2475,27 @@ out: return status; } +/* + * FSINFO request + */ +static int +nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, uint32_t *p, struct nfs_fsinfo *fsinfo) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_putfh(&xdr); + if (!status) + status = decode_fsinfo(&xdr, fsinfo); + if (!status) + status = -nfs_stat_to_errno(hdr.status); + return status; +} + /* * Decode RENEW response */ @@ -2476,6 +2569,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm), PROC(CLOSE, enc_close, dec_close), PROC(SETATTR, enc_setattr, dec_setattr), + PROC(FSINFO, enc_fsinfo, dec_fsinfo), PROC(RENEW, enc_renew, dec_renew), }; diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 1598d1b3c739..d535a46f7d4f 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -221,6 +221,7 @@ enum { NFSPROC4_CLNT_OPEN_CONFIRM, NFSPROC4_CLNT_CLOSE, NFSPROC4_CLNT_SETATTR, + NFSPROC4_CLNT_FSINFO, NFSPROC4_CLNT_RENEW, }; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 5bc59e4db5e3..09c2dd2216d0 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -449,7 +449,6 @@ struct nfs4_getattr { u32 * gt_bmval; /* request */ struct nfs_fattr * gt_attrs; /* response */ struct nfs_fsstat * gt_fsstat; /* response */ - struct nfs_fsinfo * gt_fsinfo; /* response */ struct nfs_pathconf * gt_pathconf; /* response */ }; -- cgit v1.2.3 From 88e4b0f2c019ff2f4cee0045812bab0f8146f4ae Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:56:12 +0100 Subject: NFSv4: Convert SETCLIENTID and SETCLIENTID_CONFIRM to be standalone operations. Ensure that SETCLIENTID_CONFIRM always returns the lease timeout length. --- fs/nfs/nfs4proc.c | 127 ++++++++++++++++++++++---------------------- fs/nfs/nfs4xdr.c | 136 ++++++++++++++++++++++++++++++++++++++++++------ include/linux/nfs4.h | 2 + include/linux/nfs_fs.h | 2 + include/linux/nfs_xdr.h | 2 - 5 files changed, 186 insertions(+), 83 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 264ef919baee..8e8ac9cc08f6 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -56,9 +56,6 @@ extern struct rpc_procinfo nfs4_procedures[]; extern nfs4_stateid zero_stateid; -static int nfs4_proc_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); - - static void nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops, struct nfs_server *server, char *tag) @@ -397,41 +394,6 @@ nfs4_setup_savefh(struct nfs4_compound *cp) cp->req_nops++; } -static void -nfs4_setup_setclientid(struct nfs4_compound *cp, u32 program, unsigned short port) -{ - struct nfs4_setclientid *setclientid = GET_OP(cp, setclientid); - struct nfs_server *server = cp->server; - struct timespec tv; - u32 *p; - - tv = CURRENT_TIME; - p = (u32 *)setclientid->sc_verifier.data; - *p++ = tv.tv_sec; - *p++ = tv.tv_nsec; - setclientid->sc_name = server->ip_addr; - sprintf(setclientid->sc_netid, "udp"); - sprintf(setclientid->sc_uaddr, "%s.%d.%d", server->ip_addr, port >> 8, port & 255); - setclientid->sc_prog = program; - setclientid->sc_cb_ident = 0; - setclientid->sc_state = server->nfs4_state; - - OPNUM(cp) = OP_SETCLIENTID; - cp->req_nops++; -} - -static void -nfs4_setup_setclientid_confirm(struct nfs4_compound *cp) -{ - struct nfs4_client **client_state = GET_OP(cp, setclientid_confirm); - - *client_state = cp->server->nfs4_state; - - OPNUM(cp) = OP_SETCLIENTID_CONFIRM; - cp->req_nops++; - cp->renew_index = cp->req_nops; -} - static void renew_lease(struct nfs_server *server, unsigned long timestamp) { @@ -701,51 +663,31 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs4_client *clp; struct nfs4_compound compound; struct nfs4_op ops[4]; - struct nfs_fsinfo fsinfo; unsigned char * p; struct qstr q; - unsigned long last_renewed; int status; clp = server->nfs4_state; down_write(&clp->cl_sem); /* Has the clientid already been initialized? */ - if (clp->cl_state != NFS4CLNT_NEW) { + if (clp->cl_state != NFS4CLNT_NEW) /* Yep, so just read the root attributes and the lease time. */ - fattr->valid = 0; - nfs4_setup_compound(&compound, ops, server, "getrootfh"); - nfs4_setup_putrootfh(&compound); - nfs4_setup_getattr(&compound, fattr); - nfs4_setup_getfh(&compound, fhandle); - if ((status = nfs4_call_compound(&compound, NULL, 0))) - goto out_unlock; goto no_setclientid; - } /* * SETCLIENTID. * Until delegations are imported, we don't bother setting the program * number and port to anything meaningful. */ - nfs4_setup_compound(&compound, ops, server, "setclientid"); - nfs4_setup_setclientid(&compound, 0, 0); - last_renewed = jiffies; - if ((status = nfs4_call_compound(&compound, NULL, 0))) + if ((status = nfs4_proc_setclientid(clp, 0, 0))) goto out_unlock; /* * SETCLIENTID_CONFIRM, plus root filehandle. * We also get the lease time here. */ - fattr->valid = 0; - nfs4_setup_compound(&compound, ops, server, "setclientid_confirm"); - nfs4_setup_setclientid_confirm(&compound); - nfs4_setup_putrootfh(&compound); - nfs4_setup_getattr(&compound, fattr); - nfs4_setup_getfh(&compound, fhandle); - last_renewed = jiffies; - if ((status = nfs4_call_compound(&compound, NULL, 0))) + if ((status = nfs4_proc_setclientid_confirm(clp))) goto out_unlock; /* @@ -754,10 +696,6 @@ nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, * server. * FIXME: we only need one renewd daemon per server. */ - if ((status = nfs4_proc_fsinfo(server, fhandle, &fsinfo))) - goto out_unlock; - clp->cl_lease_time = fsinfo.lease_time * HZ; - clp->cl_last_renewal = last_renewed; nfs4_schedule_state_renewal(clp); clp->cl_state = NFS4CLNT_OK; @@ -770,6 +708,13 @@ no_setclientid: * catch an ERR_WRONGSEC if it occurs along the way... */ p = server->mnt_path; + fattr->valid = 0; + nfs4_setup_compound(&compound, ops, server, "getrootfh"); + nfs4_setup_putrootfh(&compound); + nfs4_setup_getattr(&compound, fattr); + nfs4_setup_getfh(&compound, fhandle); + if ((status = nfs4_call_compound(&compound, NULL, 0))) + goto out; for (;;) { while (*p == '/') p++; @@ -798,6 +743,7 @@ no_setclientid: return status; out_unlock: up_write(&clp->cl_sem); +out: return status; } @@ -1724,6 +1670,57 @@ nfs4_request_compatible(struct nfs_page *req, struct file *filp, struct page *pa return 1; } +int +nfs4_proc_setclientid(struct nfs4_client *clp, + u32 program, unsigned short port) +{ + u32 *p; + struct nfs4_setclientid setclientid; + struct timespec tv; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID], + .rpc_argp = &setclientid, + .rpc_resp = clp, + .rpc_cred = clp->cl_cred, + }; + + tv = CURRENT_TIME; + p = (u32*)setclientid.sc_verifier.data; + *p++ = (u32)tv.tv_sec; + *p = (u32)tv.tv_nsec; + setclientid.sc_name = clp->cl_ipaddr; + sprintf(setclientid.sc_netid, "tcp"); + sprintf(setclientid.sc_uaddr, "%s.%d.%d", clp->cl_ipaddr, port >> 8, port & 255); + setclientid.sc_prog = htonl(program); + setclientid.sc_cb_ident = 0; + + return rpc_call_sync(clp->cl_rpcclient, &msg, 0); +} + +int +nfs4_proc_setclientid_confirm(struct nfs4_client *clp) +{ + struct nfs_fsinfo fsinfo; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM], + .rpc_argp = clp, + .rpc_resp = &fsinfo, + .rpc_cred = clp->cl_cred, + }; + unsigned long now; + int status; + + now = jiffies; + status = rpc_call_sync(clp->cl_rpcclient, &msg, 0); + if (status == 0) { + spin_lock(&clp->cl_lock); + clp->cl_lease_time = fsinfo.lease_time * HZ; + clp->cl_last_renewal = now; + spin_unlock(&clp->cl_lock); + } + return status; +} + struct nfs_rpc_ops nfs_v4_clientops = { .version = 4, /* protocol version */ .getroot = nfs4_proc_get_root, diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index ed79661601dd..cb96666b3831 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -73,6 +73,8 @@ extern int nfs_stat_to_errno(int); #define encode_putfh_maxsz op_encode_hdr_maxsz + 1 + \ (NFS4_FHSIZE >> 2) #define decode_putfh_maxsz op_decode_hdr_maxsz +#define encode_putrootfh_maxsz op_encode_hdr_maxsz +#define decode_putrootfh_maxsz op_decode_hdr_maxsz #define encode_getfh_maxsz op_encode_hdr_maxsz #define decode_getfh_maxsz op_decode_hdr_maxsz + 1 + \ (NFS4_FHSIZE >> 2) @@ -94,6 +96,21 @@ extern int nfs_stat_to_errno(int); #define decode_fsinfo_maxsz op_decode_hdr_maxsz + 11 #define encode_renew_maxsz op_encode_hdr_maxsz + 3 #define decode_renew_maxsz op_decode_hdr_maxsz +#define encode_setclientid_maxsz \ + op_encode_hdr_maxsz + \ + 4 /*server->ip_addr*/ + \ + 1 /*Netid*/ + \ + 6 /*uaddr*/ + \ + 6 + (NFS4_VERIFIER_SIZE >> 2) +#define decode_setclientid_maxsz \ + op_decode_hdr_maxsz + \ + 2 + \ + 1024 /* large value for CLID_INUSE */ +#define encode_setclientid_confirm_maxsz \ + op_encode_hdr_maxsz + \ + 3 + (NFS4_VERIFIER_SIZE >> 2) +#define decode_setclientid_confirm_maxsz \ + op_decode_hdr_maxsz #define NFS4_enc_compound_sz 1024 /* XXX: large enough? */ #define NFS4_dec_compound_sz 1024 /* XXX: large enough? */ @@ -173,6 +190,20 @@ extern int nfs_stat_to_errno(int); encode_renew_maxsz #define NFS4_dec_renew_sz compound_decode_hdr_maxsz + \ decode_renew_maxsz +#define NFS4_enc_setclientid_sz compound_encode_hdr_maxsz + \ + encode_setclientid_maxsz +#define NFS4_dec_setclientid_sz compound_decode_hdr_maxsz + \ + decode_setclientid_maxsz +#define NFS4_enc_setclientid_confirm_sz \ + compound_encode_hdr_maxsz + \ + encode_setclientid_confirm_maxsz + \ + encode_putrootfh_maxsz + \ + encode_fsinfo_maxsz +#define NFS4_dec_setclientid_confirm_sz \ + compound_decode_hdr_maxsz + \ + decode_setclientid_confirm_maxsz + \ + decode_putrootfh_maxsz + \ + decode_fsinfo_maxsz static struct { @@ -918,12 +949,6 @@ encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs case OP_SAVEFH: status = encode_savefh(xdr); break; - case OP_SETCLIENTID: - status = encode_setclientid(xdr, &cp->ops[i].u.setclientid); - break; - case OP_SETCLIENTID_CONFIRM: - status = encode_setclientid_confirm(xdr, cp->ops[i].u.setclientid_confirm); - break; default: BUG(); } @@ -1187,6 +1212,46 @@ nfs4_xdr_enc_renew(struct rpc_rqst *req, uint32_t *p, struct nfs4_client *clp) return encode_renew(&xdr, clp); } +/* + * a SETCLIENTID request + */ +static int +nfs4_xdr_enc_setclientid(struct rpc_rqst *req, uint32_t *p, + struct nfs4_setclientid *sc) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 1, + }; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + return encode_setclientid(&xdr, sc); +} + +/* + * a SETCLIENTID_CONFIRM request + */ +static int +nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, + struct nfs4_client *clp) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 3, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_setclientid_confirm(&xdr, clp); + if (!status) + status = encode_putrootfh(&xdr); + if (!status) + status = encode_fsinfo(&xdr); + return status; +} + /* * START OF "GENERIC" DECODE ROUTINES. * These may look a little ugly since they are imported from a "generic" @@ -2098,7 +2163,7 @@ decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res) } static int -decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid *setclientid) +decode_setclientid(struct xdr_stream *xdr, struct nfs4_client *clp) { uint32_t *p; uint32_t opnum; @@ -2114,9 +2179,9 @@ decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid *setclientid) } READ32(nfserr); if (nfserr == NFS_OK) { - READ_BUF(8 + sizeof(setclientid->sc_state->cl_confirm.data)); - READ64(setclientid->sc_state->cl_clientid); - COPYMEM(setclientid->sc_state->cl_confirm.data, sizeof(setclientid->sc_state->cl_confirm.data)); + READ_BUF(8 + sizeof(clp->cl_confirm.data)); + READ64(clp->cl_clientid); + COPYMEM(clp->cl_confirm.data, sizeof(clp->cl_confirm.data)); } else if (nfserr == NFSERR_CLID_INUSE) { uint32_t len; @@ -2231,12 +2296,6 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs case OP_SAVEFH: status = decode_savefh(xdr); break; - case OP_SETCLIENTID: - status = decode_setclientid(xdr, &op->u.setclientid); - break; - case OP_SETCLIENTID_CONFIRM: - status = decode_setclientid_confirm(xdr); - break; default: BUG(); return -EIO; @@ -2513,6 +2572,49 @@ nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, uint32_t *p, void *dummy) return status; } +/* + * a SETCLIENTID request + */ +static int +nfs4_xdr_dec_setclientid(struct rpc_rqst *req, uint32_t *p, + struct nfs4_client *clp) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_setclientid(&xdr, clp); + if (!status) + status = -nfs_stat_to_errno(hdr.status); + return status; +} + +/* + * a SETCLIENTID_CONFIRM request + */ +static int +nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, struct nfs_fsinfo *fsinfo) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_setclientid_confirm(&xdr); + if (!status) + status = decode_putrootfh(&xdr); + if (!status) + status = decode_fsinfo(&xdr, fsinfo); + if (!status) + status = -nfs_stat_to_errno(hdr.status); + return status; +} + uint32_t * nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) { @@ -2571,6 +2673,8 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(SETATTR, enc_setattr, dec_setattr), PROC(FSINFO, enc_fsinfo, dec_fsinfo), PROC(RENEW, enc_renew, dec_renew), + PROC(SETCLIENTID, enc_setclientid, dec_setclientid), + PROC(SETCLIENTID_CONFIRM, enc_setclientid_confirm, dec_setclientid_confirm), }; struct rpc_version nfs_version4 = { diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index d535a46f7d4f..a601ae91de21 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -223,6 +223,8 @@ enum { NFSPROC4_CLNT_SETATTR, NFSPROC4_CLNT_FSINFO, NFSPROC4_CLNT_RENEW, + NFSPROC4_CLNT_SETCLIENTID, + NFSPROC4_CLNT_SETCLIENTID_CONFIRM, }; #endif diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 4996221041a4..fcaf3322cadf 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -552,6 +552,8 @@ struct nfs4_state { /* nfs4proc.c */ +extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); +extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 09c2dd2216d0..244a430f83ef 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -555,8 +555,6 @@ struct nfs4_op { struct nfs4_rename rename; struct nfs4_client * renew; struct nfs4_setattr setattr; - struct nfs4_setclientid setclientid; - struct nfs4_client * setclientid_confirm; } u; }; -- cgit v1.2.3 From 2d5e9ebc702452d30ca0a9c50b9e3f0d8a56a4e9 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:57:13 +0100 Subject: NFSv4: Don't translate those NFSv4 errors that are needed by the kernel itself into EIO. Fix a signed/unsigned bug in nfs4_increment_seqid. --- fs/nfs/nfs4state.c | 4 +-- fs/nfs/nfs4xdr.c | 63 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/nfs4.h | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs_fs.h | 2 +- 4 files changed, 135 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index bded88b8d3eb..3bdb3a016e25 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -376,9 +376,9 @@ nfs4_put_open_state(struct nfs4_state *state) * see comments nfs_fs.h:seqid_mutating_error() */ void -nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp) +nfs4_increment_seqid(int status, struct nfs4_state_owner *sp) { - if (status == NFS_OK || seqid_mutating_err(status)) + if (status == NFS_OK || seqid_mutating_err(-status)) sp->so_seqid++; } diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index cb96666b3831..67a8e7cf403b 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -57,7 +57,7 @@ /* Mapping from NFS error code to "errno" error code. */ #define errno_NFSERR_IO EIO -extern int nfs_stat_to_errno(int); +static int nfs_stat_to_errno(int); /* NFSv4 COMPOUND tags are only wanted for debugging purposes */ #ifdef DEBUG @@ -2650,6 +2650,67 @@ nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) return p; } +/* + * We need to translate between nfs status return values and + * the local errno values which may not be the same. + */ +static struct { + int stat; + int errno; +} nfs_errtbl[] = { + { NFS4_OK, 0 }, + { NFS4ERR_PERM, EPERM }, + { NFS4ERR_NOENT, ENOENT }, + { NFS4ERR_IO, errno_NFSERR_IO }, + { NFS4ERR_NXIO, ENXIO }, + { NFS4ERR_ACCESS, EACCES }, + { NFS4ERR_EXIST, EEXIST }, + { NFS4ERR_XDEV, EXDEV }, + { NFS4ERR_NOTDIR, ENOTDIR }, + { NFS4ERR_ISDIR, EISDIR }, + { NFS4ERR_INVAL, EINVAL }, + { NFS4ERR_FBIG, EFBIG }, + { NFS4ERR_NOSPC, ENOSPC }, + { NFS4ERR_ROFS, EROFS }, + { NFS4ERR_MLINK, EMLINK }, + { NFS4ERR_NAMETOOLONG, ENAMETOOLONG }, + { NFS4ERR_NOTEMPTY, ENOTEMPTY }, + { NFS4ERR_DQUOT, EDQUOT }, + { NFS4ERR_STALE, ESTALE }, + { NFS4ERR_BADHANDLE, EBADHANDLE }, + { NFS4ERR_BAD_COOKIE, EBADCOOKIE }, + { NFS4ERR_NOTSUPP, ENOTSUPP }, + { NFS4ERR_TOOSMALL, ETOOSMALL }, + { NFS4ERR_SERVERFAULT, ESERVERFAULT }, + { NFS4ERR_BADTYPE, EBADTYPE }, + { NFS4ERR_LOCKED, EAGAIN }, + { NFS4ERR_RESOURCE, EREMOTEIO }, + { NFS4ERR_SYMLINK, ELOOP }, + { NFS4ERR_OP_ILLEGAL, EOPNOTSUPP }, + { NFS4ERR_DEADLOCK, EDEADLK }, + { -1, EIO } +}; + +/* + * Convert an NFS error code to a local one. + * This one is used jointly by NFSv2 and NFSv3. + */ +static int +nfs_stat_to_errno(int stat) +{ + int i; + for (i = 0; nfs_errtbl[i].stat != -1; i++) { + if (nfs_errtbl[i].stat == stat) + return nfs_errtbl[i].errno; + } + /* If we cannot translate the error, the recovery routines should + * handle it. + * Note: remaining NFSv4 error codes have values > 10000, so should + * not conflict with native Linux error codes. + */ + return stat; +} + #ifndef MAX # define MAX(a, b) (((a) > (b))? (a) : (b)) #endif diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index a601ae91de21..f56601a5825d 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -88,6 +88,76 @@ enum nfs_opnum4 { OP_WRITE = 38, }; +enum nfsstat4 { + NFS4_OK = 0, + NFS4ERR_PERM = 1, + NFS4ERR_NOENT = 2, + NFS4ERR_IO = 5, + NFS4ERR_NXIO = 6, + NFS4ERR_ACCESS = 13, + NFS4ERR_EXIST = 17, + NFS4ERR_XDEV = 18, + /* Unused/reserved 19 */ + NFS4ERR_NOTDIR = 20, + NFS4ERR_ISDIR = 21, + NFS4ERR_INVAL = 22, + NFS4ERR_FBIG = 27, + NFS4ERR_NOSPC = 28, + NFS4ERR_ROFS = 30, + NFS4ERR_MLINK = 31, + NFS4ERR_NAMETOOLONG = 63, + NFS4ERR_NOTEMPTY = 66, + NFS4ERR_DQUOT = 69, + NFS4ERR_STALE = 70, + NFS4ERR_BADHANDLE = 10001, + NFS4ERR_BAD_COOKIE = 10003, + NFS4ERR_NOTSUPP = 10004, + NFS4ERR_TOOSMALL = 10005, + NFS4ERR_SERVERFAULT = 10006, + NFS4ERR_BADTYPE = 10007, + NFS4ERR_DELAY = 10008, + NFS4ERR_SAME = 10009, + NFS4ERR_DENIED = 10010, + NFS4ERR_EXPIRED = 10011, + NFS4ERR_LOCKED = 10012, + NFS4ERR_GRACE = 10013, + NFS4ERR_FHEXPIRED = 10014, + NFS4ERR_SHARE_DENIED = 10015, + NFS4ERR_WRONGSEC = 10016, + NFS4ERR_CLID_INUSE = 10017, + NFS4ERR_RESOURCE = 10018, + NFS4ERR_MOVED = 10019, + NFS4ERR_NOFILEHANDLE = 10020, + NFS4ERR_MINOR_VERS_MISMATCH = 10021, + NFS4ERR_STALE_CLIENTID = 10022, + NFS4ERR_STALE_STATEID = 10023, + NFS4ERR_OLD_STATEID = 10024, + NFS4ERR_BAD_STATEID = 10025, + NFS4ERR_BAD_SEQID = 10026, + NFS4ERR_NOT_SAME = 10027, + NFS4ERR_LOCK_RANGE = 10028, + NFS4ERR_SYMLINK = 10029, + NFS4ERR_RESTOREFH = 10030, + NFS4ERR_LEASE_MOVED = 10031, + NFS4ERR_ATTRNOTSUPP = 10032, + NFS4ERR_NO_GRACE = 10033, + NFS4ERR_RECLAIM_BAD = 10034, + NFS4ERR_RECLAIM_CONFLICT = 10035, + NFS4ERR_BADXDR = 10036, + NFS4ERR_LOCKS_HELD = 10037, + NFS4ERR_OPENMODE = 10038, + NFS4ERR_BADOWNER = 10039, + NFS4ERR_BADCHAR = 10040, + NFS4ERR_BADNAME = 10041, + NFS4ERR_BAD_RANGE = 10042, + NFS4ERR_LOCK_NOTSUPP = 10043, + NFS4ERR_OP_ILLEGAL = 10044, + NFS4ERR_DEADLOCK = 10045, + NFS4ERR_FILE_OPEN = 10046, + NFS4ERR_ADMIN_REVOKED = 10047, + NFS4ERR_CB_PATH_DOWN = 10048 +}; + /* * Note: NF4BAD is not actually part of the protocol; it is just used * internally by nfsd. diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index fcaf3322cadf..a005c999ce43 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -571,7 +571,7 @@ extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struc extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); -extern void nfs4_increment_seqid(u32 status, struct nfs4_state_owner *sp); +extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); struct nfs4_mount_data; #else -- cgit v1.2.3 From e85c40cde9d47156479a28710becc39becf6fe24 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:57:57 +0100 Subject: NFSv4: Preparation for the server reboot recovery code. --- fs/nfs/nfs4proc.c | 55 ++++++++++++++++++++++++++-- fs/nfs/nfs4state.c | 89 +++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4xdr.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs4.h | 1 + include/linux/nfs_fs.h | 2 + include/linux/nfs_xdr.h | 13 +++++++ 6 files changed, 253 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 8e8ac9cc08f6..5e52e7d218f8 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -458,6 +458,54 @@ process_cinfo(struct nfs4_change_info *info, struct nfs_fattr *fattr) } } +/* + * OPEN_RECLAIM: + * reclaim state on the server after a reboot. + * Assumes caller is holding the sp->so_sem + */ +int +nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) +{ + struct inode *inode = state->inode; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_fattr fattr = { + .valid = 0, + }; + struct nfs4_change_info d_cinfo; + struct nfs4_getattr f_getattr = { + .gt_bmval = nfs4_fattr_bitmap, + .gt_attrs = &fattr, + }; + + struct nfs_open_reclaimargs o_arg = { + .fh = NFS_FH(inode), + .seqid = sp->so_seqid, + .id = sp->so_id, + .share_access = state->state, + .clientid = server->nfs4_state->cl_clientid, + .claim = NFS4_OPEN_CLAIM_PREVIOUS, + .f_getattr = &f_getattr, + }; + struct nfs_openres o_res = { + .cinfo = &d_cinfo, + .f_getattr = &f_getattr, + .server = server, /* Grrr */ + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_RECLAIM], + .rpc_argp = &o_arg, + .rpc_resp = &o_res, + .rpc_cred = sp->so_cred, + }; + int status; + + status = rpc_call_sync(server->client, &msg, 0); + nfs4_increment_seqid(status, sp); + /* Update the inode attributes */ + nfs_refresh_inode(inode, &fattr); + return status; +} + struct nfs4_state * nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred) { @@ -523,10 +571,9 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt o_arg.id = sp->so_id; status = rpc_call_sync(server->client, &msg, 0); - if (status) { - goto out_up; - } nfs4_increment_seqid(status, sp); + if (status) + goto out_up; process_cinfo(&d_cinfo, &d_attr); nfs_refresh_inode(dir, &d_attr); @@ -555,9 +602,9 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt memcpy(&oc_arg.stateid, &o_res.stateid, sizeof(oc_arg.stateid)); status = rpc_call_sync(server->client, &msg, 0); + nfs4_increment_seqid(status, sp); if (status) goto out_up; - nfs4_increment_seqid(status, sp); memcpy(&state->stateid, &oc_res.stateid, sizeof(state->stateid)); } else memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 3bdb3a016e25..8738414bd8ea 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -382,6 +382,95 @@ nfs4_increment_seqid(int status, struct nfs4_state_owner *sp) sp->so_seqid++; } +static int reclaimer(void *); +struct reclaimer_args { + struct nfs4_client *clp; + struct completion complete; +}; + +/* + * State recovery routine + */ +void +nfs4_recover_state(struct nfs4_client *clp) +{ + struct reclaimer_args args = { + .clp = clp, + }; + init_completion(&args.complete); + + down_read(&clp->cl_sem); + if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0) + goto out_failed; + wait_for_completion(&args.complete); + return; +out_failed: + up_read(&clp->cl_sem); +} + +static void +nfs4_reclaim_open_state(struct nfs4_state_owner *sp) +{ + struct nfs4_state *state; + int status; + + list_for_each_entry(state, &sp->so_states, open_states) { + status = nfs4_open_reclaim(sp, state); + if (status) { + /* + * Open state on this file cannot be recovered + * All we can do is revert to using the zero stateid. + */ + memset(state->stateid.data, 0, + sizeof(state->stateid.data)); + /* Mark the file as being 'closed' */ + state->state = 0; + } + } +} + +static int +reclaimer(void *ptr) +{ + struct reclaimer_args *args = (struct reclaimer_args *)ptr; + struct nfs4_client *clp = args->clp; + struct nfs4_state_owner *sp; + int status; + + daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr)); + allow_signal(SIGKILL); + + complete(&args->complete); + + /* Are there any NFS mounts out there? */ + if (list_empty(&clp->cl_superblocks)) + goto out; + status = nfs4_proc_setclientid(clp, 0, 0); + if (status) + goto out_error; + status = nfs4_proc_setclientid_confirm(clp); + if (status) + goto out_error; + spin_lock(&clp->cl_lock); + list_for_each_entry(sp, &clp->cl_state_owners, so_list) { + atomic_inc(&sp->so_count); + spin_unlock(&clp->cl_lock); + down(&sp->so_sema); + nfs4_reclaim_open_state(sp); + up(&sp->so_sema); + nfs4_put_state_owner(sp); + spin_lock(&clp->cl_lock); + } + spin_unlock(&clp->cl_lock); +out: + up_read(&clp->cl_sem); + return 0; +out_error: + printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u\n", + NIPQUAD(clp->cl_addr.s_addr)); + goto out; +} + /* * Local variables: * c-basic-offset: 8 diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 67a8e7cf403b..16296618a231 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -166,6 +166,16 @@ static int nfs_stat_to_errno(int); #define NFS4_dec_open_confirm_sz compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 4 +#define NFS4_enc_open_reclaim_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + op_encode_hdr_maxsz + \ + 11 + \ + encode_getattr_maxsz +#define NFS4_dec_open_reclaim_sz compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + \ + 4 + 5 + 2 + 3 + \ + decode_getattr_maxsz #define NFS4_enc_close_sz compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 5 @@ -666,6 +676,41 @@ encode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmargs *arg) } +static int +encode_open_reclaim(struct xdr_stream *xdr, struct nfs_open_reclaimargs *arg) +{ + uint32_t *p; + + /* + * opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4, + * owner 4, opentype 4, claim 4, delegation_type 4 = 44 + */ + RESERVE_SPACE(44); + WRITE32(OP_OPEN); + WRITE32(arg->seqid); + switch (arg->share_access) { + case FMODE_READ: + WRITE32(NFS4_SHARE_ACCESS_READ); + break; + case FMODE_WRITE: + WRITE32(NFS4_SHARE_ACCESS_WRITE); + break; + case FMODE_READ|FMODE_WRITE: + WRITE32(NFS4_SHARE_ACCESS_BOTH); + break; + default: + BUG(); + } + WRITE32(0); /* for linux, share_deny = 0 always */ + WRITE64(arg->clientid); + WRITE32(4); + WRITE32(arg->id); + WRITE32(NFS4_OPEN_NOCREATE); + WRITE32(NFS4_OPEN_CLAIM_PREVIOUS); + WRITE32(NFS4_OPEN_DELEGATE_NONE); + return 0; +} + static int encode_putfh(struct xdr_stream *xdr, struct nfs_fh *fh) { @@ -1058,6 +1103,32 @@ out: return status; } +/* + * Encode an OPEN request + */ +static int +nfs4_xdr_enc_open_reclaim(struct rpc_rqst *req, uint32_t *p, + struct nfs_open_reclaimargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 3, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if (status) + goto out; + status = encode_open_reclaim(&xdr, args); + if (status) + goto out; + status = encode_getattr(&xdr, args->f_getattr); +out: + return status; +} + /* * Encode a READ request @@ -2417,6 +2488,31 @@ out: return status; } +/* + * Decode OPEN_RECLAIM response + */ +static int +nfs4_xdr_dec_open_reclaim(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_open(&xdr, res); + if (status) + goto out; + status = decode_getattr(&xdr, res->f_getattr, res->server); +out: + return status; +} + /* * Decode SETATTR response */ @@ -2730,6 +2826,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(COMMIT, enc_commit, dec_commit), PROC(OPEN, enc_open, dec_open), PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm), + PROC(OPEN_RECLAIM, enc_open_reclaim, dec_open_reclaim), PROC(CLOSE, enc_close, dec_close), PROC(SETATTR, enc_setattr, dec_setattr), PROC(FSINFO, enc_fsinfo, dec_fsinfo), diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index f56601a5825d..a6f2d563b605 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -289,6 +289,7 @@ enum { NFSPROC4_CLNT_COMMIT, NFSPROC4_CLNT_OPEN, NFSPROC4_CLNT_OPEN_CONFIRM, + NFSPROC4_CLNT_OPEN_RECLAIM, NFSPROC4_CLNT_CLOSE, NFSPROC4_CLNT_SETATTR, NFSPROC4_CLNT_FSINFO, diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index a005c999ce43..b43412088372 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -554,6 +554,7 @@ struct nfs4_state { /* nfs4proc.c */ extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); +extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); @@ -572,6 +573,7 @@ extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); +extern void nfs4_recover_state(struct nfs4_client *); struct nfs4_mount_data; #else diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 244a430f83ef..242b50436d92 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -133,6 +133,19 @@ struct nfs_open_confirmres { nfs4_stateid stateid; }; +/* + * Arguments to the open_reclaim call. + */ +struct nfs_open_reclaimargs { + struct nfs_fh * fh; + __u64 clientid; + __u32 seqid; + __u32 id; + __u32 share_access; + __u32 claim; + struct nfs4_getattr * f_getattr; +}; + /* * Arguments to the close call. */ -- cgit v1.2.3 From dce9f3bf3f0af4a22eb06822a74e2de84f98cd57 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:58:38 +0100 Subject: NFSv4: Basic code for recovering file OPEN state after a server reboot. --- fs/nfs/inode.c | 2 + fs/nfs/nfs4proc.c | 235 ++++++++++++++++++++++++++++++++++++++++--------- fs/nfs/nfs4state.c | 129 +++++++++++++++++++++++---- include/linux/nfs_fs.h | 14 ++- 4 files changed, 317 insertions(+), 63 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 2c1df6550feb..767cd32e7a36 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -1448,6 +1448,8 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); } + if (list_empty(&clp->cl_superblocks)) + clear_bit(NFS4CLNT_OK, &clp->cl_state); list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); clnt = rpc_clone_client(clp->cl_rpcclient); server->nfs4_state = clp; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5e52e7d218f8..841934fafaee 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -48,9 +48,12 @@ #define NFSDBG_FACILITY NFSDBG_PROC +#define NFS4_POLL_RETRY_TIME (15*HZ) + #define GET_OP(cp,name) &cp->ops[cp->req_nops].u.name #define OPNUM(cp) cp->ops[cp->req_nops].opnum +static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *); extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus); extern struct rpc_procinfo nfs4_procedures[]; @@ -532,7 +535,6 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt struct nfs_openargs o_arg = { .fh = NFS_FH(dir), .share_access = flags & (FMODE_READ|FMODE_WRITE), - .clientid = NFS_SERVER(dir)->nfs4_state->cl_clientid, .opentype = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE, .createmode = (flags & O_EXCL) ? NFS4_CREATE_EXCLUSIVE : NFS4_CREATE_UNCHECKED, .name = name, @@ -553,6 +555,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt .rpc_cred = cred, }; +retry: status = -ENOMEM; if (!(sp = nfs4_get_state_owner(NFS_SERVER(dir), cred))) { dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n"); @@ -569,6 +572,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt down(&sp->so_sema); o_arg.seqid = sp->so_seqid; o_arg.id = sp->so_id; + o_arg.clientid = NFS_SERVER(dir)->nfs4_state->cl_clientid, status = rpc_call_sync(server->client, &msg, 0); nfs4_increment_seqid(status, sp); @@ -623,6 +627,9 @@ out_up: nfs4_put_open_state(state); if (inode) iput(inode); + status = nfs4_handle_error(server, status); + if (!status) + goto retry; out: return ERR_PTR(status); } @@ -651,7 +658,9 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, .rpc_argp = &arg, .rpc_resp = &res, }; + int status; +retry: fattr->valid = 0; if (state) @@ -659,7 +668,13 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, else memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); - return(rpc_call_sync(server->client, &msg, 0)); + status = rpc_call_sync(server->client, &msg, 0); + if (status) { + status = nfs4_handle_error(server, status); + if (!status) + goto retry; + } + return status; } /* @@ -707,48 +722,12 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { - struct nfs4_client *clp; struct nfs4_compound compound; struct nfs4_op ops[4]; unsigned char * p; struct qstr q; int status; - clp = server->nfs4_state; - - down_write(&clp->cl_sem); - /* Has the clientid already been initialized? */ - if (clp->cl_state != NFS4CLNT_NEW) - /* Yep, so just read the root attributes and the lease time. */ - goto no_setclientid; - - /* - * SETCLIENTID. - * Until delegations are imported, we don't bother setting the program - * number and port to anything meaningful. - */ - if ((status = nfs4_proc_setclientid(clp, 0, 0))) - goto out_unlock; - - /* - * SETCLIENTID_CONFIRM, plus root filehandle. - * We also get the lease time here. - */ - if ((status = nfs4_proc_setclientid_confirm(clp))) - goto out_unlock; - - /* - * Now that we have instantiated the clientid and determined - * the lease time, we can initialize the renew daemon for this - * server. - * FIXME: we only need one renewd daemon per server. - */ - nfs4_schedule_state_renewal(clp); - clp->cl_state = NFS4CLNT_OK; - -no_setclientid: - up_write(&clp->cl_sem); - /* * Now we do a separate LOOKUP for each component of the mount path. * The LOOKUPs are done separately so that we can conveniently @@ -787,9 +766,6 @@ no_setclientid: } break; } - return status; -out_unlock: - up_write(&clp->cl_sem); out: return status; } @@ -1410,6 +1386,20 @@ nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, return nfs4_call_compound(&compound, NULL, 0); } +static void +nfs4_restart_read(struct rpc_task *task) +{ + struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; + struct nfs_page *req; + + rpc_restart_call(task); + req = nfs_list_entry(data->pages.next); + if (req->wb_state) + memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + else + memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); +} + static void nfs4_read_done(struct rpc_task *task) { @@ -1417,6 +1407,10 @@ nfs4_read_done(struct rpc_task *task) struct inode *inode = data->inode; struct nfs_fattr *fattr = data->res.fattr; + if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { + task->tk_action = nfs4_restart_read; + return; + } if (task->tk_status > 0) renew_lease(NFS_SERVER(inode), data->timestamp); /* Check cache consistency */ @@ -1484,12 +1478,30 @@ nfs4_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) inode->i_mtime = fattr->mtime; } +static void +nfs4_restart_write(struct rpc_task *task) +{ + struct nfs_write_data *data = (struct nfs_write_data *)task->tk_calldata; + struct nfs_page *req; + + rpc_restart_call(task); + req = nfs_list_entry(data->pages.next); + if (req->wb_state) + memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + else + memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); +} + static void nfs4_write_done(struct rpc_task *task) { struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct inode *inode = data->inode; + if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { + task->tk_action = nfs4_restart_write; + return; + } if (task->tk_status >= 0) renew_lease(NFS_SERVER(inode), data->timestamp); nfs4_write_refresh_inode(inode, data->res.fattr); @@ -1552,8 +1564,13 @@ static void nfs4_commit_done(struct rpc_task *task) { struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; + struct inode *inode = data->inode; - nfs4_write_refresh_inode(data->inode, data->res.fattr); + if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { + task->tk_action = nfs4_restart_write; + return; + } + nfs4_write_refresh_inode(inode, data->res.fattr); /* Call back common NFS writeback processing */ nfs_commit_done(task); } @@ -1599,6 +1616,14 @@ renew_done(struct rpc_task *task) { struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp; unsigned long timestamp = (unsigned long)task->tk_calldata; + + if (task->tk_status < 0) { + switch (task->tk_status) { + case -NFS4ERR_STALE_CLIENTID: + nfs4_schedule_state_recovery(clp); + return; + } + } spin_lock(&clp->cl_lock); if (time_before(clp->cl_last_renewal,timestamp)) clp->cl_last_renewal = timestamp; @@ -1617,6 +1642,25 @@ nfs4_proc_async_renew(struct nfs4_client *clp) return rpc_call_async(clp->cl_rpcclient, &msg, 0, renew_done, (void *)jiffies); } +int +nfs4_proc_renew(struct nfs4_client *clp) +{ + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], + .rpc_argp = clp, + .rpc_cred = clp->cl_cred, + }; + unsigned long now = jiffies; + int status; + + status = rpc_call_sync(clp->cl_rpcclient, &msg, 0); + spin_lock(&clp->cl_lock); + if (time_before(clp->cl_last_renewal,now)) + clp->cl_last_renewal = now; + spin_unlock(&clp->cl_lock); + return status; +} + /* * We will need to arrange for the VFS layer to provide an atomic open. * Until then, this open method is prone to inefficiency and race conditions @@ -1697,6 +1741,113 @@ nfs4_request_init(struct nfs_page *req, struct file *filp) req->wb_cred = get_rpccred(state->owner->so_cred); } +static int +nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server) +{ + struct nfs4_client *clp = server->nfs4_state; + + if (!clp) + return 0; + switch(task->tk_status) { + case -NFS4ERR_STALE_CLIENTID: + case -NFS4ERR_STALE_STATEID: + case -NFS4ERR_EXPIRED: + rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL, NULL); + nfs4_schedule_state_recovery(clp); + task->tk_status = 0; + return -EAGAIN; + case -NFS4ERR_GRACE: + case -NFS4ERR_DELAY: + rpc_delay(task, NFS4_POLL_RETRY_TIME); + task->tk_status = 0; + return -EAGAIN; + } + return 0; +} + +int +nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp) +{ + DEFINE_WAIT(wait); + sigset_t oldset; + int interruptible, res; + + might_sleep(); + + rpc_clnt_sigmask(clnt, &oldset); + interruptible = TASK_UNINTERRUPTIBLE; + if (clnt->cl_intr) + interruptible = TASK_INTERRUPTIBLE; + do { + res = 0; + prepare_to_wait(&clp->cl_waitq, &wait, interruptible); + nfs4_schedule_state_recovery(clp); + if (test_bit(NFS4CLNT_OK, &clp->cl_state) && + !test_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state)) + break; + if (clnt->cl_intr && signalled()) { + res = -ERESTARTSYS; + break; + } + schedule(); + } while(!test_bit(NFS4CLNT_OK, &clp->cl_state)); + finish_wait(&clp->cl_waitq, &wait); + rpc_clnt_sigunmask(clnt, &oldset); + return res; +} + +static int +nfs4_delay(struct rpc_clnt *clnt) +{ + sigset_t oldset; + int res = 0; + + might_sleep(); + + rpc_clnt_sigmask(clnt, &oldset); + if (clnt->cl_intr) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(NFS4_POLL_RETRY_TIME); + if (signalled()) + res = -ERESTARTSYS; + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(NFS4_POLL_RETRY_TIME); + } + rpc_clnt_sigunmask(clnt, &oldset); + return res; +} + +/* This is the error handling routine for processes that are allowed + * to sleep. + */ +int +nfs4_handle_error(struct nfs_server *server, int errorcode) +{ + struct nfs4_client *clp = server->nfs4_state; + int ret = errorcode; + + switch(errorcode) { + case -NFS4ERR_STALE_CLIENTID: + case -NFS4ERR_STALE_STATEID: + case -NFS4ERR_EXPIRED: + ret = nfs4_wait_clnt_recover(server->client, clp); + break; + case -NFS4ERR_GRACE: + case -NFS4ERR_DELAY: + ret = nfs4_delay(server->client); + break; + default: + if (errorcode <= -1000) { + printk(KERN_WARNING "%s could not handle NFSv4 error %d\n", + __FUNCTION__, -errorcode); + ret = -EIO; + } + } + /* We failed to handle the error */ + return ret; +} + static int nfs4_request_compatible(struct nfs_page *req, struct file *filp, struct page *page) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 8738414bd8ea..36f3f7e4c0ef 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -56,6 +56,7 @@ nfs4_stateid one_stateid = static LIST_HEAD(nfs4_clientid_list); +static void nfs4_recover_state(void *); extern void nfs4_renew_state(void *); void @@ -98,9 +99,12 @@ nfs4_alloc_client(struct in_addr *addr) INIT_LIST_HEAD(&clp->cl_unused); spin_lock_init(&clp->cl_lock); atomic_set(&clp->cl_count, 1); + INIT_WORK(&clp->cl_recoverd, nfs4_recover_state, clp); INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp); INIT_LIST_HEAD(&clp->cl_superblocks); - clp->cl_state = NFS4CLNT_NEW; + init_waitqueue_head(&clp->cl_waitq); + INIT_RPC_WAITQ(&clp->cl_rpcwaitq, "NFS4 client"); + clp->cl_state = 1 << NFS4CLNT_NEW; } return clp; } @@ -155,6 +159,9 @@ nfs4_put_client(struct nfs4_client *clp) return; list_del(&clp->cl_servers); spin_unlock(&state_spinlock); + BUG_ON(!list_empty(&clp->cl_superblocks)); + wake_up_all(&clp->cl_waitq); + rpc_wake_up(&clp->cl_rpcwaitq); nfs4_kill_renewd(clp); nfs4_free_client(clp); } @@ -175,6 +182,7 @@ nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred) atomic_inc(&sp->so_count); sp->so_cred = cred; list_move(&sp->so_list, &clp->cl_state_owners); + sp->so_generation = clp->cl_generation; clp->cl_nunused--; } return sp; @@ -215,13 +223,17 @@ nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) new->so_client = clp; new->so_id = nfs4_alloc_lockowner_id(clp); new->so_cred = cred; + new->so_generation = clp->cl_generation; sp = new; new = NULL; } spin_unlock(&clp->cl_lock); if (new) kfree(new); - if (!sp) + if (sp) { + if (!test_bit(NFS4CLNT_OK, &clp->cl_state)) + nfs4_wait_clnt_recover(server->client, clp); + } else put_rpccred(cred); return sp; } @@ -353,6 +365,7 @@ nfs4_put_open_state(struct nfs4_state *state) { struct inode *inode = state->inode; struct nfs4_state_owner *owner = state->owner; + int status = 0; if (!atomic_dec_and_lock(&state->count, &inode->i_lock)) return; @@ -360,8 +373,16 @@ nfs4_put_open_state(struct nfs4_state *state) spin_unlock(&inode->i_lock); down(&owner->so_sema); list_del(&state->open_states); - if (state->state != 0) - nfs4_do_close(inode, state); + if (state->state != 0) { + do { + status = nfs4_do_close(inode, state); + if (!status) + break; + up(&owner->so_sema); + status = nfs4_handle_error(NFS_SERVER(inode), status); + down(&owner->so_sema); + } while (!status); + } up(&owner->so_sema); iput(inode); nfs4_free_open_state(state); @@ -392,41 +413,81 @@ struct reclaimer_args { * State recovery routine */ void -nfs4_recover_state(struct nfs4_client *clp) +nfs4_recover_state(void *data) { + struct nfs4_client *clp = (struct nfs4_client *)data; struct reclaimer_args args = { .clp = clp, }; + might_sleep(); + init_completion(&args.complete); down_read(&clp->cl_sem); - if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0) + if (test_and_set_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state)) goto out_failed; + if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0) + goto out_failed_clear; wait_for_completion(&args.complete); return; +out_failed_clear: + smp_mb__before_clear_bit(); + clear_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state); + smp_mb__after_clear_bit(); + wake_up_all(&clp->cl_waitq); + rpc_wake_up(&clp->cl_rpcwaitq); out_failed: up_read(&clp->cl_sem); } -static void +/* + * Schedule a state recovery attempt + */ +void +nfs4_schedule_state_recovery(struct nfs4_client *clp) +{ + if (!clp) + return; + smp_mb__before_clear_bit(); + clear_bit(NFS4CLNT_OK, &clp->cl_state); + smp_mb__after_clear_bit(); + schedule_work(&clp->cl_recoverd); +} + +static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp) { struct nfs4_state *state; - int status; + int status = 0; list_for_each_entry(state, &sp->so_states, open_states) { status = nfs4_open_reclaim(sp, state); - if (status) { - /* - * Open state on this file cannot be recovered - * All we can do is revert to using the zero stateid. - */ - memset(state->stateid.data, 0, + if (status >= 0) + continue; + switch (status) { + default: + printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", + __FUNCTION__, status); + case -NFS4ERR_EXPIRED: + case -NFS4ERR_NO_GRACE: + case -NFS4ERR_RECLAIM_BAD: + case -NFS4ERR_RECLAIM_CONFLICT: + /* + * Open state on this file cannot be recovered + * All we can do is revert to using the zero stateid. + */ + memset(state->stateid.data, 0, sizeof(state->stateid.data)); - /* Mark the file as being 'closed' */ - state->state = 0; + /* Mark the file as being 'closed' */ + state->state = 0; + break; + case -NFS4ERR_STALE_CLIENTID: + goto out_err; } } + return 0; +out_err: + return status; } static int @@ -435,6 +496,7 @@ reclaimer(void *ptr) struct reclaimer_args *args = (struct reclaimer_args *)ptr; struct nfs4_client *clp = args->clp; struct nfs4_state_owner *sp; + int generation; int status; daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr)); @@ -445,29 +507,58 @@ reclaimer(void *ptr) /* Are there any NFS mounts out there? */ if (list_empty(&clp->cl_superblocks)) goto out; + if (!test_bit(NFS4CLNT_NEW, &clp->cl_state)) { + status = nfs4_proc_renew(clp); + if (status == 0) { + set_bit(NFS4CLNT_OK, &clp->cl_state); + goto out; + } + } status = nfs4_proc_setclientid(clp, 0, 0); if (status) goto out_error; status = nfs4_proc_setclientid_confirm(clp); if (status) goto out_error; + generation = ++(clp->cl_generation); + clear_bit(NFS4CLNT_NEW, &clp->cl_state); + set_bit(NFS4CLNT_OK, &clp->cl_state); + up_read(&clp->cl_sem); + nfs4_schedule_state_renewal(clp); +restart_loop: spin_lock(&clp->cl_lock); list_for_each_entry(sp, &clp->cl_state_owners, so_list) { + if (sp->so_generation - generation <= 0) + continue; atomic_inc(&sp->so_count); spin_unlock(&clp->cl_lock); down(&sp->so_sema); - nfs4_reclaim_open_state(sp); + if (sp->so_generation - generation < 0) { + smp_rmb(); + sp->so_generation = clp->cl_generation; + status = nfs4_reclaim_open_state(sp); + } up(&sp->so_sema); nfs4_put_state_owner(sp); - spin_lock(&clp->cl_lock); + if (status < 0) { + if (status == -NFS4ERR_STALE_CLIENTID) + nfs4_schedule_state_recovery(clp); + goto out; + } + goto restart_loop; } spin_unlock(&clp->cl_lock); out: - up_read(&clp->cl_sem); + smp_mb__before_clear_bit(); + clear_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state); + smp_mb__after_clear_bit(); + wake_up_all(&clp->cl_waitq); + rpc_wake_up(&clp->cl_rpcwaitq); return 0; out_error: printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u\n", NIPQUAD(clp->cl_addr.s_addr)); + up_read(&clp->cl_sem); goto out; } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index b43412088372..3efc91b200e6 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -465,6 +465,7 @@ extern void * nfs_root_data(void); enum nfs4_client_state { NFS4CLNT_OK = 0, NFS4CLNT_NEW, + NFS4CLNT_SETUP_STATE, }; /* @@ -475,7 +476,8 @@ struct nfs4_client { struct in_addr cl_addr; /* Server identifier */ u64 cl_clientid; /* constant */ nfs4_verifier cl_confirm; - enum nfs4_client_state cl_state; + unsigned long cl_state; + long cl_generation; u32 cl_lockowner_id; @@ -499,6 +501,10 @@ struct nfs4_client { unsigned long cl_lease_time; unsigned long cl_last_renewal; struct work_struct cl_renewd; + struct work_struct cl_recoverd; + + wait_queue_head_t cl_waitq; + struct rpc_wait_queue cl_rpcwaitq; /* Our own IP address, as a null-terminated string. * This is used to generate the clientid, and the callback address. @@ -523,6 +529,7 @@ struct nfs4_state_owner { u32 so_seqid; /* protected by so_sema */ unsigned int so_flags; /* protected by so_sema */ atomic_t so_count; + long so_generation; struct rpc_cred *so_cred; /* Associated cred */ struct list_head so_states; @@ -556,7 +563,9 @@ extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); extern int nfs4_proc_async_renew(struct nfs4_client *); +extern int nfs4_proc_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); +extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *); /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs4_client *); @@ -573,7 +582,8 @@ extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); -extern void nfs4_recover_state(struct nfs4_client *); +extern int nfs4_handle_error(struct nfs_server *, int); +extern void nfs4_schedule_state_recovery(struct nfs4_client *); struct nfs4_mount_data; #else -- cgit v1.2.3 From 2642498f5cd2b90e55736e8cfd312c05f63301e3 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 16:59:20 +0100 Subject: RPC/NFSv4: Allow lease RENEW calls to be soft (i.e. to time out) despite the mount being hard. --- fs/nfs/nfs4proc.c | 3 ++- include/linux/sunrpc/sched.h | 2 ++ net/sunrpc/clnt.c | 4 ++-- net/sunrpc/sched.c | 5 ++++- net/sunrpc/xprt.c | 6 +++--- 5 files changed, 13 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 841934fafaee..f617d0bf5313 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1639,7 +1639,8 @@ nfs4_proc_async_renew(struct nfs4_client *clp) .rpc_cred = clp->cl_cred, }; - return rpc_call_async(clp->cl_rpcclient, &msg, 0, renew_done, (void *)jiffies); + return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT, + renew_done, (void *)jiffies); } int diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 1113d7f3df13..6b8e3eb91513 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -108,6 +108,7 @@ typedef void (*rpc_action)(struct rpc_task *); #define RPC_TASK_ROOTCREDS 0x0040 /* force root creds */ #define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */ #define RPC_TASK_KILLED 0x0100 /* task was killed */ +#define RPC_TASK_SOFT 0x0200 /* Use soft timeouts */ #define RPC_IS_ASYNC(t) ((t)->tk_flags & RPC_TASK_ASYNC) #define RPC_IS_SETUID(t) ((t)->tk_flags & RPC_TASK_SETUID) @@ -117,6 +118,7 @@ typedef void (*rpc_action)(struct rpc_task *); #define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED) #define RPC_IS_ACTIVATED(t) ((t)->tk_active) #define RPC_DO_CALLBACK(t) ((t)->tk_callback != NULL) +#define RPC_IS_SOFT(t) ((t)->tk_flags & RPC_TASK_SOFT) #define RPC_TASK_SLEEPING 0 #define RPC_TASK_RUNNING 1 diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 6c6a8310000a..b41434a36009 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -798,7 +798,7 @@ call_timeout(struct rpc_task *task) to->to_retries = clnt->cl_timeout.to_retries; dprintk("RPC: %4d call_timeout (major)\n", task->tk_pid); - if (clnt->cl_softrtry) { + if (RPC_IS_SOFT(task)) { if (clnt->cl_chatty) printk(KERN_NOTICE "%s: server %s not responding, timed out\n", clnt->cl_protname, clnt->cl_server); @@ -841,7 +841,7 @@ call_decode(struct rpc_task *task) } if (task->tk_status < 12) { - if (!clnt->cl_softrtry) { + if (!RPC_IS_SOFT(task)) { task->tk_action = call_bind; clnt->cl_stats->rpcretrans++; goto out_retry; diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 79ebf10e03d9..ad56eb17e80f 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -731,8 +731,11 @@ rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, list_add(&task->tk_task, &all_tasks); spin_unlock(&rpc_sched_lock); - if (clnt) + if (clnt) { atomic_inc(&clnt->cl_users); + if (clnt->cl_softrtry) + task->tk_flags |= RPC_TASK_SOFT; + } #ifdef RPC_DEBUG task->tk_magic = 0xf00baa; diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index e6c5f7ab7968..aa93b824433d 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -488,7 +488,7 @@ xprt_connect(struct rpc_task *task) case -ECONNREFUSED: case -ECONNRESET: case -ENOTCONN: - if (!task->tk_client->cl_softrtry) { + if (!RPC_IS_SOFT(task)) { rpc_delay(task, RPC_REESTABLISH_TIMEOUT); task->tk_status = -ENOTCONN; break; @@ -496,7 +496,7 @@ xprt_connect(struct rpc_task *task) default: /* Report myriad other possible returns. If this file * system is soft mounted, just error out, like Solaris. */ - if (task->tk_client->cl_softrtry) { + if (RPC_IS_SOFT(task)) { printk(KERN_WARNING "RPC: error %d connecting to server %s, exiting\n", -status, task->tk_client->cl_server); @@ -530,7 +530,7 @@ xprt_connect_status(struct rpc_task *task) } /* if soft mounted, just cause this RPC to fail */ - if (task->tk_client->cl_softrtry) + if (RPC_IS_SOFT(task)) task->tk_status = -EIO; switch (task->tk_status) { -- cgit v1.2.3 From ab91d13dcba1d0b69394fa1b0a3b034db71316ca Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 17:00:03 +0100 Subject: RPC: Ensure that we disconnect TCP sockets if there has been no NFS traffic for the last 5 minutes. This code also affects NFSv2/v3. --- include/linux/sunrpc/xprt.h | 7 +++ net/sunrpc/xprt.c | 106 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 90 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 8472b1c5ad2e..393e6dc6a268 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -163,6 +163,12 @@ struct rpc_xprt { tcp_offset; /* fragment offset */ unsigned long tcp_copied, /* copied to request */ tcp_flags; + /* + * Disconnection of idle sockets + */ + struct work_struct task_cleanup; + struct timer_list timer; + unsigned long last_used; /* * Send stuff @@ -202,6 +208,7 @@ int xprt_clear_backlog(struct rpc_xprt *); void xprt_sock_setbufsize(struct rpc_xprt *); #define XPRT_CONNECT 0 +#define XPRT_LOCKED 1 #define xprt_connected(xp) (test_bit(XPRT_CONNECT, &(xp)->sockstate)) #define xprt_set_connected(xp) (set_bit(XPRT_CONNECT, &(xp)->sockstate)) diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index aa93b824433d..84ccc9d8e05b 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,7 @@ #endif #define XPRT_MAX_BACKOFF (8) +#define XPRT_IDLE_TIMEOUT (5*60*HZ) /* * Local functions @@ -139,25 +141,33 @@ __xprt_lock_write(struct rpc_xprt *xprt, struct rpc_task *task) { struct rpc_rqst *req = task->tk_rqstp; - if (!xprt->snd_task) { - if (xprt->nocong || __xprt_get_cong(xprt, task)) { - xprt->snd_task = task; - if (req) { - req->rq_bytes_sent = 0; - req->rq_ntrans++; - } - } + if (test_and_set_bit(XPRT_LOCKED, &xprt->sockstate)) { + if (task == xprt->snd_task) + return 1; + if (task == NULL) + return 0; + goto out_sleep; } - if (xprt->snd_task != task) { - dprintk("RPC: %4d TCP write queue full\n", task->tk_pid); - task->tk_timeout = 0; - task->tk_status = -EAGAIN; - if (req && req->rq_ntrans) - rpc_sleep_on(&xprt->resend, task, NULL, NULL); - else - rpc_sleep_on(&xprt->sending, task, NULL, NULL); + if (xprt->nocong || __xprt_get_cong(xprt, task)) { + xprt->snd_task = task; + if (req) { + req->rq_bytes_sent = 0; + req->rq_ntrans++; + } + return 1; } - return xprt->snd_task == task; + smp_mb__before_clear_bit(); + clear_bit(XPRT_LOCKED, &xprt->sockstate); + smp_mb__after_clear_bit(); +out_sleep: + dprintk("RPC: %4d failed to lock socket %p\n", task->tk_pid, xprt); + task->tk_timeout = 0; + task->tk_status = -EAGAIN; + if (req && req->rq_ntrans) + rpc_sleep_on(&xprt->resend, task, NULL, NULL); + else + rpc_sleep_on(&xprt->sending, task, NULL, NULL); + return 0; } static inline int @@ -177,15 +187,15 @@ __xprt_lock_write_next(struct rpc_xprt *xprt) { struct rpc_task *task; - if (xprt->snd_task) + if (test_and_set_bit(XPRT_LOCKED, &xprt->sockstate)) return; + if (!xprt->nocong && RPCXPRT_CONGESTED(xprt)) + goto out_unlock; task = rpc_wake_up_next(&xprt->resend); if (!task) { - if (!xprt->nocong && RPCXPRT_CONGESTED(xprt)) - return; task = rpc_wake_up_next(&xprt->sending); if (!task) - return; + goto out_unlock; } if (xprt->nocong || __xprt_get_cong(xprt, task)) { struct rpc_rqst *req = task->tk_rqstp; @@ -194,7 +204,12 @@ __xprt_lock_write_next(struct rpc_xprt *xprt) req->rq_bytes_sent = 0; req->rq_ntrans++; } + return; } +out_unlock: + smp_mb__before_clear_bit(); + clear_bit(XPRT_LOCKED, &xprt->sockstate); + smp_mb__after_clear_bit(); } /* @@ -203,9 +218,13 @@ __xprt_lock_write_next(struct rpc_xprt *xprt) static void __xprt_release_write(struct rpc_xprt *xprt, struct rpc_task *task) { - if (xprt->snd_task == task) + if (xprt->snd_task == task) { xprt->snd_task = NULL; - __xprt_lock_write_next(xprt); + smp_mb__before_clear_bit(); + clear_bit(XPRT_LOCKED, &xprt->sockstate); + smp_mb__after_clear_bit(); + __xprt_lock_write_next(xprt); + } } static inline void @@ -393,6 +412,15 @@ xprt_close(struct rpc_xprt *xprt) sock_release(sock); } +static void +xprt_socket_autoclose(void *args) +{ + struct rpc_xprt *xprt = (struct rpc_xprt *)args; + + xprt_close(xprt); + xprt_release_write(xprt, NULL); +} + /* * Mark a transport as disconnected */ @@ -406,6 +434,27 @@ xprt_disconnect(struct rpc_xprt *xprt) spin_unlock_bh(&xprt->sock_lock); } +/* + * Used to allow disconnection when we've been idle + */ +static void +xprt_init_autodisconnect(unsigned long data) +{ + struct rpc_xprt *xprt = (struct rpc_xprt *)data; + + spin_lock(&xprt->sock_lock); + if (!list_empty(&xprt->recv) || xprt->shutdown) + goto out_abort; + if (test_and_set_bit(XPRT_LOCKED, &xprt->sockstate)) + goto out_abort; + spin_unlock(&xprt->sock_lock); + /* Let keventd close the socket */ + schedule_work(&xprt->task_cleanup); + return; +out_abort: + spin_unlock(&xprt->sock_lock); +} + /* * Attempt to connect a TCP socket. * @@ -1254,6 +1303,8 @@ xprt_reserve(struct rpc_task *task) spin_lock(&xprt->xprt_lock); do_xprt_reserve(task); spin_unlock(&xprt->xprt_lock); + if (task->tk_rqstp) + del_timer_sync(&xprt->timer); } } @@ -1333,6 +1384,9 @@ xprt_release(struct rpc_task *task) __xprt_put_cong(xprt, req); if (!list_empty(&req->rq_list)) list_del(&req->rq_list); + xprt->last_used = jiffies; + if (list_empty(&xprt->recv) && !xprt->shutdown) + mod_timer(&xprt->timer, xprt->last_used + XPRT_IDLE_TIMEOUT); spin_unlock_bh(&xprt->sock_lock); task->tk_rqstp = NULL; memset(req, 0, sizeof(*req)); /* mark unused */ @@ -1403,6 +1457,11 @@ xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to) init_waitqueue_head(&xprt->cong_wait); INIT_LIST_HEAD(&xprt->recv); + INIT_WORK(&xprt->task_cleanup, xprt_socket_autoclose, xprt); + init_timer(&xprt->timer); + xprt->timer.function = xprt_init_autodisconnect; + xprt->timer.data = (unsigned long) xprt; + xprt->last_used = jiffies; /* Set timeout parameters */ if (to) { @@ -1583,6 +1642,7 @@ xprt_shutdown(struct rpc_xprt *xprt) rpc_wake_up(&xprt->backlog); if (waitqueue_active(&xprt->cong_wait)) wake_up(&xprt->cong_wait); + del_timer_sync(&xprt->timer); } /* -- cgit v1.2.3 From 5bb0bc7c89ec7d694cd77487fe26f004ee0d0bb7 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 17:00:45 +0100 Subject: NFSv4: Atomic open(). Fixes races w.r.t. opening files. --- fs/nfs/dir.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++- fs/nfs/inode.c | 54 +++++++++++++++++-- fs/nfs/nfs3proc.c | 2 + fs/nfs/nfs4proc.c | 134 ++++++++++++++++++++++++++++++++-------------- fs/nfs/nfs4state.c | 2 - fs/nfs/proc.c | 2 + include/linux/nfs_fs.h | 6 +++ include/linux/nfs_xdr.h | 2 + 8 files changed, 295 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 0796bd6a3ac0..1adc5bfea4f6 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -72,6 +72,26 @@ struct inode_operations nfs_dir_inode_operations = { .setattr = nfs_setattr, }; +#ifdef CONFIG_NFS_V4 + +static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); +struct inode_operations nfs4_dir_inode_operations = { + .create = nfs_create, + .lookup = nfs_atomic_lookup, + .link = nfs_link, + .unlink = nfs_unlink, + .symlink = nfs_symlink, + .mkdir = nfs_mkdir, + .rmdir = nfs_rmdir, + .mknod = nfs_mknod, + .rename = nfs_rename, + .permission = nfs_permission, + .getattr = nfs_getattr, + .setattr = nfs_setattr, +}; + +#endif /* CONFIG_NFS_V4 */ + /* * Open file */ @@ -670,7 +690,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru goto out; error = -ENOMEM; - dentry->d_op = &nfs_dentry_operations; + dentry->d_op = NFS_PROTO(dir)->dentry_ops; lock_kernel(); @@ -702,6 +722,119 @@ out: return ERR_PTR(error); } +#ifdef CONFIG_NFS_V4 +static int nfs_open_revalidate(struct dentry *, struct nameidata *); + +struct dentry_operations nfs4_dentry_operations = { + .d_revalidate = nfs_open_revalidate, + .d_delete = nfs_dentry_delete, + .d_iput = nfs_dentry_iput, +}; + +static int is_atomic_open(struct inode *dir, struct nameidata *nd) +{ + if (!nd) + return 0; + /* Check that we are indeed trying to open this file */ + if ((nd->flags & LOOKUP_CONTINUE) || !(nd->flags & LOOKUP_OPEN)) + return 0; + /* NFS does not (yet) have a stateful open for directories */ + if (nd->flags & LOOKUP_DIRECTORY) + return 0; + /* Are we trying to write to a read only partition? */ + if (IS_RDONLY(dir) && (nd->intent.open.flags & (O_CREAT|O_TRUNC|FMODE_WRITE))) + return 0; + return 1; +} + +static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct inode *inode = NULL; + int error = 0; + + /* Check that we are indeed trying to open this file */ + if (!is_atomic_open(dir, nd)) + goto no_open; + + if (dentry->d_name.len > NFS_SERVER(dir)->namelen) { + error = -ENAMETOOLONG; + goto out; + } + dentry->d_op = NFS_PROTO(dir)->dentry_ops; + + /* Let vfs_create() deal with O_EXCL */ + if (nd->intent.open.flags & O_EXCL) + goto no_entry; + + /* Open the file on the server */ + lock_kernel(); + inode = nfs4_atomic_open(dir, dentry, nd); + unlock_kernel(); + if (IS_ERR(inode)) { + error = PTR_ERR(inode); + switch (error) { + /* Make a negative dentry */ + case -ENOENT: + inode = NULL; + break; + /* This turned out not to be a regular file */ + case -ELOOP: + if (!(nd->intent.open.flags & O_NOFOLLOW)) + goto no_open; + /* case -EISDIR: */ + /* case -EINVAL: */ + default: + goto out; + } + } +no_entry: + d_add(dentry, inode); + nfs_renew_times(dentry); +out: + BUG_ON(error > 0); + return ERR_PTR(error); +no_open: + return nfs_lookup(dir, dentry, nd); +} + +static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + struct dentry *parent = NULL; + struct inode *inode = dentry->d_inode; + int openflags, ret = 0; + + /* NFS only supports OPEN for regular files */ + if (inode && !S_ISREG(inode->i_mode)) + goto no_open; + parent = dget_parent(dentry); + if (!is_atomic_open(parent->d_inode, nd)) + goto no_open; + openflags = nd->intent.open.flags; + if (openflags & O_CREAT) { + /* If this is a negative dentry, just drop it */ + if (!inode) + goto out; + /* If this is exclusive open, just revalidate */ + if (openflags & O_EXCL) + goto no_open; + } + /* We can't create new files, or truncate existing ones here */ + openflags &= ~(O_CREAT|O_TRUNC); + + lock_kernel(); + ret = nfs4_open_revalidate(parent->d_inode, dentry, openflags); + unlock_kernel(); +out: + dput(parent); + if (!ret) + d_drop(dentry); + return ret; +no_open: + dput(parent); + return nfs_lookup_revalidate(dentry, nd); +} +#endif /* CONFIG_NFSV4 */ + static inline int find_dirent_name(nfs_readdir_descriptor_t *desc, struct page *page, struct dentry *dentry) { @@ -1306,6 +1439,9 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd) /* We only need to check permissions on file open() and access() */ if (!nd || !(nd->flags & (LOOKUP_OPEN|LOOKUP_ACCESS))) return 0; + /* NFSv4 has atomic_open... */ + if (NFS_PROTO(inode)->version > 3 && (nd->flags & LOOKUP_OPEN)) + return 0; } lock_kernel(); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 767cd32e7a36..276f3a10298b 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -303,7 +303,6 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) server = NFS_SB(sb); sb->s_magic = NFS_SUPER_MAGIC; - sb->s_op = &nfs_sops; /* Did getting the root inode fail? */ if (nfs_get_root(&root_inode, authflavor, sb, &server->fh) < 0) @@ -312,7 +311,7 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) if (!sb->s_root) goto out_no_root; - sb->s_root->d_op = &nfs_dentry_operations; + sb->s_root->d_op = server->rpc_ops->dentry_ops; /* Get some general file system info */ if (server->rpc_ops->fsinfo(server, &server->fh, &fsinfo) < 0) { @@ -513,6 +512,7 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) goto out_shutdown; } + sb->s_op = &nfs_sops; err = nfs_sb_init(sb, authflavor); if (err != 0) goto out_noinit; @@ -745,7 +745,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) inode->i_data.a_ops = &nfs_file_aops; inode->i_data.backing_dev_info = &NFS_SB(sb)->backing_dev_info; } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &nfs_dir_inode_operations; + inode->i_op = NFS_SB(sb)->rpc_ops->dir_inode_ops; inode->i_fop = &nfs_dir_operations; if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS) && fattr->size <= NFS_LIMIT_READDIRPLUS) @@ -837,7 +837,12 @@ printk("nfs_setattr: revalidate failed, error=%d\n", error); filemap_fdatawait(inode->i_mapping); if (error) goto out; + /* Optimize away unnecessary truncates */ + if ((attr->ia_valid & ATTR_SIZE) && i_size_read(inode) == attr->ia_size) + attr->ia_valid &= ~ATTR_SIZE; } + if (!attr->ia_valid) + goto out; error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr); if (error) @@ -1357,6 +1362,48 @@ static struct file_system_type nfs_fs_type = { #ifdef CONFIG_NFS_V4 +static void nfs4_clear_inode(struct inode *); + +static struct super_operations nfs4_sops = { + .alloc_inode = nfs_alloc_inode, + .destroy_inode = nfs_destroy_inode, + .write_inode = nfs_write_inode, + .delete_inode = nfs_delete_inode, + .put_super = nfs_put_super, + .statfs = nfs_statfs, + .clear_inode = nfs4_clear_inode, + .umount_begin = nfs_umount_begin, + .show_options = nfs_show_options, +}; + +/* + * Clean out any remaining NFSv4 state that might be left over due + * to open() calls that passed nfs_atomic_lookup, but failed to call + * nfs_open(). + */ +static void nfs4_clear_inode(struct inode *inode) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + while (!list_empty(&nfsi->open_states)) { + struct nfs4_state *state; + + state = list_entry(nfsi->open_states.next, + struct nfs4_state, + inode_states); + dprintk("%s(%s/%Ld): found unclaimed NFSv4 state %p\n", + __FUNCTION__, + inode->i_sb->s_id, + (long long)NFS_FILEID(inode), + state); + list_del(&state->inode_states); + nfs4_put_open_state(state); + } + /* Now call standard NFS clear_inode() code */ + nfs_clear_inode(inode); +} + + static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, int silent) { struct nfs_server *server; @@ -1481,6 +1528,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, if ((server->idmap = nfs_idmap_new(server)) == NULL) printk(KERN_WARNING "NFS: couldn't start IDmap\n"); + sb->s_op = &nfs4_sops; err = nfs_sb_init(sb, authflavour); if (err == 0) return 0; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 0ca8bbc17e13..e5b2ad2f8623 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -898,6 +898,8 @@ nfs3_request_compatible(struct nfs_page *req, struct file *filp, struct page *pa struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ + .dentry_ops = &nfs_dentry_operations, + .dir_inode_ops = &nfs_dir_inode_operations, .getroot = nfs3_proc_get_root, .getattr = nfs3_proc_getattr, .setattr = nfs3_proc_setattr, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f617d0bf5313..a6ac022add44 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -45,6 +45,7 @@ #include #include #include +#include #define NFSDBG_FACILITY NFSDBG_PROC @@ -509,6 +510,9 @@ nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) return status; } +/* + * Returns an nfs4_state + an referenced inode + */ struct nfs4_state * nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred) { @@ -617,19 +621,23 @@ retry: up(&sp->so_sema); nfs4_put_state_owner(sp); - iput(inode); return state; out_up: up(&sp->so_sema); nfs4_put_state_owner(sp); - if (state) + if (state) { nfs4_put_open_state(state); - if (inode) + state = NULL; + } + if (inode) { iput(inode); + inode = NULL; + } status = nfs4_handle_error(server, status); if (!status) goto retry; + BUG_ON(status < -1000 || status > 0); out: return ERR_PTR(status); } @@ -718,6 +726,56 @@ nfs4_do_close(struct inode *inode, struct nfs4_state *state) return status; } +struct inode * +nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + struct iattr attr; + struct rpc_cred *cred; + struct nfs4_state *state; + + if (nd->flags & LOOKUP_CREATE) { + attr.ia_mode = nd->intent.open.create_mode; + attr.ia_valid = ATTR_MODE; + if (!IS_POSIXACL(dir)) + attr.ia_mode &= ~current->fs->umask; + } else { + attr.ia_valid = 0; + BUG_ON(nd->intent.open.flags & O_CREAT); + } + + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + state = nfs4_do_open(dir, &dentry->d_name, nd->intent.open.flags, &attr, cred); + put_rpccred(cred); + if (IS_ERR(state)) + return (struct inode *)state; + return state->inode; +} + +int +nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags) +{ + struct rpc_cred *cred; + struct nfs4_state *state; + struct inode *inode; + + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + state = nfs4_do_open(dir, &dentry->d_name, openflags, NULL, cred); + put_rpccred(cred); + if (state == ERR_PTR(-ENOENT) && dentry->d_inode == 0) + return 1; + if (IS_ERR(state)) + return 0; + inode = state->inode; + if (inode == dentry->d_inode) { + iput(inode); + return 1; + } + d_drop(dentry); + nfs4_put_open_state(state); + iput(inode); + return 0; +} + static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) @@ -808,28 +866,39 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, struct inode * inode = dentry->d_inode; int size_change = sattr->ia_valid & ATTR_SIZE; struct nfs4_state *state = NULL; - int status; + int need_iput = 0; + int status; fattr->valid = 0; if (size_change) { - struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); - state = nfs4_do_open(dentry->d_parent->d_inode, + state = nfs4_find_state_bypid(inode, current->pid); + + if (!state) { + struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + state = nfs4_do_open(dentry->d_parent->d_inode, &dentry->d_name, FMODE_WRITE, NULL, cred); - put_rpccred(cred); + put_rpccred(cred); + need_iput = 1; + } if (IS_ERR(state)) return PTR_ERR(state); if (state->inode != inode) { - printk(KERN_WARNING "nfs: raced in setattr, returning -EIO\n"); - nfs4_put_open_state(state); - return -EIO; + printk(KERN_WARNING "nfs: raced in setattr (%p != %p), returning -EIO\n", inode, state->inode); + status = -EIO; + goto out; } } status = nfs4_do_setattr(NFS_SERVER(inode), fattr, NFS_FH(inode), sattr, state); - if (state) +out: + if (state) { + inode = state->inode; nfs4_put_open_state(state); + if (need_iput) + iput(inode); + } return status; } @@ -1085,18 +1154,18 @@ nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, state = nfs4_do_open(dir, name, flags, sattr, cred); put_rpccred(cred); if (!IS_ERR(state)) { - inode = igrab(state->inode); + inode = state->inode; if (flags & O_EXCL) { struct nfs_fattr fattr; int status; status = nfs4_do_setattr(NFS_SERVER(dir), &fattr, NFS_FH(inode), sattr, state); if (status != 0) { + nfs4_put_open_state(state); iput(inode); inode = ERR_PTR(status); } } - nfs4_put_open_state(state); } else inode = (struct inode *)state; return inode; @@ -1672,43 +1741,28 @@ static int nfs4_proc_file_open(struct inode *inode, struct file *filp) { struct dentry *dentry = filp->f_dentry; - struct inode *dir = dentry->d_parent->d_inode; - struct rpc_cred *cred; struct nfs4_state *state; - int flags = filp->f_flags; - int status = 0; dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n", (int)dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, (int)dentry->d_name.len, dentry->d_name.name); - if ((flags + 1) & O_ACCMODE) - flags++; - - lock_kernel(); -/* -* We have already opened the file "O_EXCL" in nfs4_proc_create!! -* This ugliness will go away with lookup-intent... -*/ - cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); - state = nfs4_do_open(dir, &dentry->d_name, flags, NULL, cred); - if (IS_ERR(state)) { - status = PTR_ERR(state); - state = NULL; - } else if (filp->f_mode & FMODE_WRITE) - nfs_set_mmcred(inode, cred); - if (inode != filp->f_dentry->d_inode) { + /* Find our open stateid */ + state = nfs4_find_state_bypid(inode, current->pid); + if (state == NULL) { printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__); - status = -EIO; /* ERACE actually */ - nfs4_put_open_state(state); - state = NULL; + return -EIO; /* ERACE actually */ + } + nfs4_put_open_state(state); + if (filp->f_mode & FMODE_WRITE) { + lock_kernel(); + nfs_set_mmcred(inode, state->owner->so_cred); + unlock_kernel(); } filp->private_data = state; - put_rpccred(cred); - unlock_kernel(); - return status; + return 0; } /* @@ -1922,6 +1976,8 @@ nfs4_proc_setclientid_confirm(struct nfs4_client *clp) struct nfs_rpc_ops nfs_v4_clientops = { .version = 4, /* protocol version */ + .dentry_ops = &nfs4_dentry_operations, + .dir_inode_ops = &nfs4_dir_inode_operations, .getroot = nfs4_proc_get_root, .getattr = nfs4_proc_getattr, .setattr = nfs4_proc_setattr, diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 36f3f7e4c0ef..333daf5f48e7 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -349,7 +349,6 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner) atomic_inc(&owner->so_count); list_add(&state->inode_states, &nfsi->open_states); state->inode = inode; - atomic_inc(&inode->i_count); spin_unlock(&inode->i_lock); } else { spin_unlock(&inode->i_lock); @@ -384,7 +383,6 @@ nfs4_put_open_state(struct nfs4_state *state) } while (!status); } up(&owner->so_sema); - iput(inode); nfs4_free_open_state(state); nfs4_put_state_owner(owner); } diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 6570e719ee54..79f18e4cfb81 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -656,6 +656,8 @@ nfs_request_compatible(struct nfs_page *req, struct file *filp, struct page *pag struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ + .dentry_ops = &nfs_dentry_operations, + .dir_inode_ops = &nfs_dir_inode_operations, .getroot = nfs_proc_get_root, .getattr = nfs_proc_getattr, .setattr = nfs_proc_setattr, diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 3efc91b200e6..0605e9c63026 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -558,6 +558,9 @@ struct nfs4_state { }; +extern struct dentry_operations nfs4_dentry_operations; +extern struct inode_operations nfs4_dir_inode_operations; + /* nfs4proc.c */ extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); @@ -566,6 +569,8 @@ extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_proc_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *); +extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); +extern int nfs4_open_revalidate(struct inode *, struct dentry *, int); /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs4_client *); @@ -581,6 +586,7 @@ extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struc extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); +extern struct nfs4_state *nfs4_find_state_bypid(struct inode *, pid_t); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); extern int nfs4_handle_error(struct nfs_server *, int); extern void nfs4_schedule_state_recovery(struct nfs4_client *); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 242b50436d92..4393ae7c305d 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -637,6 +637,8 @@ struct nfs_page; */ struct nfs_rpc_ops { int version; /* Protocol version */ + struct dentry_operations *dentry_ops; + struct inode_operations *dir_inode_ops; int (*getroot) (struct nfs_server *, struct nfs_fh *, struct nfs_fattr *); -- cgit v1.2.3 From c04e88dbd89ec891a252ffe09378310dd1d18546 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 17:01:32 +0100 Subject: NFSv4: Share open_owner structs between several different processes. Reduces the load on the server. --- fs/nfs/nfs4proc.c | 80 +++++++++++++++++++++++++++----- fs/nfs/nfs4state.c | 118 ++++++++++++++++++++++++++++++++++++++++++------ fs/nfs/nfs4xdr.c | 82 +++++++++++++++++++++++++++++++++ include/linux/nfs4.h | 1 + include/linux/nfs_fs.h | 7 ++- include/linux/nfs_xdr.h | 1 + 6 files changed, 263 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a6ac022add44..10f67569de73 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -616,8 +616,13 @@ retry: memcpy(&state->stateid, &oc_res.stateid, sizeof(state->stateid)); } else memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); + spin_lock(&inode->i_lock); + if (flags & FMODE_READ) + state->nreaders++; + if (flags & FMODE_WRITE) + state->nwriters++; state->state |= flags & (FMODE_READ|FMODE_WRITE); - state->pid = current->pid; + spin_unlock(&inode->i_lock); up(&sp->so_sema); nfs4_put_state_owner(sp); @@ -634,6 +639,21 @@ out_up: iput(inode); inode = NULL; } + /* NOTE: BAD_SEQID means the server and client disagree about the + * book-keeping w.r.t. state-changing operations + * (OPEN/CLOSE/LOCK/LOCKU...) + * It is actually a sign of a bug on the client or on the server. + * + * If we receive a BAD_SEQID error in the particular case of + * doing an OPEN, we assume that nfs4_increment_seqid() will + * have unhashed the old state_owner for us, and that we can + * therefore safely retry using a new one. We should still warn + * the user though... + */ + if (status == -NFS4ERR_BAD_SEQID) { + printk(KERN_WARNING "NFS: v4 server returned a bad sequence-id error!\n"); + goto retry; + } status = nfs4_handle_error(server, status); if (!status) goto retry; @@ -722,6 +742,36 @@ nfs4_do_close(struct inode *inode, struct nfs4_state *state) * the state_owner. we keep this around to process errors */ nfs4_increment_seqid(status, sp); + if (!status) + memcpy(&state->stateid, &res.stateid, sizeof(state->stateid)); + + return status; +} + +int +nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode) +{ + struct nfs4_state_owner *sp = state->owner; + int status = 0; + struct nfs_closeargs arg = { + .fh = NFS_FH(inode), + .seqid = sp->so_seqid, + .share_access = mode, + }; + struct nfs_closeres res = { + .status = 0, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE], + .rpc_argp = &arg, + .rpc_resp = &res, + }; + + memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid)); + status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0); + nfs4_increment_seqid(status, sp); + if (!status) + memcpy(&state->stateid, &res.stateid, sizeof(state->stateid)); return status; } @@ -771,7 +821,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags) return 1; } d_drop(dentry); - nfs4_put_open_state(state); + nfs4_close_state(state, openflags); iput(inode); return 0; } @@ -872,15 +922,14 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, fattr->valid = 0; if (size_change) { - state = nfs4_find_state_bypid(inode, current->pid); - + struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + state = nfs4_find_state(inode, cred, FMODE_WRITE); if (!state) { - struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); state = nfs4_do_open(dentry->d_parent->d_inode, &dentry->d_name, FMODE_WRITE, NULL, cred); - put_rpccred(cred); need_iput = 1; } + put_rpccred(cred); if (IS_ERR(state)) return PTR_ERR(state); @@ -895,7 +944,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, out: if (state) { inode = state->inode; - nfs4_put_open_state(state); + nfs4_close_state(state, FMODE_WRITE); if (need_iput) iput(inode); } @@ -1161,7 +1210,7 @@ nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, status = nfs4_do_setattr(NFS_SERVER(dir), &fattr, NFS_FH(inode), sattr, state); if (status != 0) { - nfs4_put_open_state(state); + nfs4_close_state(state, flags); iput(inode); inode = ERR_PTR(status); } @@ -1742,6 +1791,7 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp) { struct dentry *dentry = filp->f_dentry; struct nfs4_state *state; + struct rpc_cred *cred; dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n", (int)dentry->d_parent->d_name.len, @@ -1750,12 +1800,14 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp) /* Find our open stateid */ - state = nfs4_find_state_bypid(inode, current->pid); + cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + state = nfs4_find_state(inode, cred, filp->f_mode); + put_rpccred(cred); if (state == NULL) { printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__); return -EIO; /* ERACE actually */ } - nfs4_put_open_state(state); + nfs4_close_state(state, filp->f_mode); if (filp->f_mode & FMODE_WRITE) { lock_kernel(); nfs_set_mmcred(inode, state->owner->so_cred); @@ -1774,7 +1826,7 @@ nfs4_proc_file_release(struct inode *inode, struct file *filp) struct nfs4_state *state = (struct nfs4_state *)filp->private_data; if (state) - nfs4_put_open_state(state); + nfs4_close_state(state, filp->f_mode); return 0; } @@ -1816,6 +1868,9 @@ nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server) rpc_delay(task, NFS4_POLL_RETRY_TIME); task->tk_status = 0; return -EAGAIN; + case -NFS4ERR_OLD_STATEID: + task->tk_status = 0; + return -EAGAIN; } return 0; } @@ -1892,6 +1947,9 @@ nfs4_handle_error(struct nfs_server *server, int errorcode) case -NFS4ERR_DELAY: ret = nfs4_delay(server->client); break; + case -NFS4ERR_OLD_STATEID: + ret = 0; + break; default: if (errorcode <= -1000) { printk(KERN_WARNING "%s could not handle NFSv4 error %d\n", diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 333daf5f48e7..7a078a42eec3 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -188,6 +188,23 @@ nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred) return sp; } +static struct nfs4_state_owner * +nfs4_find_state_owner(struct nfs4_client *clp, struct rpc_cred *cred) +{ + struct nfs4_state_owner *sp, *res = NULL; + + list_for_each_entry(sp, &clp->cl_state_owners, so_list) { + if (sp->so_cred != cred) + continue; + atomic_inc(&sp->so_count); + /* Move to the head of the list */ + list_move(&sp->so_list, &clp->cl_state_owners); + res = sp; + break; + } + return res; +} + /* * nfs4_alloc_state_owner(): this is called on the OPEN or CREATE path to * create a new state_owner. @@ -208,6 +225,15 @@ nfs4_alloc_state_owner(void) return sp; } +static void +nfs4_unhash_state_owner(struct nfs4_state_owner *sp) +{ + struct nfs4_client *clp = sp->so_client; + spin_lock(&clp->cl_lock); + list_del_init(&sp->so_list); + spin_unlock(&clp->cl_lock); +} + struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) { @@ -217,7 +243,9 @@ nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) get_rpccred(cred); new = nfs4_alloc_state_owner(); spin_lock(&clp->cl_lock); - sp = nfs4_client_grab_unused(clp, cred); + sp = nfs4_find_state_owner(clp, cred); + if (sp == NULL) + sp = nfs4_client_grab_unused(clp, cred); if (sp == NULL && new != NULL) { list_add(&new->so_list, &clp->cl_state_owners); new->so_client = clp; @@ -248,6 +276,8 @@ nfs4_put_state_owner(struct nfs4_state_owner *sp) return; if (clp->cl_nunused >= OPENOWNER_POOL_SIZE) goto out_free; + if (list_empty(&sp->so_list)) + goto out_free; list_move(&sp->so_list, &clp->cl_unused); clp->cl_nunused++; spin_unlock(&clp->cl_lock); @@ -269,24 +299,38 @@ nfs4_alloc_open_state(void) state = kmalloc(sizeof(*state), GFP_KERNEL); if (!state) return NULL; - state->pid = current->pid; state->state = 0; + state->nreaders = 0; + state->nwriters = 0; memset(state->stateid.data, 0, sizeof(state->stateid.data)); atomic_set(&state->count, 1); return state; } static struct nfs4_state * -__nfs4_find_state_bypid(struct inode *inode, pid_t pid) +__nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode) { struct nfs_inode *nfsi = NFS_I(inode); struct nfs4_state *state; + mode &= (FMODE_READ|FMODE_WRITE); list_for_each_entry(state, &nfsi->open_states, inode_states) { - if (state->pid == pid) { - atomic_inc(&state->count); - return state; - } + if (state->owner->so_cred != cred) + continue; + if ((mode & FMODE_READ) != 0 && state->nreaders == 0) + continue; + if ((mode & FMODE_WRITE) != 0 && state->nwriters == 0) + continue; + if ((state->state & mode) != mode) + continue; + /* Add the state to the head of the inode's list */ + list_move(&state->inode_states, &nfsi->open_states); + atomic_inc(&state->count); + if (mode & FMODE_READ) + state->nreaders++; + if (mode & FMODE_WRITE) + state->nwriters++; + return state; } return NULL; } @@ -298,7 +342,12 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) struct nfs4_state *state; list_for_each_entry(state, &nfsi->open_states, inode_states) { + /* Is this in the process of being freed? */ + if (state->nreaders == 0 && state->nwriters == 0) + continue; if (state->owner == owner) { + /* Add the state to the head of the inode's list */ + list_move(&state->inode_states, &nfsi->open_states); atomic_inc(&state->count); return state; } @@ -307,16 +356,12 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) } struct nfs4_state * -nfs4_find_state_bypid(struct inode *inode, pid_t pid) +nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode) { - struct nfs_inode *nfsi = NFS_I(inode); struct nfs4_state *state; spin_lock(&inode->i_lock); - state = __nfs4_find_state_bypid(inode, pid); - /* Add the state to the tail of the inode's list */ - if (state) - list_move_tail(&state->inode_states, &nfsi->open_states); + state = __nfs4_find_state(inode, cred, mode); spin_unlock(&inode->i_lock); return state; } @@ -387,6 +432,50 @@ nfs4_put_open_state(struct nfs4_state *state) nfs4_put_state_owner(owner); } +void +nfs4_close_state(struct nfs4_state *state, mode_t mode) +{ + struct inode *inode = state->inode; + struct nfs4_state_owner *owner = state->owner; + int newstate; + int status = 0; + + down(&owner->so_sema); + /* Protect against nfs4_find_state() */ + spin_lock(&inode->i_lock); + if (mode & FMODE_READ) + state->nreaders--; + if (mode & FMODE_WRITE) + state->nwriters--; + if (state->nwriters == 0 && state->nreaders == 0) + list_del_init(&state->inode_states); + spin_unlock(&inode->i_lock); + do { + newstate = 0; + if (state->state == 0) + break; + if (state->nreaders) + newstate |= FMODE_READ; + if (state->nwriters) + newstate |= FMODE_WRITE; + if (state->state == newstate) + break; + if (newstate != 0) + status = nfs4_do_downgrade(inode, state, newstate); + else + status = nfs4_do_close(inode, state); + if (!status) { + state->state = newstate; + break; + } + up(&owner->so_sema); + status = nfs4_handle_error(NFS_SERVER(inode), status); + down(&owner->so_sema); + } while (!status); + up(&owner->so_sema); + nfs4_put_open_state(state); +} + /* * Called with sp->so_sema held. * @@ -399,6 +488,9 @@ nfs4_increment_seqid(int status, struct nfs4_state_owner *sp) { if (status == NFS_OK || seqid_mutating_err(-status)) sp->so_seqid++; + /* If the server returns BAD_SEQID, unhash state_owner here */ + if (status == -NFS4ERR_BAD_SEQID) + nfs4_unhash_state_owner(sp); } static int reclaimer(void *); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 16296618a231..7a2d241e50e8 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -176,6 +176,14 @@ static int nfs_stat_to_errno(int); op_decode_hdr_maxsz + \ 4 + 5 + 2 + 3 + \ decode_getattr_maxsz +#define NFS4_enc_open_downgrade_sz \ + compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + op_encode_hdr_maxsz + 7 +#define NFS4_dec_open_downgrade_sz \ + compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + 4 #define NFS4_enc_close_sz compound_encode_hdr_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 5 @@ -711,6 +719,22 @@ encode_open_reclaim(struct xdr_stream *xdr, struct nfs_open_reclaimargs *arg) return 0; } +static int +encode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeargs *arg) +{ + uint32_t *p; + + RESERVE_SPACE(16+sizeof(arg->stateid.data)); + WRITE32(OP_OPEN_DOWNGRADE); + WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data)); + WRITE32(arg->seqid); + WRITE32(arg->share_access); + /* No deny modes */ + WRITE32(0); + + return 0; +} + static int encode_putfh(struct xdr_stream *xdr, struct nfs_fh *fh) { @@ -1129,6 +1153,27 @@ out: return status; } +/* + * Encode an OPEN_DOWNGRADE request + */ +static int +nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, uint32_t *p, struct nfs_closeargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if (status) + goto out; + status = encode_open_downgrade(&xdr, args); +out: + return status; +} /* * Encode a READ request @@ -2001,6 +2046,19 @@ decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res) return 0; } +static int +decode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeres *res) +{ + uint32_t *p; + int status; + + status = decode_op_hdr(xdr, OP_OPEN_DOWNGRADE); + if (status) + return status; + READ_BUF(sizeof(res->stateid.data)); + COPYMEM(res->stateid.data, sizeof(res->stateid.data)); + return 0; +} static int decode_putfh(struct xdr_stream *xdr) @@ -2377,6 +2435,29 @@ decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqs DECODE_TAIL; } + +/* + * Decode OPEN_DOWNGRADE response + */ +static int +nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_closeres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_open_downgrade(&xdr, res); +out: + return status; +} + /* * END OF "GENERIC" DECODE ROUTINES. */ @@ -2827,6 +2908,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(OPEN, enc_open, dec_open), PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm), PROC(OPEN_RECLAIM, enc_open_reclaim, dec_open_reclaim), + PROC(OPEN_DOWNGRADE, enc_open_downgrade, dec_open_downgrade), PROC(CLOSE, enc_close, dec_close), PROC(SETATTR, enc_setattr, dec_setattr), PROC(FSINFO, enc_fsinfo, dec_fsinfo), diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index a6f2d563b605..4a61a4682718 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -290,6 +290,7 @@ enum { NFSPROC4_CLNT_OPEN, NFSPROC4_CLNT_OPEN_CONFIRM, NFSPROC4_CLNT_OPEN_RECLAIM, + NFSPROC4_CLNT_OPEN_DOWNGRADE, NFSPROC4_CLNT_CLOSE, NFSPROC4_CLNT_SETATTR, NFSPROC4_CLNT_FSINFO, diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 0605e9c63026..cfbb7ff1aa89 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -549,10 +549,11 @@ struct nfs4_state { struct nfs4_state_owner *owner; /* Pointer to the open owner */ struct inode *inode; /* Pointer to the inode */ - pid_t pid; /* Thread that called OPEN */ nfs4_stateid stateid; + unsigned int nreaders; + unsigned int nwriters; int state; /* State on the server (R,W, or RW) */ atomic_t count; }; @@ -568,6 +569,7 @@ extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_proc_renew(struct nfs4_client *); extern int nfs4_do_close(struct inode *, struct nfs4_state *); +int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode); extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *); extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int); @@ -586,7 +588,8 @@ extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struc extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern void nfs4_put_open_state(struct nfs4_state *); -extern struct nfs4_state *nfs4_find_state_bypid(struct inode *, pid_t); +extern void nfs4_close_state(struct nfs4_state *, mode_t); +extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode); extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); extern int nfs4_handle_error(struct nfs_server *, int); extern void nfs4_schedule_state_recovery(struct nfs4_client *); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 4393ae7c305d..21827ad1a71e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -153,6 +153,7 @@ struct nfs_closeargs { struct nfs_fh * fh; nfs4_stateid stateid; __u32 seqid; + __u32 share_access; }; struct nfs_closeres { -- cgit v1.2.3 From 1f37cd43d9e866e99f81ffe6141b49cc6f83f619 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 17:02:21 +0100 Subject: NFSv4: Fix a bug which was causing Oopses if the client was mounting more than one partition from the same server. --- fs/nfs/idmap.c | 44 ++++++++++++++++++++++---------------------- fs/nfs/inode.c | 15 +++++---------- fs/nfs/nfs4state.c | 2 ++ fs/nfs/nfs4xdr.c | 8 ++++---- include/linux/nfs_fs.h | 5 +++++ include/linux/nfs_fs_sb.h | 1 - include/linux/nfs_idmap.h | 12 ++++++------ 7 files changed, 44 insertions(+), 43 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index bd1d1335561c..1d5d8a9dba1e 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -88,23 +88,27 @@ static struct rpc_pipe_ops idmap_upcall_ops = { .destroy_msg = idmap_pipe_destroy_msg, }; -void * -nfs_idmap_new(struct nfs_server *server) +void +nfs_idmap_new(struct nfs4_client *clp) { struct idmap *idmap; + if (clp->cl_idmap != NULL) + return; if ((idmap = kmalloc(sizeof(*idmap), GFP_KERNEL)) == NULL) - return (NULL); + return; memset(idmap, 0, sizeof(*idmap)); snprintf(idmap->idmap_path, sizeof(idmap->idmap_path), - "%s/idmap", server->client->cl_pathname); + "%s/idmap", clp->cl_rpcclient->cl_pathname); idmap->idmap_dentry = rpc_mkpipe(idmap->idmap_path, idmap, &idmap_upcall_ops, 0); - if (IS_ERR(idmap->idmap_dentry)) - goto err_free; + if (IS_ERR(idmap->idmap_dentry)) { + kfree(idmap); + return; + } init_MUTEX(&idmap->idmap_lock); init_MUTEX(&idmap->idmap_im_lock); @@ -112,22 +116,18 @@ nfs_idmap_new(struct nfs_server *server) idmap->idmap_user_hash.h_type = IDMAP_TYPE_USER; idmap->idmap_group_hash.h_type = IDMAP_TYPE_GROUP; - return (idmap); - - err_free: - kfree(idmap); - return (NULL); + clp->cl_idmap = idmap; } void -nfs_idmap_delete(struct nfs_server *server) +nfs_idmap_delete(struct nfs4_client *clp) { - struct idmap *idmap = server->idmap; + struct idmap *idmap = clp->cl_idmap; if (!idmap) return; rpc_unlink(idmap->idmap_path); - server->idmap = NULL; + clp->cl_idmap = NULL; kfree(idmap); } @@ -468,29 +468,29 @@ static unsigned int fnvhash32(const void *buf, size_t buflen) return (hash); } -int nfs_map_name_to_uid(struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) +int nfs_map_name_to_uid(struct nfs4_client *clp, const char *name, size_t namelen, __u32 *uid) { - struct idmap *idmap = server->idmap; + struct idmap *idmap = clp->cl_idmap; return nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid); } -int nfs_map_group_to_gid(struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) +int nfs_map_group_to_gid(struct nfs4_client *clp, const char *name, size_t namelen, __u32 *uid) { - struct idmap *idmap = server->idmap; + struct idmap *idmap = clp->cl_idmap; return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid); } -int nfs_map_uid_to_name(struct nfs_server *server, __u32 uid, char *buf) +int nfs_map_uid_to_name(struct nfs4_client *clp, __u32 uid, char *buf) { - struct idmap *idmap = server->idmap; + struct idmap *idmap = clp->cl_idmap; return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf); } -int nfs_map_gid_to_group(struct nfs_server *server, __u32 uid, char *buf) +int nfs_map_gid_to_group(struct nfs4_client *clp, __u32 uid, char *buf) { - struct idmap *idmap = server->idmap; + struct idmap *idmap = clp->cl_idmap; return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf); } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 276f3a10298b..f23707e5ecb2 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -158,11 +158,6 @@ nfs_put_super(struct super_block *sb) { struct nfs_server *server = NFS_SB(sb); -#ifdef CONFIG_NFS_V4 - if (server->idmap != NULL) - nfs_idmap_delete(server); -#endif /* CONFIG_NFS_V4 */ - nfs4_renewd_prepare_shutdown(server); if (server->client != NULL) @@ -1494,6 +1489,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, clp->cl_rpcclient = clnt; clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); + nfs_idmap_new(clp); } if (list_empty(&clp->cl_superblocks)) clear_bit(NFS4CLNT_OK, &clp->cl_state); @@ -1507,6 +1503,10 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, printk(KERN_WARNING "NFS: cannot create RPC client.\n"); goto out_remove_list; } + if (server->nfs4_state->cl_idmap == NULL) { + printk(KERN_WARNING "NFS: failed to create idmapper.\n"); + goto out_shutdown; + } clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0; clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0; @@ -1525,16 +1525,11 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, goto out_shutdown; } - if ((server->idmap = nfs_idmap_new(server)) == NULL) - printk(KERN_WARNING "NFS: couldn't start IDmap\n"); - sb->s_op = &nfs4_sops; err = nfs_sb_init(sb, authflavour); if (err == 0) return 0; rpciod_down(); - if (server->idmap != NULL) - nfs_idmap_delete(server); out_shutdown: rpc_shutdown_client(server->client); out_remove_list: diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 7a078a42eec3..0b391c7f4ce4 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #define OPENOWNER_POOL_SIZE 8 @@ -124,6 +125,7 @@ nfs4_free_client(struct nfs4_client *clp) BUG_ON(!list_empty(&clp->cl_state_owners)); if (clp->cl_cred) put_rpccred(clp->cl_cred); + nfs_idmap_delete(clp); if (clp->cl_rpcclient) rpc_shutdown_client(clp->cl_rpcclient); kfree(clp); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 7a2d241e50e8..3a372266bb03 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -328,7 +328,7 @@ encode_attrs(struct xdr_stream *xdr, struct iattr *iap, if (iap->ia_valid & ATTR_MODE) len += 4; if (iap->ia_valid & ATTR_UID) { - owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name); + owner_namelen = nfs_map_uid_to_name(server->nfs4_state, iap->ia_uid, owner_name); if (owner_namelen < 0) { printk(KERN_WARNING "nfs: couldn't resolve uid %d to string\n", iap->ia_uid); @@ -340,7 +340,7 @@ encode_attrs(struct xdr_stream *xdr, struct iattr *iap, len += 4 + (XDR_QUADLEN(owner_namelen) << 2); } if (iap->ia_valid & ATTR_GID) { - owner_grouplen = nfs_map_gid_to_group(server, iap->ia_gid, owner_group); + owner_grouplen = nfs_map_gid_to_group(server->nfs4_state, iap->ia_gid, owner_group); if (owner_grouplen < 0) { printk(KERN_WARNING "nfs4: couldn't resolve gid %d to string\n", iap->ia_gid); @@ -1677,7 +1677,7 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, } READ_BUF(dummy32); len += (XDR_QUADLEN(dummy32) << 2); - if ((status = nfs_map_name_to_uid(server, (char *)p, dummy32, + if ((status = nfs_map_name_to_uid(server->nfs4_state, (char *)p, dummy32, &nfp->uid)) < 0) { dprintk("read_attrs: name-to-uid mapping failed!\n"); nfp->uid = -2; @@ -1694,7 +1694,7 @@ decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr, } READ_BUF(dummy32); len += (XDR_QUADLEN(dummy32) << 2); - if ((status = nfs_map_group_to_gid(server, (char *)p, dummy32, + if ((status = nfs_map_group_to_gid(server->nfs4_state, (char *)p, dummy32, &nfp->gid)) < 0) { dprintk("read_attrs: group-to-gid mapping failed!\n"); nfp->gid = -2; diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index cfbb7ff1aa89..512d9203905f 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -438,6 +438,8 @@ extern void * nfs_root_data(void); #ifdef CONFIG_NFS_V4 +struct idmap; + /* * In a seqid-mutating op, this macro controls which error return * values trigger incrementation of the seqid. @@ -506,6 +508,9 @@ struct nfs4_client { wait_queue_head_t cl_waitq; struct rpc_wait_queue cl_rpcwaitq; + /* idmapper */ + struct idmap * cl_idmap; + /* Our own IP address, as a null-terminated string. * This is used to generate the clientid, and the callback address. */ diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 5f0b0ce3aa2c..1b5f7e130502 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -38,7 +38,6 @@ struct nfs_server { struct list_head nfs4_siblings; /* List of other nfs_server structs * that share the same clientid */ - void *idmap; #endif }; diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index c95076e5941b..50df56b5a01e 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -60,13 +60,13 @@ struct idmap_msg { }; #ifdef __KERNEL__ -void *nfs_idmap_new(struct nfs_server *); -void nfs_idmap_delete(struct nfs_server *); +void nfs_idmap_new(struct nfs4_client *); +void nfs_idmap_delete(struct nfs4_client *); -int nfs_map_name_to_uid(struct nfs_server *, const char *, size_t, __u32 *); -int nfs_map_group_to_gid(struct nfs_server *, const char *, size_t, __u32 *); -int nfs_map_uid_to_name(struct nfs_server *, __u32, char *); -int nfs_map_gid_to_group(struct nfs_server *, __u32, char *); +int nfs_map_name_to_uid(struct nfs4_client *, const char *, size_t, __u32 *); +int nfs_map_group_to_gid(struct nfs4_client *, const char *, size_t, __u32 *); +int nfs_map_uid_to_name(struct nfs4_client *, __u32, char *); +int nfs_map_gid_to_group(struct nfs4_client *, __u32, char *); #endif /* __KERNEL__ */ #endif /* NFS_IDMAP_H */ -- cgit v1.2.3 From 3f1990d30fe3d6e62d41d0224c27855b59517b8b Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 7 Feb 2004 17:03:03 +0100 Subject: NFSv4: Add support for POSIX file locking. --- fs/nfs/file.c | 23 ++-- fs/nfs/nfs3proc.c | 8 ++ fs/nfs/nfs4proc.c | 290 +++++++++++++++++++++++++++++++++++++++++--- fs/nfs/nfs4state.c | 174 +++++++++++++++++++++++++- fs/nfs/nfs4xdr.c | 309 ++++++++++++++++++++++++++++++++++++++++++++++- fs/nfs/proc.c | 8 ++ include/linux/nfs4.h | 3 + include/linux/nfs_fs.h | 37 +++++- include/linux/nfs_page.h | 1 + include/linux/nfs_xdr.h | 65 +++++++++- 10 files changed, 878 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/fs/nfs/file.c b/fs/nfs/file.c index b000db0f1b23..c32e7a2575d3 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -278,21 +277,17 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) if (!inode) return -EINVAL; - /* This will be in a forthcoming patch. */ - if (NFS_PROTO(inode)->version == 4) { - printk(KERN_INFO "NFS: file locking over NFSv4 is not yet supported\n"); - return -EIO; - } - /* No mandatory locks over NFS */ if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) return -ENOLCK; - /* Fake OK code if mounted without NLM support */ - if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) { - if (IS_GETLK(cmd)) - status = LOCK_USE_CLNT; - goto out_ok; + if (NFS_PROTO(inode)->version != 4) { + /* Fake OK code if mounted without NLM support */ + if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) { + if (IS_GETLK(cmd)) + status = LOCK_USE_CLNT; + goto out_ok; + } } /* @@ -302,7 +297,7 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) * Not sure whether that would be unique, though, or whether * that would break in other places. */ - if (!fl->fl_owner || (fl->fl_flags & FL_POSIX) != FL_POSIX) + if (!fl->fl_owner || !(fl->fl_flags & FL_POSIX)) return -ENOLCK; /* @@ -322,7 +317,7 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) return status; lock_kernel(); - status = nlmclnt_proc(inode, cmd, fl); + status = NFS_PROTO(inode)->lock(filp, cmd, fl); unlock_kernel(); if (status < 0) return status; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index e5b2ad2f8623..a27b48f411cb 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #define NFSDBG_FACILITY NFSDBG_PROC @@ -896,6 +897,12 @@ nfs3_request_compatible(struct nfs_page *req, struct file *filp, struct page *pa return 1; } +static int +nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl) +{ + return nlmclnt_proc(filp->f_dentry->d_inode, cmd, fl); +} + struct nfs_rpc_ops nfs_v3_clientops = { .version = 3, /* protocol version */ .dentry_ops = &nfs_dentry_operations, @@ -931,4 +938,5 @@ struct nfs_rpc_ops nfs_v3_clientops = { .file_release = nfs_release, .request_init = nfs3_request_init, .request_compatible = nfs3_request_compatible, + .lock = nfs3_proc_lock, }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 10f67569de73..3d509bd94b0e 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -598,9 +598,7 @@ retry: .fh = &o_res.fh, .seqid = sp->so_seqid, }; - struct nfs_open_confirmres oc_res = { - .status = 0, - }; + struct nfs_open_confirmres oc_res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM], .rpc_argp = &oc_arg, @@ -692,7 +690,7 @@ retry: fattr->valid = 0; if (state) - memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid)); + nfs4_copy_stateid(&arg.stateid, state, 0); else memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); @@ -724,9 +722,7 @@ nfs4_do_close(struct inode *inode, struct nfs4_state *state) struct nfs_closeargs arg = { .fh = NFS_FH(inode), }; - struct nfs_closeres res = { - .status = 0, - }; + struct nfs_closeres res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE], .rpc_argp = &arg, @@ -758,9 +754,7 @@ nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode) .seqid = sp->so_seqid, .share_access = mode, }; - struct nfs_closeres res = { - .status = 0, - }; + struct nfs_closeres res; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE], .rpc_argp = &arg, @@ -1085,7 +1079,7 @@ nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp) if (filp) { struct nfs4_state *state; state = (struct nfs4_state *)filp->private_data; - memcpy(&rdata->args.stateid, &state->stateid, sizeof(rdata->args.stateid)); + nfs4_copy_stateid(&rdata->args.stateid, state, rdata->lockowner); msg.rpc_cred = state->owner->so_cred; } else { memcpy(&rdata->args.stateid, &zero_stateid, sizeof(rdata->args.stateid)); @@ -1127,7 +1121,7 @@ nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp) if (filp) { struct nfs4_state *state; state = (struct nfs4_state *)filp->private_data; - memcpy(&wdata->args.stateid, &state->stateid, sizeof(wdata->args.stateid)); + nfs4_copy_stateid(&wdata->args.stateid, state, wdata->lockowner); msg.rpc_cred = state->owner->so_cred; } else { memcpy(&wdata->args.stateid, &zero_stateid, sizeof(wdata->args.stateid)); @@ -1163,7 +1157,7 @@ nfs4_proc_commit(struct nfs_write_data *cdata, struct file *filp) if (filp) { struct nfs4_state *state; state = (struct nfs4_state *)filp->private_data; - memcpy(&cdata->args.stateid, &state->stateid, sizeof(cdata->args.stateid)); + nfs4_copy_stateid(&cdata->args.stateid, state, cdata->lockowner); msg.rpc_cred = state->owner->so_cred; } else { memcpy(&cdata->args.stateid, &zero_stateid, sizeof(cdata->args.stateid)); @@ -1513,7 +1507,7 @@ nfs4_restart_read(struct rpc_task *task) rpc_restart_call(task); req = nfs_list_entry(data->pages.next); if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); } @@ -1564,8 +1558,9 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count) data->res.eof = 0; data->timestamp = jiffies; + data->lockowner = req->wb_lockowner; if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); @@ -1605,7 +1600,7 @@ nfs4_restart_write(struct rpc_task *task) rpc_restart_call(task); req = nfs_list_entry(data->pages.next); if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); } @@ -1661,8 +1656,9 @@ nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how) data->res.verf = &data->verf; data->timestamp = jiffies; + data->lockowner = req->wb_lockowner; if (req->wb_state) - memcpy(&data->args.stateid, &req->wb_state->stateid, sizeof(data->args.stateid)); + nfs4_copy_stateid(&data->args.stateid, req->wb_state, req->wb_lockowner); else memcpy(&data->args.stateid, &zero_stateid, sizeof(data->args.stateid)); @@ -1846,6 +1842,7 @@ nfs4_request_init(struct nfs_page *req, struct file *filp) state = (struct nfs4_state *)filp->private_data; req->wb_state = state; req->wb_cred = get_rpccred(state->owner->so_cred); + req->wb_lockowner = current->files; } static int @@ -1975,6 +1972,8 @@ nfs4_request_compatible(struct nfs_page *req, struct file *filp, struct page *pa state = (struct nfs4_state *)filp->private_data; if (req->wb_state != state) return 0; + if (req->wb_lockowner != current->files) + return 0; cred = state->owner->so_cred; if (req->wb_cred != cred) return 0; @@ -2032,6 +2031,262 @@ nfs4_proc_setclientid_confirm(struct nfs4_client *clp) return status; } +#define NFS4_LOCK_MINTIMEOUT (1 * HZ) +#define NFS4_LOCK_MAXTIMEOUT (30 * HZ) + +/* + * sleep, with exponential backoff, and retry the LOCK operation. + */ +static unsigned long +nfs4_set_lock_task_retry(unsigned long timeout) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(timeout); + timeout <<= 1; + if (timeout > NFS4_LOCK_MAXTIMEOUT) + return NFS4_LOCK_MAXTIMEOUT; + return timeout; +} + +static inline int +nfs4_lck_type(int cmd, struct file_lock *request) +{ + /* set lock type */ + switch (request->fl_type) { + case F_RDLCK: + return IS_SETLKW(cmd) ? NFS4_READW_LT : NFS4_READ_LT; + case F_WRLCK: + return IS_SETLKW(cmd) ? NFS4_WRITEW_LT : NFS4_WRITE_LT; + case F_UNLCK: + return NFS4_WRITE_LT; + } + BUG(); +} + +static inline uint64_t +nfs4_lck_length(struct file_lock *request) +{ + if (request->fl_end == OFFSET_MAX) + return ~(uint64_t)0; + return request->fl_end - request->fl_start + 1; +} + +int +nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request) +{ + struct inode *inode = state->inode; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs4_client *clp = server->nfs4_state; + struct nfs_lockargs arg = { + .fh = NFS_FH(inode), + .type = nfs4_lck_type(cmd, request), + .offset = request->fl_start, + .length = nfs4_lck_length(request), + }; + struct nfs_lockres res = { + .server = server, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKT], + .rpc_argp = &arg, + .rpc_resp = &res, + .rpc_cred = state->owner->so_cred, + }; + struct nfs_lowner nlo; + struct nfs4_lock_state *lsp; + int status; + + nlo.clientid = clp->cl_clientid; + down(&state->lock_sema); + lsp = nfs4_find_lock_state(state, request->fl_owner); + if (lsp) + nlo.id = lsp->ls_id; + else { + spin_lock(&clp->cl_lock); + nlo.id = nfs4_alloc_lockowner_id(clp); + spin_unlock(&clp->cl_lock); + } + arg.u.lockt = &nlo; + status = rpc_call_sync(server->client, &msg, 0); + if (!status) { + request->fl_type = F_UNLCK; + } else if (status == -NFS4ERR_DENIED) { + int64_t len, start, end; + start = res.u.denied.offset; + len = res.u.denied.length; + end = start + len - 1; + if (end < 0 || len == 0) + request->fl_end = OFFSET_MAX; + else + request->fl_end = (loff_t)end; + request->fl_start = (loff_t)start; + request->fl_type = F_WRLCK; + if (res.u.denied.type & 1) + request->fl_type = F_RDLCK; + request->fl_pid = 0; + status = 0; + } + if (lsp) + nfs4_put_lock_state(lsp); + up(&state->lock_sema); + return status; +} + +int +nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) +{ + struct inode *inode = state->inode; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_lockargs arg = { + .fh = NFS_FH(inode), + .type = nfs4_lck_type(cmd, request), + .offset = request->fl_start, + .length = nfs4_lck_length(request), + }; + struct nfs_lockres res = { + .server = server, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU], + .rpc_argp = &arg, + .rpc_resp = &res, + .rpc_cred = state->owner->so_cred, + }; + struct nfs4_lock_state *lsp; + struct nfs_locku_opargs luargs; + int status = 0; + + down(&state->lock_sema); + lsp = nfs4_find_lock_state(state, request->fl_owner); + if (!lsp) + goto out; + luargs.seqid = lsp->ls_seqid; + memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid)); + arg.u.locku = &luargs; + status = rpc_call_sync(server->client, &msg, 0); + nfs4_increment_lock_seqid(status, lsp); + + if (status == 0) { + memcpy(&lsp->ls_stateid, &res.u.stateid, + sizeof(lsp->ls_stateid)); + nfs4_notify_unlck(inode, request, lsp); + } + nfs4_put_lock_state(lsp); +out: + up(&state->lock_sema); + return status; +} + +static int +nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) +{ + struct inode *inode = state->inode; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs4_lock_state *lsp; + struct nfs_lockargs arg = { + .fh = NFS_FH(inode), + .type = nfs4_lck_type(cmd, request), + .offset = request->fl_start, + .length = nfs4_lck_length(request), + }; + struct nfs_lockres res = { + .server = server, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK], + .rpc_argp = &arg, + .rpc_resp = &res, + .rpc_cred = state->owner->so_cred, + }; + struct nfs_lock_opargs largs = { + .new_lock_owner = 0, + }; + int status; + + down(&state->lock_sema); + lsp = nfs4_find_lock_state(state, request->fl_owner); + if (lsp == NULL) { + struct nfs4_state_owner *owner = state->owner; + struct nfs_open_to_lock otl = { + .lock_owner.clientid = server->nfs4_state->cl_clientid, + }; + status = -ENOMEM; + lsp = nfs4_alloc_lock_state(state, request->fl_owner); + if (!lsp) + goto out; + otl.lock_seqid = lsp->ls_seqid; + otl.lock_owner.id = lsp->ls_id; + memcpy(&otl.open_stateid, &state->stateid, sizeof(otl.open_stateid)); + largs.u.open_lock = &otl; + largs.new_lock_owner = 1; + arg.u.lock = &largs; + down(&owner->so_sema); + otl.open_seqid = owner->so_seqid; + status = rpc_call_sync(server->client, &msg, 0); + /* increment open_owner seqid on success, and + * seqid mutating errors */ + nfs4_increment_seqid(status, owner); + up(&owner->so_sema); + } else { + struct nfs_exist_lock el = { + .seqid = lsp->ls_seqid, + }; + memcpy(&el.stateid, &lsp->ls_stateid, sizeof(el.stateid)); + largs.u.exist_lock = ⪙ + largs.new_lock_owner = 0; + arg.u.lock = &largs; + status = rpc_call_sync(server->client, &msg, 0); + } + /* increment seqid on success, and * seqid mutating errors*/ + nfs4_increment_lock_seqid(status, lsp); + /* save the returned stateid. */ + if (status == 0) { + memcpy(&lsp->ls_stateid, &res.u.stateid, sizeof(nfs4_stateid)); + nfs4_notify_setlk(inode, request, lsp); + } else if (status == -NFS4ERR_DENIED) + status = -EAGAIN; + nfs4_put_lock_state(lsp); +out: + up(&state->lock_sema); + return status; +} + +static int +nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request) +{ + struct nfs4_state *state; + unsigned long timeout = NFS4_LOCK_MINTIMEOUT; + int status; + + /* verify open state */ + state = (struct nfs4_state *)filp->private_data; + BUG_ON(!state); + + if (request->fl_start < 0 || request->fl_end < 0) + return -EINVAL; + + if (IS_GETLK(cmd)) + return nfs4_proc_getlk(state, F_GETLK, request); + + if (!(IS_SETLK(cmd) || IS_SETLKW(cmd))) + return -EINVAL; + + if (request->fl_type == F_UNLCK) + return nfs4_proc_unlck(state, cmd, request); + + do { + status = nfs4_proc_setlk(state, cmd, request); + if ((status != -EAGAIN) || IS_SETLK(cmd)) + break; + timeout = nfs4_set_lock_task_retry(timeout); + status = -ERESTARTSYS; + if (signalled()) + break; + } while(status < 0); + + return status; +} + struct nfs_rpc_ops nfs_v4_clientops = { .version = 4, /* protocol version */ .dentry_ops = &nfs4_dentry_operations, @@ -2067,6 +2322,7 @@ struct nfs_rpc_ops nfs_v4_clientops = { .file_release = nfs4_proc_file_release, .request_init = nfs4_request_init, .request_compatible = nfs4_request_compatible, + .lock = nfs4_proc_lock, }; /* diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 0b391c7f4ce4..0694b2e13342 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -43,6 +43,7 @@ #include #include #include +#include #define OPENOWNER_POOL_SIZE 8 @@ -168,7 +169,7 @@ nfs4_put_client(struct nfs4_client *clp) nfs4_free_client(clp); } -static inline u32 +u32 nfs4_alloc_lockowner_id(struct nfs4_client *clp) { return clp->cl_lockowner_id ++; @@ -304,8 +305,12 @@ nfs4_alloc_open_state(void) state->state = 0; state->nreaders = 0; state->nwriters = 0; + state->flags = 0; memset(state->stateid.data, 0, sizeof(state->stateid.data)); atomic_set(&state->count, 1); + INIT_LIST_HEAD(&state->lock_states); + init_MUTEX(&state->lock_sema); + rwlock_init(&state->state_lock); return state; } @@ -453,7 +458,7 @@ nfs4_close_state(struct nfs4_state *state, mode_t mode) list_del_init(&state->inode_states); spin_unlock(&inode->i_lock); do { - newstate = 0; + newstate = 0; if (state->state == 0) break; if (state->nreaders) @@ -478,6 +483,171 @@ nfs4_close_state(struct nfs4_state *state, mode_t mode) nfs4_put_open_state(state); } +/* + * Search the state->lock_states for an existing lock_owner + * that is compatible with current->files + */ +static struct nfs4_lock_state * +__nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) +{ + struct nfs4_lock_state *pos; + list_for_each_entry(pos, &state->lock_states, ls_locks) { + if (pos->ls_owner != fl_owner) + continue; + atomic_inc(&pos->ls_count); + return pos; + } + return NULL; +} + +struct nfs4_lock_state * +nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) +{ + struct nfs4_lock_state *lsp; + read_lock(&state->state_lock); + lsp = __nfs4_find_lock_state(state, fl_owner); + read_unlock(&state->state_lock); + return lsp; +} + +/* + * Return a compatible lock_state. If no initialized lock_state structure + * exists, return an uninitialized one. + * + * The caller must be holding state->lock_sema + */ +struct nfs4_lock_state * +nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) +{ + struct nfs4_lock_state *lsp; + struct nfs4_client *clp = state->owner->so_client; + + lsp = kmalloc(sizeof(*lsp), GFP_KERNEL); + if (lsp == NULL) + return NULL; + lsp->ls_seqid = 0; /* arbitrary */ + lsp->ls_id = -1; + memset(lsp->ls_stateid.data, 0, sizeof(lsp->ls_stateid.data)); + atomic_set(&lsp->ls_count, 1); + lsp->ls_owner = fl_owner; + lsp->ls_parent = state; + INIT_LIST_HEAD(&lsp->ls_locks); + spin_lock(&clp->cl_lock); + lsp->ls_id = nfs4_alloc_lockowner_id(clp); + spin_unlock(&clp->cl_lock); + return lsp; +} + +/* + * Byte-range lock aware utility to initialize the stateid of read/write + * requests. + */ +void +nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner) +{ + if (test_bit(LK_STATE_IN_USE, &state->flags)) { + struct nfs4_lock_state *lsp; + + lsp = nfs4_find_lock_state(state, fl_owner); + if (lsp) { + memcpy(dst, &lsp->ls_stateid, sizeof(*dst)); + nfs4_put_lock_state(lsp); + return; + } + } + memcpy(dst, &state->stateid, sizeof(*dst)); +} + +/* +* Called with state->lock_sema held. +*/ +void +nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp) +{ + if (status == NFS_OK || seqid_mutating_err(-status)) + lsp->ls_seqid++; +} + +/* +* Check to see if the request lock (type FL_UNLK) effects the fl lock. +* +* fl and request must have the same posix owner +* +* return: +* 0 -> fl not effected by request +* 1 -> fl consumed by request +*/ + +static int +nfs4_check_unlock(struct file_lock *fl, struct file_lock *request) +{ + if (fl->fl_start >= request->fl_start && fl->fl_end <= request->fl_end) + return 1; + return 0; +} + +/* + * Post an initialized lock_state on the state->lock_states list. + */ +void +nfs4_notify_setlk(struct inode *inode, struct file_lock *request, struct nfs4_lock_state *lsp) +{ + struct nfs4_state *state = lsp->ls_parent; + + if (!list_empty(&lsp->ls_locks)) + return; + write_lock(&state->state_lock); + list_add(&lsp->ls_locks, &state->lock_states); + set_bit(LK_STATE_IN_USE, &state->flags); + write_unlock(&state->state_lock); +} + +/* + * to decide to 'reap' lock state: + * 1) search i_flock for file_locks with fl.lock_state = to ls. + * 2) determine if unlock will consume found lock. + * if so, reap + * + * else, don't reap. + * + */ +void +nfs4_notify_unlck(struct inode *inode, struct file_lock *request, struct nfs4_lock_state *lsp) +{ + struct nfs4_state *state = lsp->ls_parent; + struct file_lock *fl; + + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { + if (!(fl->fl_flags & FL_POSIX)) + continue; + if (fl->fl_owner != lsp->ls_owner) + continue; + /* Exit if we find at least one lock which is not consumed */ + if (nfs4_check_unlock(fl,request) == 0) + return; + } + + write_lock(&state->state_lock); + list_del_init(&lsp->ls_locks); + if (list_empty(&state->lock_states)) + clear_bit(LK_STATE_IN_USE, &state->flags); + write_unlock(&state->state_lock); +} + +/* + * Release reference to lock_state, and free it if we see that + * it is no longer in use + */ +void +nfs4_put_lock_state(struct nfs4_lock_state *lsp) +{ + if (!atomic_dec_and_test(&lsp->ls_count)) + return; + if (!list_empty(&lsp->ls_locks)) + return; + kfree(lsp); +} + /* * Called with sp->so_sema held. * diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 3a372266bb03..f0a688fa675d 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -66,6 +66,10 @@ static int nfs_stat_to_errno(int); #define NFS4_MAXTAGLEN 0 #endif +/* lock,open owner id: + * we currently use size 1 (u32) out of (NFS4_OPAQUE_LIMIT >> 2) + */ +#define owner_id_maxsz 1 + 1 #define compound_encode_hdr_maxsz 3 + (NFS4_MAXTAGLEN >> 2) #define compound_decode_hdr_maxsz 2 + (NFS4_MAXTAGLEN >> 2) #define op_encode_hdr_maxsz 1 @@ -222,6 +226,36 @@ static int nfs_stat_to_errno(int); decode_setclientid_confirm_maxsz + \ decode_putrootfh_maxsz + \ decode_fsinfo_maxsz +#define NFS4_enc_lock_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz + \ + op_encode_hdr_maxsz + \ + 1 + 1 + 2 + 2 + \ + 1 + 4 + 1 + 2 + \ + owner_id_maxsz +#define NFS4_dec_lock_sz compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_getattr_maxsz + \ + op_decode_hdr_maxsz + \ + 2 + 2 + 1 + 2 + \ + owner_id_maxsz +#define NFS4_enc_lockt_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz + \ + op_encode_hdr_maxsz + \ + 1 + 2 + 2 + 2 + \ + owner_id_maxsz +#define NFS4_dec_lockt_sz NFS4_dec_lock_sz +#define NFS4_enc_locku_sz compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz + \ + op_encode_hdr_maxsz + \ + 1 + 1 + 4 + 2 + 2 +#define NFS4_dec_locku_sz compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + decode_getattr_maxsz + \ + op_decode_hdr_maxsz + 4 + static struct { @@ -596,6 +630,80 @@ encode_link(struct xdr_stream *xdr, struct nfs4_link *link) return 0; } +/* + * opcode,type,reclaim,offset,length,new_lock_owner = 32 + * open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40 + */ +static int +encode_lock(struct xdr_stream *xdr, struct nfs_lockargs *arg) +{ + uint32_t *p; + struct nfs_lock_opargs *opargs = arg->u.lock; + + RESERVE_SPACE(32); + WRITE32(OP_LOCK); + WRITE32(arg->type); + WRITE32(opargs->reclaim); + WRITE64(arg->offset); + WRITE64(arg->length); + WRITE32(opargs->new_lock_owner); + if (opargs->new_lock_owner){ + struct nfs_open_to_lock *ol = opargs->u.open_lock; + + RESERVE_SPACE(40); + WRITE32(ol->open_seqid); + WRITEMEM(&ol->open_stateid, sizeof(ol->open_stateid)); + WRITE32(ol->lock_seqid); + WRITE64(ol->lock_owner.clientid); + WRITE32(4); + WRITE32(ol->lock_owner.id); + } + else { + struct nfs_exist_lock *el = opargs->u.exist_lock; + + RESERVE_SPACE(20); + WRITEMEM(&el->stateid, sizeof(el->stateid)); + WRITE32(el->seqid); + } + + return 0; +} + +static int +encode_lockt(struct xdr_stream *xdr, struct nfs_lockargs *arg) +{ + uint32_t *p; + struct nfs_lowner *opargs = arg->u.lockt; + + RESERVE_SPACE(40); + WRITE32(OP_LOCKT); + WRITE32(arg->type); + WRITE64(arg->offset); + WRITE64(arg->length); + WRITE64(opargs->clientid); + WRITE32(4); + WRITE32(opargs->id); + + return 0; +} + +static int +encode_locku(struct xdr_stream *xdr, struct nfs_lockargs *arg) +{ + uint32_t *p; + struct nfs_locku_opargs *opargs = arg->u.locku; + + RESERVE_SPACE(44); + WRITE32(OP_LOCKU); + WRITE32(arg->type); + WRITE32(opargs->seqid); + WRITEMEM(&opargs->stateid, sizeof(opargs->stateid)); + WRITE64(arg->offset); + WRITE64(arg->length); + + return 0; +} + static int encode_lookup(struct xdr_stream *xdr, struct nfs4_lookup *lookup) { @@ -1175,6 +1283,72 @@ out: return status; } +/* + * Encode a LOCK request + */ +static int +nfs4_xdr_enc_lock(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if(status) + goto out; + status = encode_lock(&xdr, args); +out: + return status; +} + +/* + * Encode a LOCKT request + */ +static int +nfs4_xdr_enc_lockt(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if(status) + goto out; + status = encode_lockt(&xdr, args); +out: + return status; +} + +/* + * Encode a LOCKU request + */ +static int +nfs4_xdr_enc_locku(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 2, + }; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + status = encode_putfh(&xdr, args->fh); + if(status) + goto out; + status = encode_locku(&xdr, args); +out: + return status; +} + /* * Encode a READ request */ @@ -1997,6 +2171,66 @@ decode_link(struct xdr_stream *xdr, struct nfs4_link *link) return decode_change_info(xdr, link->ln_cinfo); } +/* + * We create the owner, so we know a proper owner.id length is 4. + */ +static int +decode_lock_denied (struct xdr_stream *xdr, struct nfs_lock_denied *denied) +{ + uint32_t *p; + uint32_t namelen; + + READ_BUF(32); + READ64(denied->offset); + READ64(denied->length); + READ32(denied->type); + READ64(denied->owner.clientid); + READ32(namelen); + READ_BUF(namelen); + if (namelen == 4) + READ32(denied->owner.id); + return -NFS4ERR_DENIED; +} + +static int +decode_lock(struct xdr_stream *xdr, struct nfs_lockres *res) +{ + uint32_t *p; + int status; + + status = decode_op_hdr(xdr, OP_LOCK); + if (status == 0) { + READ_BUF(sizeof(nfs4_stateid)); + COPYMEM(&res->u.stateid, sizeof(res->u.stateid)); + } else if (status == -NFS4ERR_DENIED) + return decode_lock_denied(xdr, &res->u.denied); + return status; +} + +static int +decode_lockt(struct xdr_stream *xdr, struct nfs_lockres *res) +{ + int status; + status = decode_op_hdr(xdr, OP_LOCKT); + if (status == -NFS4ERR_DENIED) + return decode_lock_denied(xdr, &res->u.denied); + return status; +} + +static int +decode_locku(struct xdr_stream *xdr, struct nfs_lockres *res) +{ + uint32_t *p; + int status; + + status = decode_op_hdr(xdr, OP_LOCKU); + if (status == 0) { + READ_BUF(sizeof(nfs4_stateid)); + COPYMEM(&res->u.stateid, sizeof(res->u.stateid)); + } + return status; +} + static int decode_lookup(struct xdr_stream *xdr) { @@ -2037,10 +2271,11 @@ static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res) { uint32_t *p; + int status; - res->status = decode_op_hdr(xdr, OP_OPEN_CONFIRM); - if (res->status) - return res->status; + status = decode_op_hdr(xdr, OP_OPEN_CONFIRM); + if (status) + return status; READ_BUF(sizeof(res->stateid.data)); COPYMEM(res->stateid.data, sizeof(res->stateid.data)); return 0; @@ -2619,6 +2854,71 @@ out: return status; } +/* + * Decode LOCK response + */ +static int +nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_lock(&xdr, res); +out: + return status; +} + +/* + * Decode LOCKT response + */ +static int +nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_lockt(&xdr, res); +out: + return status; +} + +/* + * Decode LOCKU response + */ +static int +nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status) + goto out; + status = decode_putfh(&xdr); + if (status) + goto out; + status = decode_locku(&xdr, res); +out: + return status; +} /* * Decode Read response @@ -2915,6 +3215,9 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(RENEW, enc_renew, dec_renew), PROC(SETCLIENTID, enc_setclientid, dec_setclientid), PROC(SETCLIENTID_CONFIRM, enc_setclientid_confirm, dec_setclientid_confirm), + PROC(LOCK, enc_lock, dec_lock), + PROC(LOCKT, enc_lockt, dec_lockt), + PROC(LOCKU, enc_locku, dec_locku), }; struct rpc_version nfs_version4 = { diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 79f18e4cfb81..3b118742286f 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #define NFSDBG_FACILITY NFSDBG_PROC @@ -653,6 +654,12 @@ nfs_request_compatible(struct nfs_page *req, struct file *filp, struct page *pag return 1; } +static int +nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl) +{ + return nlmclnt_proc(filp->f_dentry->d_inode, cmd, fl); +} + struct nfs_rpc_ops nfs_v2_clientops = { .version = 2, /* protocol version */ @@ -689,4 +696,5 @@ struct nfs_rpc_ops nfs_v2_clientops = { .file_release = nfs_release, .request_init = nfs_request_init, .request_compatible = nfs_request_compatible, + .lock = nfs_proc_lock, }; diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 4a61a4682718..35baf20a5b5c 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -297,6 +297,9 @@ enum { NFSPROC4_CLNT_RENEW, NFSPROC4_CLNT_SETCLIENTID, NFSPROC4_CLNT_SETCLIENTID_CONFIRM, + NFSPROC4_CLNT_LOCK, + NFSPROC4_CLNT_LOCKT, + NFSPROC4_CLNT_LOCKU, }; #endif diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 512d9203905f..524eb6d04d7b 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -542,19 +542,43 @@ struct nfs4_state_owner { /* * struct nfs4_state maintains the client-side state for a given - * (state_owner,inode) tuple. + * (state_owner,inode) tuple (OPEN) or state_owner (LOCK). * + * OPEN: * In order to know when to OPEN_DOWNGRADE or CLOSE the state on the server, * we need to know how many files are open for reading or writing on a * given inode. This information too is stored here. + * + * LOCK: one nfs4_state (LOCK) to hold the lock stateid nfs4_state(OPEN) */ + +struct nfs4_lock_state { + struct list_head ls_locks; /* Other lock stateids */ + fl_owner_t ls_owner; /* POSIX lock owner */ + struct nfs4_state * ls_parent; /* Parent nfs4_state */ + u32 ls_seqid; + u32 ls_id; + nfs4_stateid ls_stateid; + atomic_t ls_count; +}; + +/* bits for nfs4_state->flags */ +enum { + LK_STATE_IN_USE, +}; + struct nfs4_state { struct list_head open_states; /* List of states for the same state_owner */ struct list_head inode_states; /* List of states for the same inode */ + struct list_head lock_states; /* List of subservient lock stateids */ struct nfs4_state_owner *owner; /* Pointer to the open owner */ struct inode *inode; /* Pointer to the inode */ + unsigned long flags; /* Do we hold any locks? */ + struct semaphore lock_sema; /* Serializes file locking operations */ + rwlock_t state_lock; /* Protects the lock_states list */ + nfs4_stateid stateid; unsigned int nreaders; @@ -589,6 +613,8 @@ extern void init_nfsv4_state(struct nfs_server *); extern void destroy_nfsv4_state(struct nfs_server *); extern struct nfs4_client *nfs4_get_client(struct in_addr *); extern void nfs4_put_client(struct nfs4_client *clp); +extern u32 nfs4_alloc_lockowner_id(struct nfs4_client *); + extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); extern void nfs4_put_state_owner(struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); @@ -598,6 +624,15 @@ extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mod extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp); extern int nfs4_handle_error(struct nfs_server *, int); extern void nfs4_schedule_state_recovery(struct nfs4_client *); +extern struct nfs4_lock_state *nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t); +extern struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t); +extern void nfs4_put_lock_state(struct nfs4_lock_state *state); +extern void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *ls); +extern void nfs4_notify_setlk(struct inode *, struct file_lock *, struct nfs4_lock_state *); +extern void nfs4_notify_unlck(struct inode *, struct file_lock *, struct nfs4_lock_state *); +extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t); + + struct nfs4_mount_data; #else diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 42677b62e92b..c41a4e75555e 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -26,6 +26,7 @@ struct nfs_page { struct list_head wb_list, /* Defines state of page: */ *wb_list_head; /* read/write/commit */ struct file *wb_file; + fl_owner_t wb_lockowner; struct inode *wb_inode; struct rpc_cred *wb_cred; struct nfs4_state *wb_state; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 21827ad1a71e..a3ecfab78bc6 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -109,7 +109,6 @@ struct nfs_openargs { }; struct nfs_openres { - __u32 status; nfs4_stateid stateid; struct nfs_fh fh; struct nfs4_change_info * cinfo; @@ -129,7 +128,6 @@ struct nfs_open_confirmargs { }; struct nfs_open_confirmres { - __u32 status; nfs4_stateid stateid; }; @@ -157,10 +155,68 @@ struct nfs_closeargs { }; struct nfs_closeres { - __u32 status; nfs4_stateid stateid; }; +/* + * * Arguments to the lock,lockt, and locku call. + * */ +struct nfs_lowner { + __u64 clientid; + u32 id; +}; + +struct nfs_open_to_lock { + __u32 open_seqid; + nfs4_stateid open_stateid; + __u32 lock_seqid; + struct nfs_lowner lock_owner; +}; + +struct nfs_exist_lock { + nfs4_stateid stateid; + __u32 seqid; +}; +struct nfs_lock_opargs { + __u32 reclaim; + __u32 new_lock_owner; + union { + struct nfs_open_to_lock *open_lock; + struct nfs_exist_lock *exist_lock; + } u; +}; + +struct nfs_locku_opargs { + __u32 seqid; + nfs4_stateid stateid; +}; + +struct nfs_lockargs { + struct nfs_fh * fh; + __u32 type; + __u64 offset; + __u64 length; + union { + struct nfs_lock_opargs *lock; /* LOCK */ + struct nfs_lowner *lockt; /* LOCKT */ + struct nfs_locku_opargs *locku; /* LOCKU */ + } u; +}; + +struct nfs_lock_denied { + __u64 offset; + __u64 length; + __u32 type; + struct nfs_lowner owner; +}; + +struct nfs_lockres { + union { + nfs4_stateid stateid;/* LOCK success, LOCKU */ + struct nfs_lock_denied denied; /* LOCK failed, LOCKT success */ + } u; + struct nfs_server * server; +}; /* * Arguments to the read call. @@ -605,6 +661,7 @@ struct nfs_read_data { struct rpc_task task; struct inode *inode; struct rpc_cred *cred; + fl_owner_t lockowner; struct nfs_fattr fattr; /* fattr storage */ struct list_head pages; /* Coalesced read requests */ struct page *pagevec[NFS_READ_MAXIOV]; @@ -620,6 +677,7 @@ struct nfs_write_data { struct rpc_task task; struct inode *inode; struct rpc_cred *cred; + fl_owner_t lockowner; struct nfs_fattr fattr; struct nfs_writeverf verf; struct list_head pages; /* Coalesced requests we wish to flush */ @@ -686,6 +744,7 @@ struct nfs_rpc_ops { int (*file_release) (struct inode *, struct file *); void (*request_init)(struct nfs_page *, struct file *); int (*request_compatible)(struct nfs_page *, struct file *, struct page *); + int (*lock)(struct file *, int, struct file_lock *); }; /* -- cgit v1.2.3 From 87afa29af1652cfd6c8ad35aadb3102aee4e02a9 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 7 Feb 2004 17:41:41 -0800 Subject: [PATCH] add device id to radeonfb From: Andreas Steinmetz The attached patch adds the pci id 5961 to radeonfb. Without the patch my 9200 displays only a blank screen. lspci output below. 05:00.0 VGA compatible controller: ATI Technologies Inc Radeon RV280 [Radeon 9200] (rev 01) (prog-if 00 [VGA]) Subsystem: Giga-byte Technology: Unknown device 4018 Flags: bus master, 66Mhz, medium devsel, latency 64, IRQ 16 Memory at e0000000 (32-bit, prefetchable) [size=128M] I/O ports at b800 [size=256] Memory at feaf0000 (32-bit, non-prefetchable) [size=64K] Expansion ROM at feac0000 [disabled] [size=128K] Capabilities: [58] AGP version 3.0 Capabilities: [50] Power Management version 2 --- drivers/video/radeonfb.c | 2 ++ include/linux/pci_ids.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include/linux') diff --git a/drivers/video/radeonfb.c b/drivers/video/radeonfb.c index cb7b2b74ded3..8569e1614528 100644 --- a/drivers/video/radeonfb.c +++ b/drivers/video/radeonfb.c @@ -114,6 +114,7 @@ enum radeon_chips { RADEON_Ie, RADEON_If, RADEON_Ig, + RADEON_Ya, RADEON_Yd, RADEON_Ld, RADEON_Le, @@ -208,6 +209,7 @@ static struct pci_device_id radeonfb_pci_table[] = { { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ie, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ie}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_If, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_If}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ig, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ig}, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ya, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ya}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Yd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Yd}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ld, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ld}, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Le, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Le}, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index c21bb5925bd1..79a90610f302 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -291,6 +291,7 @@ #define PCI_DEVICE_ID_ATI_RADEON_Ig 0x4967 /* Radeon RV280 (9200) */ #define PCI_DEVICE_ID_ATI_RADEON_Y_ 0x5960 +#define PCI_DEVICE_ID_ATI_RADEON_Ya 0x5961 #define PCI_DEVICE_ID_ATI_RADEON_Yd 0x5964 /* Radeon R300 (9500) */ #define PCI_DEVICE_ID_ATI_RADEON_AD 0x4144 -- cgit v1.2.3 From 6a5b27ef8bbe9e800b6a3f7662a380392f1b3040 Mon Sep 17 00:00:00 2001 From: Hideaki Yoshifuji Date: Sat, 7 Feb 2004 20:54:14 -0800 Subject: [IPV6]: Kill broken and unused IPV6_AUTHHDR support. --- include/linux/ipv6.h | 2 -- net/ipv6/datagram.c | 24 ------------------------ net/ipv6/exthdrs.c | 26 -------------------------- net/ipv6/ipv6_sockglue.c | 9 --------- 4 files changed, 61 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index ab97fc520921..29911bf4218e 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -185,7 +185,6 @@ struct inet6_skb_parm int iif; __u16 ra; __u16 hop; - __u16 auth; __u16 dst0; __u16 srcrt; __u16 dst1; @@ -211,7 +210,6 @@ struct ipv6_pinfo { rxhlim:1, hopopts:1, dstopts:1, - authhdr:1, rxflow:1; } bits; __u8 all; diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index a32e0b5d9c82..8c39cf5f6e34 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -242,10 +242,6 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt); put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr); } - if (np->rxopt.bits.authhdr && opt->auth) { - u8 *ptr = skb->nh.raw + opt->auth; - put_cmsg(msg, SOL_IPV6, IPV6_AUTHHDR, (ptr[1]+1)<<2, ptr); - } if (np->rxopt.bits.dstopts && opt->dst1) { u8 *ptr = skb->nh.raw + opt->dst1; put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); @@ -378,26 +374,6 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, opt->dst1opt = hdr; break; - case IPV6_AUTHHDR: - if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) { - err = -EINVAL; - goto exit_f; - } - - hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg); - len = ((hdr->hdrlen + 2) << 2); - if (cmsg->cmsg_len < CMSG_LEN(len)) { - err = -EINVAL; - goto exit_f; - } - if (len & ~7) { - err = -EINVAL; - goto exit_f; - } - opt->opt_flen += len; - opt->auth = hdr; - break; - case IPV6_RTHDR: if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) { err = -EINVAL; diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 65f00568b1a4..93b2f8bbf65b 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -518,17 +518,6 @@ static u8 *ipv6_build_exthdr(struct sk_buff *skb, u8 *prev_hdr, u8 type, struct return &h->nexthdr; } -static u8 *ipv6_build_authhdr(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_opt_hdr *opt) -{ - struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, (opt->hdrlen+2)<<2); - - memcpy(h, opt, (opt->hdrlen+2)<<2); - h->nexthdr = *prev_hdr; - *prev_hdr = NEXTHDR_AUTH; - return &h->nexthdr; -} - - u8 *ipv6_build_nfrag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt, struct in6_addr *daddr, u32 jumbolen) { @@ -567,8 +556,6 @@ u8 *ipv6_build_nfrag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptio u8 *ipv6_build_frag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt) { - if (opt->auth) - prev_hdr = ipv6_build_authhdr(skb, prev_hdr, opt->auth); if (opt->dst1opt) prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst1opt); return prev_hdr; @@ -608,15 +595,6 @@ static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv *proto = type; } -static void ipv6_push_authhdr(struct sk_buff *skb, u8 *proto, struct ipv6_opt_hdr *opt) -{ - struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, (opt->hdrlen+2)<<2); - - memcpy(h, opt, (opt->hdrlen+2)<<2); - h->nexthdr = *proto; - *proto = NEXTHDR_AUTH; -} - void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto, struct in6_addr **daddr) @@ -633,8 +611,6 @@ void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *pr { if (opt->dst1opt) ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt); - if (opt->auth) - ipv6_push_authhdr(skb, proto, opt->auth); } struct ipv6_txoptions * @@ -652,8 +628,6 @@ ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) *((char**)&opt2->dst0opt) += dif; if (opt2->dst1opt) *((char**)&opt2->dst1opt) += dif; - if (opt2->auth) - *((char**)&opt2->auth) += dif; if (opt2->srcrt) *((char**)&opt2->srcrt) += dif; } diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index c9aa51f318b3..52c02189c483 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -230,11 +230,6 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, retv = 0; break; - case IPV6_AUTHHDR: - np->rxopt.bits.authhdr = valbool; - retv = 0; - break; - case IPV6_DSTOPTS: np->rxopt.bits.dstopts = valbool; retv = 0; @@ -623,10 +618,6 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval, val = np->rxopt.bits.hopopts; break; - case IPV6_AUTHHDR: - val = np->rxopt.bits.authhdr; - break; - case IPV6_DSTOPTS: val = np->rxopt.bits.dstopts; break; -- cgit v1.2.3 From d89c83bebae011a76e1ca9ec65c973aac0ad5da9 Mon Sep 17 00:00:00 2001 From: Adam Belay Date: Sun, 8 Feb 2004 15:28:23 +0000 Subject: [PNP]: Resource flags update This patch reorganizes resource flags to ensure that manual resource settings are properly recognized. This fix is necessary for many ALSA drivers. It also prevents comparisons between unset resource structures. The bug was discovered by Rene Herman , who also wrote an initial version of this patch. I made further improvements to ensure that the pnp subsystem was compatible with this initial change. --- drivers/pnp/isapnp/core.c | 8 ++++---- drivers/pnp/manager.c | 18 +++++++++--------- drivers/pnp/pnpbios/rsparser.c | 8 ++++---- drivers/pnp/resource.c | 19 +++++++++++-------- include/linux/pnp.h | 16 ++++++++++++---- 5 files changed, 40 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c index b5e3e898f8e5..453f69c55967 100644 --- a/drivers/pnp/isapnp/core.c +++ b/drivers/pnp/isapnp/core.c @@ -1039,17 +1039,17 @@ static int isapnp_set_resources(struct pnp_dev *dev, struct pnp_resource_table * isapnp_cfg_begin(dev->card->number, dev->number); dev->active = 1; - for (tmp = 0; tmp < PNP_MAX_PORT && res->port_resource[tmp].flags & IORESOURCE_IO; tmp++) + for (tmp = 0; tmp < PNP_MAX_PORT && (res->port_resource[tmp].flags & (IORESOURCE_IO | IORESOURCE_UNSET)) == IORESOURCE_IO; tmp++) isapnp_write_word(ISAPNP_CFG_PORT+(tmp<<1), res->port_resource[tmp].start); - for (tmp = 0; tmp < PNP_MAX_IRQ && res->irq_resource[tmp].flags & IORESOURCE_IRQ; tmp++) { + for (tmp = 0; tmp < PNP_MAX_IRQ && (res->irq_resource[tmp].flags & (IORESOURCE_IRQ | IORESOURCE_UNSET)) == IORESOURCE_IRQ; tmp++) { int irq = res->irq_resource[tmp].start; if (irq == 2) irq = 9; isapnp_write_byte(ISAPNP_CFG_IRQ+(tmp<<1), irq); } - for (tmp = 0; tmp < PNP_MAX_DMA && res->dma_resource[tmp].flags & IORESOURCE_DMA; tmp++) + for (tmp = 0; tmp < PNP_MAX_DMA && (res->dma_resource[tmp].flags & (IORESOURCE_DMA | IORESOURCE_UNSET)) == IORESOURCE_DMA; tmp++) isapnp_write_byte(ISAPNP_CFG_DMA+tmp, res->dma_resource[tmp].start); - for (tmp = 0; tmp < PNP_MAX_MEM && res->mem_resource[tmp].flags & IORESOURCE_MEM; tmp++) + for (tmp = 0; tmp < PNP_MAX_MEM && (res->mem_resource[tmp].flags & (IORESOURCE_MEM | IORESOURCE_UNSET)) == IORESOURCE_MEM; tmp++) isapnp_write_word(ISAPNP_CFG_MEM+(tmp<<2), (res->mem_resource[tmp].start >> 8) & 0xffff); /* FIXME: We aren't handling 32bit mems properly here */ isapnp_activate(dev->number); diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index 36b6220a6335..bd344233e5e8 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -223,25 +223,25 @@ void pnp_init_resource_table(struct pnp_resource_table *table) table->irq_resource[idx].name = NULL; table->irq_resource[idx].start = -1; table->irq_resource[idx].end = -1; - table->irq_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + table->irq_resource[idx].flags = IORESOURCE_IRQ | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_DMA; idx++) { table->dma_resource[idx].name = NULL; table->dma_resource[idx].start = -1; table->dma_resource[idx].end = -1; - table->dma_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + table->dma_resource[idx].flags = IORESOURCE_DMA | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_PORT; idx++) { table->port_resource[idx].name = NULL; table->port_resource[idx].start = 0; table->port_resource[idx].end = 0; - table->port_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + table->port_resource[idx].flags = IORESOURCE_IO | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_MEM; idx++) { table->mem_resource[idx].name = NULL; table->mem_resource[idx].start = 0; table->mem_resource[idx].end = 0; - table->mem_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + table->mem_resource[idx].flags = IORESOURCE_MEM | IORESOURCE_AUTO | IORESOURCE_UNSET; } } @@ -258,28 +258,28 @@ static void pnp_clean_resource_table(struct pnp_resource_table * res) continue; res->irq_resource[idx].start = -1; res->irq_resource[idx].end = -1; - res->irq_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + res->irq_resource[idx].flags = IORESOURCE_IRQ | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_DMA; idx++) { if (!(res->dma_resource[idx].flags & IORESOURCE_AUTO)) continue; res->dma_resource[idx].start = -1; res->dma_resource[idx].end = -1; - res->dma_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + res->dma_resource[idx].flags = IORESOURCE_DMA | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_PORT; idx++) { if (!(res->port_resource[idx].flags & IORESOURCE_AUTO)) continue; res->port_resource[idx].start = 0; res->port_resource[idx].end = 0; - res->port_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + res->port_resource[idx].flags = IORESOURCE_IO | IORESOURCE_AUTO | IORESOURCE_UNSET; } for (idx = 0; idx < PNP_MAX_MEM; idx++) { if (!(res->mem_resource[idx].flags & IORESOURCE_AUTO)) continue; res->mem_resource[idx].start = 0; res->mem_resource[idx].end = 0; - res->mem_resource[idx].flags = IORESOURCE_AUTO | IORESOURCE_UNSET; + res->mem_resource[idx].flags = IORESOURCE_MEM | IORESOURCE_AUTO | IORESOURCE_UNSET; } } @@ -550,7 +550,7 @@ void pnp_resource_change(struct resource *resource, unsigned long start, unsigne { if (resource == NULL) return; - resource->flags &= ~IORESOURCE_AUTO; + resource->flags &= ~(IORESOURCE_AUTO | IORESOURCE_UNSET); resource->start = start; resource->end = start + size - 1; } diff --git a/drivers/pnp/pnpbios/rsparser.c b/drivers/pnp/pnpbios/rsparser.c index 7af3cb0ceceb..758d6804f0ff 100644 --- a/drivers/pnp/pnpbios/rsparser.c +++ b/drivers/pnp/pnpbios/rsparser.c @@ -49,7 +49,7 @@ static void pnpbios_parse_allocated_irqresource(struct pnp_resource_table * res, int irq) { int i = 0; - while ((res->irq_resource[i].flags & IORESOURCE_IRQ) && i < PNP_MAX_IRQ) i++; + while (!(res->irq_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_IRQ) i++; if (i < PNP_MAX_IRQ) { res->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag if (irq == -1) { @@ -65,7 +65,7 @@ static void pnpbios_parse_allocated_dmaresource(struct pnp_resource_table * res, int dma) { int i = 0; - while ((res->dma_resource[i].flags & IORESOURCE_DMA) && i < PNP_MAX_DMA) i++; + while (!(res->dma_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_DMA) i++; if (i < PNP_MAX_DMA) { res->dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag if (dma == -1) { @@ -81,7 +81,7 @@ static void pnpbios_parse_allocated_ioresource(struct pnp_resource_table * res, int io, int len) { int i = 0; - while ((res->port_resource[i].flags & IORESOURCE_IO) && i < PNP_MAX_PORT) i++; + while (!(res->port_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_PORT) i++; if (i < PNP_MAX_PORT) { res->port_resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag if (len <= 0 || (io + len -1) >= 0x10003) { @@ -97,7 +97,7 @@ static void pnpbios_parse_allocated_memresource(struct pnp_resource_table * res, int mem, int len) { int i = 0; - while ((res->mem_resource[i].flags & IORESOURCE_MEM) && i < PNP_MAX_MEM) i++; + while (!(res->mem_resource[i].flags & IORESOURCE_UNSET) && i < PNP_MAX_MEM) i++; if (i < PNP_MAX_MEM) { res->mem_resource[i].flags = IORESOURCE_MEM; // Also clears _UNSET flag if (len <= 0) { diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index dfe565a82ea8..1c1ab31a619f 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -241,6 +241,9 @@ void pnp_free_option(struct pnp_option *option) (*(enda) >= *(startb) && *(enda) <= *(endb)) || \ (*(starta) < *(startb) && *(enda) > *(endb))) +#define cannot_compare(flags) \ +((flags) & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) + int pnp_check_port(struct pnp_dev * dev, int idx) { int tmp; @@ -250,7 +253,7 @@ int pnp_check_port(struct pnp_dev * dev, int idx) end = &dev->res.port_resource[idx].end; /* if the resource doesn't exist, don't complain about it */ - if (dev->res.port_resource[idx].flags & IORESOURCE_UNSET) + if (cannot_compare(dev->res.port_resource[idx].flags)) return 1; /* check if the resource is already in use, skip if the @@ -284,7 +287,7 @@ int pnp_check_port(struct pnp_dev * dev, int idx) continue; for (tmp = 0; tmp < PNP_MAX_PORT; tmp++) { if (tdev->res.port_resource[tmp].flags & IORESOURCE_IO) { - if (pnp_port_flags(dev, tmp) & IORESOURCE_DISABLED) + if (cannot_compare(tdev->res.port_resource[tmp].flags)) continue; tport = &tdev->res.port_resource[tmp].start; tend = &tdev->res.port_resource[tmp].end; @@ -306,7 +309,7 @@ int pnp_check_mem(struct pnp_dev * dev, int idx) end = &dev->res.mem_resource[idx].end; /* if the resource doesn't exist, don't complain about it */ - if (dev->res.mem_resource[idx].flags & IORESOURCE_UNSET) + if (cannot_compare(dev->res.mem_resource[idx].flags)) return 1; /* check if the resource is already in use, skip if the @@ -340,7 +343,7 @@ int pnp_check_mem(struct pnp_dev * dev, int idx) continue; for (tmp = 0; tmp < PNP_MAX_MEM; tmp++) { if (tdev->res.mem_resource[tmp].flags & IORESOURCE_MEM) { - if (pnp_mem_flags(dev, tmp) & IORESOURCE_DISABLED) + if (cannot_compare(tdev->res.mem_resource[tmp].flags)) continue; taddr = &tdev->res.mem_resource[tmp].start; tend = &tdev->res.mem_resource[tmp].end; @@ -365,7 +368,7 @@ int pnp_check_irq(struct pnp_dev * dev, int idx) unsigned long * irq = &dev->res.irq_resource[idx].start; /* if the resource doesn't exist, don't complain about it */ - if (dev->res.irq_resource[idx].flags & IORESOURCE_UNSET) + if (cannot_compare(dev->res.irq_resource[idx].flags)) return 1; /* check if the resource is valid */ @@ -411,7 +414,7 @@ int pnp_check_irq(struct pnp_dev * dev, int idx) continue; for (tmp = 0; tmp < PNP_MAX_IRQ; tmp++) { if (tdev->res.irq_resource[tmp].flags & IORESOURCE_IRQ) { - if (pnp_irq_flags(dev, tmp) & IORESOURCE_DISABLED) + if (cannot_compare(tdev->res.irq_resource[tmp].flags)) continue; if ((tdev->res.irq_resource[tmp].start == *irq)) return 0; @@ -429,7 +432,7 @@ int pnp_check_dma(struct pnp_dev * dev, int idx) unsigned long * dma = &dev->res.dma_resource[idx].start; /* if the resource doesn't exist, don't complain about it */ - if (dev->res.dma_resource[idx].flags & IORESOURCE_UNSET) + if (cannot_compare(dev->res.dma_resource[idx].flags)) return 1; /* check if the resource is valid */ @@ -464,7 +467,7 @@ int pnp_check_dma(struct pnp_dev * dev, int idx) continue; for (tmp = 0; tmp < PNP_MAX_DMA; tmp++) { if (tdev->res.dma_resource[tmp].flags & IORESOURCE_DMA) { - if (pnp_dma_flags(dev, tmp) & IORESOURCE_DISABLED) + if (cannot_compare(tdev->res.dma_resource[tmp].flags)) continue; if ((tdev->res.dma_resource[tmp].start == *dma)) return 0; diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 1dcef561a280..5aa6df8e1a5a 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -33,7 +33,9 @@ struct pnp_dev; #define pnp_port_start(dev,bar) ((dev)->res.port_resource[(bar)].start) #define pnp_port_end(dev,bar) ((dev)->res.port_resource[(bar)].end) #define pnp_port_flags(dev,bar) ((dev)->res.port_resource[(bar)].flags) -#define pnp_port_valid(dev,bar) (pnp_port_flags((dev),(bar)) & IORESOURCE_IO) +#define pnp_port_valid(dev,bar) \ + ((pnp_port_flags((dev),(bar)) & (IORESOURCE_IO | IORESOURCE_UNSET)) \ + == IORESOURCE_IO) #define pnp_port_len(dev,bar) \ ((pnp_port_start((dev),(bar)) == 0 && \ pnp_port_end((dev),(bar)) == \ @@ -45,7 +47,9 @@ struct pnp_dev; #define pnp_mem_start(dev,bar) ((dev)->res.mem_resource[(bar)].start) #define pnp_mem_end(dev,bar) ((dev)->res.mem_resource[(bar)].end) #define pnp_mem_flags(dev,bar) ((dev)->res.mem_resource[(bar)].flags) -#define pnp_mem_valid(dev,bar) (pnp_mem_flags((dev),(bar)) & IORESOURCE_MEM) +#define pnp_mem_valid(dev,bar) \ + ((pnp_mem_flags((dev),(bar)) & (IORESOURCE_MEM | IORESOURCE_UNSET)) \ + == IORESOURCE_MEM) #define pnp_mem_len(dev,bar) \ ((pnp_mem_start((dev),(bar)) == 0 && \ pnp_mem_end((dev),(bar)) == \ @@ -56,11 +60,15 @@ struct pnp_dev; #define pnp_irq(dev,bar) ((dev)->res.irq_resource[(bar)].start) #define pnp_irq_flags(dev,bar) ((dev)->res.irq_resource[(bar)].flags) -#define pnp_irq_valid(dev,bar) (pnp_irq_flags((dev),(bar)) & IORESOURCE_IRQ) +#define pnp_irq_valid(dev,bar) \ + ((pnp_irq_flags((dev),(bar)) & (IORESOURCE_IRQ | IORESOURCE_UNSET)) \ + == IORESOURCE_IRQ) #define pnp_dma(dev,bar) ((dev)->res.dma_resource[(bar)].start) #define pnp_dma_flags(dev,bar) ((dev)->res.dma_resource[(bar)].flags) -#define pnp_dma_valid(dev,bar) (pnp_dma_flags((dev),(bar)) & IORESOURCE_DMA) +#define pnp_dma_valid(dev,bar) \ + ((pnp_dma_flags((dev),(bar)) & (IORESOURCE_DMA | IORESOURCE_UNSET)) \ + == IORESOURCE_DMA) #define PNP_PORT_FLAG_16BITADDR (1<<0) #define PNP_PORT_FLAG_FIXED (1<<1) -- cgit v1.2.3 From 3f0f5c1cc3474d7c13adba438fd313123c8176dc Mon Sep 17 00:00:00 2001 From: Adam Belay Date: Sun, 8 Feb 2004 15:36:08 +0000 Subject: [PNP] Move ID declarations This patch moves the PnP ID declarations to mod_devicetable.h like most of the other buses. It is from Takashi Iwai . --- include/linux/mod_devicetable.h | 17 +++++++++++++++++ include/linux/pnp.h | 16 +--------------- 2 files changed, 18 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 67cce1744c86..c0d7ce138d99 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -148,4 +148,21 @@ struct ccw_device_id { #define CCW_DEVICE_ID_MATCH_DEVICE_MODEL 0x08 +#define PNP_ID_LEN 8 +#define PNP_MAX_DEVICES 8 + +struct pnp_device_id { + __u8 id[PNP_ID_LEN]; + kernel_ulong_t driver_data; +}; + +struct pnp_card_device_id { + __u8 id[PNP_ID_LEN]; + kernel_ulong_t driver_data; + struct { + __u8 id[PNP_ID_LEN]; + } devs[PNP_MAX_DEVICES]; +}; + + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 5aa6df8e1a5a..d728e2f5cf71 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -12,13 +12,12 @@ #include #include #include +#include #define PNP_MAX_PORT 8 #define PNP_MAX_MEM 4 #define PNP_MAX_IRQ 2 #define PNP_MAX_DMA 2 -#define PNP_MAX_DEVICES 8 -#define PNP_ID_LEN 8 #define PNP_NAME_LEN 50 struct pnp_protocol; @@ -287,19 +286,6 @@ struct pnp_id { struct pnp_id * next; }; -struct pnp_device_id { - char id[PNP_ID_LEN]; - unsigned long driver_data; /* data private to the driver */ -}; - -struct pnp_card_device_id { - char id[PNP_ID_LEN]; - unsigned long driver_data; /* data private to the driver */ - struct { - char id[PNP_ID_LEN]; - } devs[PNP_MAX_DEVICES]; /* logical devices */ -}; - struct pnp_driver { char * name; const struct pnp_device_id *id_table; -- cgit v1.2.3 From a6eebfd5c5e94e2d94a672407795e2d1a9045315 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 8 Feb 2004 03:02:20 -0800 Subject: Make SET_INPUT_KEYCODE return the old value, and clean up users of this that were very confused indeed. --- drivers/char/keyboard.c | 3 +-- drivers/input/evdev.c | 8 +++----- include/linux/input.h | 9 ++++++--- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 643014bf8c4c..010bff6e1449 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -201,8 +201,7 @@ int setkeycode(unsigned int scancode, unsigned int keycode) if (scancode < 0 || scancode >= dev->keycodemax) return -EINVAL; - oldkey = INPUT_KEYCODE(dev, scancode); - SET_INPUT_KEYCODE(dev, scancode, oldkey); + oldkey = SET_INPUT_KEYCODE(dev, scancode, keycode); clear_bit(oldkey, dev->keybit); set_bit(keycode, dev->keybit); diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 0e16eeeb9e55..9b26ff99a414 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -209,7 +209,7 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, struct evdev *evdev = list->evdev; struct input_dev *dev = evdev->handle.dev; struct input_absinfo abs; - int i, t, u, v; + int t, u, v; if (!evdev->exist) return -ENODEV; @@ -231,10 +231,8 @@ static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, if (get_user(t, ((int *) arg) + 0)) return -EFAULT; if (t < 0 || t > dev->keycodemax || !dev->keycodesize) return -EINVAL; if (get_user(v, ((int *) arg) + 1)) return -EFAULT; - u = INPUT_KEYCODE(dev, t); - SET_INPUT_KEYCODE(dev, t, v); - for (i = 0; i < dev->keycodemax; i++) if (v == u) break; - if (i == dev->keycodemax) clear_bit(u, dev->keybit); + u = SET_INPUT_KEYCODE(dev, t, v); + clear_bit(u, dev->keybit); set_bit(v, dev->keybit); return 0; diff --git a/include/linux/input.h b/include/linux/input.h index 59afa63abbb3..77ba731cfb3a 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -752,25 +752,28 @@ struct ff_effect { #define init_input_dev(dev) do { INIT_LIST_HEAD(&((dev)->h_list)); INIT_LIST_HEAD(&((dev)->node)); } while (0) #define SET_INPUT_KEYCODE(dev, scancode, val) \ - do { \ + ({ unsigned __old; \ switch (dev->keycodesize) { \ case 1: { \ u8 *k = (u8 *)dev->keycode; \ + __old = k[scancode]; \ k[scancode] = val; \ break; \ } \ case 2: { \ u16 *k = (u16 *)dev->keycode; \ + __old = k[scancode]; \ k[scancode] = val; \ break; \ } \ - case 4: { \ + default: { \ u32 *k = (u32 *)dev->keycode; \ + __old = k[scancode]; \ k[scancode] = val; \ break; \ } \ } \ - } while (0) + __old; }) struct input_dev { -- cgit v1.2.3 From 016a8f324652ad92272c0b6ed8d899b231beceb0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 8 Feb 2004 19:03:22 -0800 Subject: PCI: remove stupid MSI debugging code that was never used. --- drivers/pci/msi.c | 1 - include/linux/pci_msi.h | 29 +++-------------------------- 2 files changed, 3 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 43fdb340c697..40d7cfc6c5cc 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -21,7 +21,6 @@ #include -_DEFINE_DBG_BUFFER static spinlock_t msi_lock = SPIN_LOCK_UNLOCKED; static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL }; diff --git a/include/linux/pci_msi.h b/include/linux/pci_msi.h index 0e42e65bc3e1..b638a07ba125 100644 --- a/include/linux/pci_msi.h +++ b/include/linux/pci_msi.h @@ -3,8 +3,8 @@ * */ -#ifndef _ASM_PCI_MSI_H -#define _ASM_PCI_MSI_H +#ifndef PCI_MSI_H +#define PCI_MSI_H #include @@ -82,29 +82,6 @@ extern void restore_ioapic_irq_handler(int irq); #define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) #define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) -extern char __dbg_str_buf[256]; -#define _DEFINE_DBG_BUFFER char __dbg_str_buf[256]; -#define _DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) -#define _DBG_K_TRACE_EXIT ((unsigned int)0x00000002) -#define _DBG_K_INFO ((unsigned int)0x00000004) -#define _DBG_K_ERROR ((unsigned int)0x00000008) -#define _DBG_K_TRACE (_DBG_K_TRACE_ENTRY | _DBG_K_TRACE_EXIT) - -#define _DEBUG_LEVEL (_DBG_K_INFO | _DBG_K_ERROR | _DBG_K_TRACE) -#define _DBG_PRINT( dbg_flags, args... ) \ -if ( _DEBUG_LEVEL & (dbg_flags) ) \ -{ \ - int len; \ - len = sprintf(__dbg_str_buf, "%s:%d: %s ", \ - __FILE__, __LINE__, __FUNCTION__ ); \ - sprintf(__dbg_str_buf + len, args); \ - printk(KERN_INFO "%s\n", __dbg_str_buf); \ -} - -#define MSI_FUNCTION_TRACE_ENTER \ - _DBG_PRINT (_DBG_K_TRACE_ENTRY, "%s", "[Entry]"); -#define MSI_FUNCTION_TRACE_EXIT \ - _DBG_PRINT (_DBG_K_TRACE_EXIT, "%s", "[Entry]"); /* * MSI Defined Data Structures @@ -190,4 +167,4 @@ struct msi_desc { struct pci_dev *dev; }; -#endif /* _ASM_PCI_MSI_H */ +#endif /* PCI_MSI_H */ -- cgit v1.2.3 From 694924e419744f6ea6efdd1c7e4e504ef5a5080f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 8 Feb 2004 19:04:55 -0800 Subject: PCI: move pci_msi.h out of include/linux to drivers/pci where it belongs. --- drivers/pci/msi.c | 2 +- drivers/pci/msi.h | 168 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci_msi.h | 170 ------------------------------------------------ 3 files changed, 169 insertions(+), 171 deletions(-) create mode 100644 drivers/pci/msi.h delete mode 100644 include/linux/pci_msi.h (limited to 'include/linux') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 40d7cfc6c5cc..2090a7089e41 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -19,7 +19,7 @@ #include #include -#include +#include "msi.h" static spinlock_t msi_lock = SPIN_LOCK_UNLOCKED; diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h new file mode 100644 index 000000000000..dd1c359e7a88 --- /dev/null +++ b/drivers/pci/msi.h @@ -0,0 +1,168 @@ +/* + * msi.h + * + */ + +#ifndef MSI_H +#define MSI_H + +#define MSI_AUTO -1 +#define NR_REPEATS 23 +#define NR_RESERVED_VECTORS 3 /*FIRST_DEVICE_VECTOR,FIRST_SYSTEM_VECTOR,0x80 */ + +/* + * Assume the maximum number of hot plug slots supported by the system is about + * ten. The worstcase is that each of these slots is hot-added with a device, + * which has two MSI/MSI-X capable functions. To avoid any MSI-X driver, which + * attempts to request all available vectors, NR_HP_RESERVED_VECTORS is defined + * as below to ensure at least one message is assigned to each detected MSI/ + * MSI-X device function. + */ +#define NR_HP_RESERVED_VECTORS 20 + +extern int vector_irq[NR_IRQS]; +extern cpumask_t pending_irq_balance_cpumask[NR_IRQS]; +extern void (*interrupt[NR_IRQS])(void); + +#ifdef CONFIG_SMP +#define set_msi_irq_affinity set_msi_affinity +#else +#define set_msi_irq_affinity NULL +static inline void move_msi(int vector) {} +#endif + +#ifndef CONFIG_X86_IO_APIC +static inline int get_ioapic_vector(struct pci_dev *dev) { return -1;} +static inline void restore_ioapic_irq_handler(int irq) {} +#else +extern void restore_ioapic_irq_handler(int irq); +#endif + +/* + * MSI-X Address Register + */ +#define PCI_MSIX_FLAGS_QSIZE 0x7FF +#define PCI_MSIX_FLAGS_ENABLE (1 << 15) +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) +#define PCI_MSIX_FLAGS_BITMASK (1 << 0) + +#define PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET 0 +#define PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET 4 +#define PCI_MSIX_ENTRY_DATA_OFFSET 8 +#define PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET 12 +#define PCI_MSIX_ENTRY_SIZE 16 + +#define msi_control_reg(base) (base + PCI_MSI_FLAGS) +#define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO) +#define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI) +#define msi_data_reg(base, is64bit) \ + ( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 ) +#define msi_mask_bits_reg(base, is64bit) \ + ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4) +#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE +#define multi_msi_capable(control) \ + (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1)) +#define multi_msi_enable(control, num) \ + control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE); +#define is_64bit_address(control) (control & PCI_MSI_FLAGS_64BIT) +#define is_mask_bit_support(control) (control & PCI_MSI_FLAGS_MASKBIT) +#define msi_enable(control, num) multi_msi_enable(control, num); \ + control |= PCI_MSI_FLAGS_ENABLE + +#define msix_control_reg msi_control_reg +#define msix_table_offset_reg(base) (base + 0x04) +#define msix_pba_offset_reg(base) (base + 0x08) +#define msix_enable(control) control |= PCI_MSIX_FLAGS_ENABLE +#define msix_disable(control) control &= ~PCI_MSIX_FLAGS_ENABLE +#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1) +#define multi_msix_capable msix_table_size +#define msix_unmask(address) (address & ~PCI_MSIX_FLAGS_BITMASK) +#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) +#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) + + +/* + * MSI Defined Data Structures + */ +#define MSI_ADDRESS_HEADER 0xfee +#define MSI_ADDRESS_HEADER_SHIFT 12 +#define MSI_ADDRESS_HEADER_MASK 0xfff000 +#define MSI_TARGET_CPU_SHIFT 4 +#define MSI_TARGET_CPU_MASK 0xff +#define MSI_DELIVERY_MODE 0 +#define MSI_LEVEL_MODE 1 /* Edge always assert */ +#define MSI_TRIGGER_MODE 0 /* MSI is edge sensitive */ +#define MSI_LOGICAL_MODE 1 +#define MSI_REDIRECTION_HINT_MODE 0 +#ifdef CONFIG_SMP +#define MSI_TARGET_CPU logical_smp_processor_id() +#else +#define MSI_TARGET_CPU TARGET_CPUS +#endif + +struct msg_data { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u32 vector : 8; + __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ + __u32 reserved_1 : 3; + __u32 level : 1; /* 0: deassert | 1: assert */ + __u32 trigger : 1; /* 0: edge | 1: level */ + __u32 reserved_2 : 16; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u32 reserved_2 : 16; + __u32 trigger : 1; /* 0: edge | 1: level */ + __u32 level : 1; /* 0: deassert | 1: assert */ + __u32 reserved_1 : 3; + __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ + __u32 vector : 8; +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif +} __attribute__ ((packed)); + +struct msg_address { + union { + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u32 reserved_1 : 2; + __u32 dest_mode : 1; /*0:physic | 1:logic */ + __u32 redirection_hint: 1; /*0: dedicated CPU + 1: lowest priority */ + __u32 reserved_2 : 4; + __u32 dest_id : 24; /* Destination ID */ +#elif defined(__BIG_ENDIAN_BITFIELD) + __u32 dest_id : 24; /* Destination ID */ + __u32 reserved_2 : 4; + __u32 redirection_hint: 1; /*0: dedicated CPU + 1: lowest priority */ + __u32 dest_mode : 1; /*0:physic | 1:logic */ + __u32 reserved_1 : 2; +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + }u; + __u32 value; + }lo_address; + __u32 hi_address; +} __attribute__ ((packed)); + +struct msi_desc { + struct { + __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ + __u8 maskbit : 1; /* mask-pending bit supported ? */ + __u8 reserved: 2; /* reserved */ + __u8 entry_nr; /* specific enabled entry */ + __u8 default_vector; /* default pre-assigned vector */ + __u8 current_cpu; /* current destination cpu */ + }msi_attrib; + + struct { + __u16 head; + __u16 tail; + }link; + + unsigned long mask_base; + struct pci_dev *dev; +}; + +#endif /* MSI_H */ diff --git a/include/linux/pci_msi.h b/include/linux/pci_msi.h deleted file mode 100644 index b638a07ba125..000000000000 --- a/include/linux/pci_msi.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * ../include/linux/pci_msi.h - * - */ - -#ifndef PCI_MSI_H -#define PCI_MSI_H - -#include - -#define MSI_AUTO -1 -#define NR_REPEATS 23 -#define NR_RESERVED_VECTORS 3 /*FIRST_DEVICE_VECTOR,FIRST_SYSTEM_VECTOR,0x80 */ - -/* - * Assume the maximum number of hot plug slots supported by the system is about - * ten. The worstcase is that each of these slots is hot-added with a device, - * which has two MSI/MSI-X capable functions. To avoid any MSI-X driver, which - * attempts to request all available vectors, NR_HP_RESERVED_VECTORS is defined - * as below to ensure at least one message is assigned to each detected MSI/ - * MSI-X device function. - */ -#define NR_HP_RESERVED_VECTORS 20 - -extern int vector_irq[NR_IRQS]; -extern cpumask_t pending_irq_balance_cpumask[NR_IRQS]; -extern void (*interrupt[NR_IRQS])(void); - -#ifdef CONFIG_SMP -#define set_msi_irq_affinity set_msi_affinity -#else -#define set_msi_irq_affinity NULL -static inline void move_msi(int vector) {} -#endif - -#ifndef CONFIG_X86_IO_APIC -static inline int get_ioapic_vector(struct pci_dev *dev) { return -1;} -static inline void restore_ioapic_irq_handler(int irq) {} -#else -extern void restore_ioapic_irq_handler(int irq); -#endif - -/* - * MSI-X Address Register - */ -#define PCI_MSIX_FLAGS_QSIZE 0x7FF -#define PCI_MSIX_FLAGS_ENABLE (1 << 15) -#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) -#define PCI_MSIX_FLAGS_BITMASK (1 << 0) - -#define PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET 0 -#define PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET 4 -#define PCI_MSIX_ENTRY_DATA_OFFSET 8 -#define PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET 12 -#define PCI_MSIX_ENTRY_SIZE 16 - -#define msi_control_reg(base) (base + PCI_MSI_FLAGS) -#define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO) -#define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI) -#define msi_data_reg(base, is64bit) \ - ( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 ) -#define msi_mask_bits_reg(base, is64bit) \ - ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4) -#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE -#define multi_msi_capable(control) \ - (1 << ((control & PCI_MSI_FLAGS_QMASK) >> 1)) -#define multi_msi_enable(control, num) \ - control |= (((num >> 1) << 4) & PCI_MSI_FLAGS_QSIZE); -#define is_64bit_address(control) (control & PCI_MSI_FLAGS_64BIT) -#define is_mask_bit_support(control) (control & PCI_MSI_FLAGS_MASKBIT) -#define msi_enable(control, num) multi_msi_enable(control, num); \ - control |= PCI_MSI_FLAGS_ENABLE - -#define msix_control_reg msi_control_reg -#define msix_table_offset_reg(base) (base + 0x04) -#define msix_pba_offset_reg(base) (base + 0x08) -#define msix_enable(control) control |= PCI_MSIX_FLAGS_ENABLE -#define msix_disable(control) control &= ~PCI_MSIX_FLAGS_ENABLE -#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1) -#define multi_msix_capable msix_table_size -#define msix_unmask(address) (address & ~PCI_MSIX_FLAGS_BITMASK) -#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) -#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) - - -/* - * MSI Defined Data Structures - */ -#define MSI_ADDRESS_HEADER 0xfee -#define MSI_ADDRESS_HEADER_SHIFT 12 -#define MSI_ADDRESS_HEADER_MASK 0xfff000 -#define MSI_TARGET_CPU_SHIFT 4 -#define MSI_TARGET_CPU_MASK 0xff -#define MSI_DELIVERY_MODE 0 -#define MSI_LEVEL_MODE 1 /* Edge always assert */ -#define MSI_TRIGGER_MODE 0 /* MSI is edge sensitive */ -#define MSI_LOGICAL_MODE 1 -#define MSI_REDIRECTION_HINT_MODE 0 -#ifdef CONFIG_SMP -#define MSI_TARGET_CPU logical_smp_processor_id() -#else -#define MSI_TARGET_CPU TARGET_CPUS -#endif - -struct msg_data { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u32 vector : 8; - __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ - __u32 reserved_1 : 3; - __u32 level : 1; /* 0: deassert | 1: assert */ - __u32 trigger : 1; /* 0: edge | 1: level */ - __u32 reserved_2 : 16; -#elif defined(__BIG_ENDIAN_BITFIELD) - __u32 reserved_2 : 16; - __u32 trigger : 1; /* 0: edge | 1: level */ - __u32 level : 1; /* 0: deassert | 1: assert */ - __u32 reserved_1 : 3; - __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ - __u32 vector : 8; -#else -#error "Bitfield endianness not defined! Check your byteorder.h" -#endif -} __attribute__ ((packed)); - -struct msg_address { - union { - struct { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u32 reserved_1 : 2; - __u32 dest_mode : 1; /*0:physic | 1:logic */ - __u32 redirection_hint: 1; /*0: dedicated CPU - 1: lowest priority */ - __u32 reserved_2 : 4; - __u32 dest_id : 24; /* Destination ID */ -#elif defined(__BIG_ENDIAN_BITFIELD) - __u32 dest_id : 24; /* Destination ID */ - __u32 reserved_2 : 4; - __u32 redirection_hint: 1; /*0: dedicated CPU - 1: lowest priority */ - __u32 dest_mode : 1; /*0:physic | 1:logic */ - __u32 reserved_1 : 2; -#else -#error "Bitfield endianness not defined! Check your byteorder.h" -#endif - }u; - __u32 value; - }lo_address; - __u32 hi_address; -} __attribute__ ((packed)); - -struct msi_desc { - struct { - __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ - __u8 maskbit : 1; /* mask-pending bit supported ? */ - __u8 reserved: 2; /* reserved */ - __u8 entry_nr; /* specific enabled entry */ - __u8 default_vector; /* default pre-assigned vector */ - __u8 current_cpu; /* current destination cpu */ - }msi_attrib; - - struct { - __u16 head; - __u16 tail; - }link; - - unsigned long mask_base; - struct pci_dev *dev; -}; - -#endif /* PCI_MSI_H */ -- cgit v1.2.3 From bc7e061c860f4bb766c3b013c4c554cb01d87290 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sun, 8 Feb 2004 20:16:07 -0800 Subject: [PATCH] USB: re-factor enumeration/reset paths This patch starts dis-entangling some of the enumeration logic by moving initialization code into the usb_alloc_dev() constructor. Some call signatures changed; a usbcore-internal declaration was moved in to a more appropriate location. With the driver model init now more centralized, it's safer to use driver model calls (including dev_info) a lot earlier, so the "new device at address N" message now does that. It also reports the device speed, which may not be evident otherwise. --- drivers/usb/core/hcd.c | 10 +++---- drivers/usb/core/hcd.h | 4 ++- drivers/usb/core/hub.c | 44 +++++++--------------------- drivers/usb/core/usb.c | 65 +++++++++++++++++++++++++----------------- drivers/usb/host/ehci-hcd.c | 2 +- drivers/usb/host/hc_sl811_rh.c | 2 +- drivers/usb/host/ohci-hcd.c | 2 +- drivers/usb/host/uhci-hcd.c | 2 +- include/linux/usb.h | 1 - 9 files changed, 62 insertions(+), 70 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 8ee641df6b9c..e76cb4c1cf4d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -738,7 +738,7 @@ EXPORT_SYMBOL (usb_deregister_bus); * * The USB host controller calls this function to register the root hub * properly with the USB subsystem. It sets up the device properly in - * the driverfs tree, and then calls usb_new_device() to register the + * the device model tree, and then calls usb_new_device() to register the * usb device. It also assigns the root hub's USB address (always 1). */ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev) @@ -746,14 +746,14 @@ int usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev const int devnum = 1; int retval; - sprintf (&usb_dev->dev.bus_id[0], "usb%d", usb_dev->bus->busnum); - usb_dev->state = USB_STATE_DEFAULT; - usb_dev->devnum = devnum; usb_dev->bus->devnum_next = devnum + 1; + memset (&usb_dev->bus->devmap.devicemap, 0, + sizeof usb_dev->bus->devmap.devicemap); set_bit (devnum, usb_dev->bus->devmap.devicemap); + usb_dev->state = USB_STATE_ADDRESS; - retval = usb_new_device (usb_dev, parent_dev); + retval = usb_new_device (usb_dev); if (retval) dev_err (parent_dev, "can't register root hub for %s, %d\n", usb_dev->dev.bus_id, retval); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 1d0ea74d0148..725a92f29092 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -241,7 +241,9 @@ extern void usb_hc_died (struct usb_hcd *hcd); /* -------------------------------------------------------------------------- */ /* Enumeration is only for the hub driver, or HCD virtual root hubs */ -extern int usb_new_device(struct usb_device *dev, struct device *parent); +extern struct usb_device *usb_alloc_dev(struct usb_device *parent, + struct usb_bus *, unsigned port); +extern int usb_new_device(struct usb_device *dev); extern void usb_choose_address(struct usb_device *dev); extern void usb_disconnect(struct usb_device **); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 6c2976d24562..e9db8522acd7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -930,11 +930,9 @@ static void hub_port_connect_change(struct usb_hub *hubstate, int port, down(&usb_address0_sem); for (i = 0; i < HUB_PROBE_TRIES; i++) { - struct usb_device *pdev; - int len; /* Allocate a new device struct */ - dev = usb_alloc_dev(hub, hub->bus); + dev = usb_alloc_dev(hub, hub->bus, port); if (!dev) { dev_err (&hubstate->intf->dev, "couldn't allocate usb_device\n"); @@ -962,38 +960,18 @@ static void hub_port_connect_change(struct usb_hub *hubstate, int port, dev->ttport = port + 1; } - /* Save readable and stable topology id, distinguishing - * devices by location for diagnostics, tools, etc. The - * string is a path along hub ports, from the root. Each - * device's id will be stable until USB is re-cabled, and - * hubs are often labeled with these port numbers. - * - * Initial size: ".NN" times five hubs + NUL = 16 bytes max - * (quite rare, since most hubs have 4-6 ports). - */ - pdev = dev->parent; - if (pdev->devpath [0] != '0') /* parent not root? */ - len = snprintf (dev->devpath, sizeof dev->devpath, - "%s.%d", pdev->devpath, port + 1); - /* root == "0", root port 2 == "2", port 3 that hub "2.3" */ - else - len = snprintf (dev->devpath, sizeof dev->devpath, - "%d", port + 1); - if (len == sizeof dev->devpath) - dev_err (&hubstate->intf->dev, - "devpath size! usb/%03d/%03d path %s\n", - dev->bus->busnum, dev->devnum, dev->devpath); - dev_info (&hubstate->intf->dev, - "new USB device on port %d, assigned address %d\n", - port + 1, dev->devnum); - - /* put the device in the global device tree. the hub port - * is the "bus_id"; hubs show in hierarchy like bridges - */ - dev->dev.parent = dev->parent->dev.parent->parent; + dev_info (&dev->dev, + "new %s speed USB device using address %d\n", + ({ char *speed; switch (dev->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + }; speed;}), + dev->devnum); /* Run it through the hoops (find a driver, etc) */ - if (!usb_new_device(dev, &hub->dev)) { + if (usb_new_device(dev) == 0) { hub->children[port] = dev; goto done; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 378d51ce2374..07ddb791beed 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -678,17 +678,19 @@ static void usb_release_dev(struct device *dev) } /** - * usb_alloc_dev - allocate a usb device structure (usbcore-internal) - * @parent: hub to which device is connected + * usb_alloc_dev - usb device constructor (usbcore-internal) + * @parent: hub to which device is connected; null to allocate a root hub * @bus: bus used to access the device + * @port: zero based index of port; ignored for root hubs * Context: !in_interrupt () * * Only hub drivers (including virtual root hub drivers for host * controllers) should ever call this. * - * This call is synchronous, and may not be used in an interrupt context. + * This call may not be used in a non-sleeping context. */ -struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus) +struct usb_device * +usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port) { struct usb_device *dev; @@ -705,11 +707,42 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus) } device_initialize(&dev->dev); + dev->dev.bus = &usb_bus_type; + dev->dev.dma_mask = bus->controller->dma_mask; + dev->dev.driver_data = &usb_generic_driver_data; + dev->dev.driver = &usb_generic_driver; dev->dev.release = usb_release_dev; dev->state = USB_STATE_ATTACHED; - if (!parent) + /* Save readable and stable topology id, distinguishing devices + * by location for diagnostics, tools, driver model, etc. The + * string is a path along hub ports, from the root. Each device's + * dev->devpath will be stable until USB is re-cabled, and hubs + * are often labeled with these port numbers. The bus_id isn't + * as stable: bus->busnum changes easily from modprobe order, + * cardbus or pci hotplugging, and so on. + */ + if (unlikely (!parent)) { dev->devpath [0] = '0'; + + dev->dev.parent = bus->controller; + sprintf (&dev->dev.bus_id[0], "usb%d", bus->busnum); + } else { + /* match any labeling on the hubs; it's one-based */ + if (parent->devpath [0] == '0') + snprintf (dev->devpath, sizeof dev->devpath, + "%d", port + 1); + else + snprintf (dev->devpath, sizeof dev->devpath, + "%s.%d", parent->devpath, port + 1); + + dev->dev.parent = &parent->dev; + sprintf (&dev->dev.bus_id[0], "%d-%s", + bus->busnum, dev->devpath); + + /* hub driver sets up TT records */ + } + dev->bus = bus; dev->parent = parent; INIT_LIST_HEAD(&dev->filelist); @@ -1011,33 +1044,13 @@ static inline void usb_show_string(struct usb_device *dev, char *id, int index) */ #define NEW_DEVICE_RETRYS 2 #define SET_ADDRESS_RETRYS 2 -int usb_new_device(struct usb_device *dev, struct device *parent) +int usb_new_device(struct usb_device *dev) { int err = -EINVAL; int i; int j; int config; - /* - * Set the driver for the usb device to point to the "generic" driver. - * This prevents the main usb device from being sent to the usb bus - * probe function. Yes, it's a hack, but a nice one :) - * - * Do it asap, so more driver model stuff (like the device.h message - * utilities) can be used in hcd submit/unlink code paths. - */ - usb_generic_driver.bus = &usb_bus_type; - dev->dev.parent = parent; - dev->dev.driver = &usb_generic_driver; - dev->dev.bus = &usb_bus_type; - dev->dev.driver_data = &usb_generic_driver_data; - if (dev->dev.bus_id[0] == 0) - sprintf (&dev->dev.bus_id[0], "%d-%s", - dev->bus->busnum, dev->devpath); - - /* dma masks come from the controller; readonly, except to hcd */ - dev->dev.dma_mask = parent->dma_mask; - /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ... * it's fixed size except for full speed devices. */ diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 5c58a16c7693..0db191fb8ea0 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -473,7 +473,7 @@ static int ehci_start (struct usb_hcd *hcd) /* wire up the root hub */ bus = hcd_to_bus (hcd); - bus->root_hub = udev = usb_alloc_dev (NULL, bus); + bus->root_hub = udev = usb_alloc_dev (NULL, bus, 0); if (!udev) { done2: ehci_mem_cleanup (ehci); diff --git a/drivers/usb/host/hc_sl811_rh.c b/drivers/usb/host/hc_sl811_rh.c index 42ec077b72bf..a4dd4628ed66 100644 --- a/drivers/usb/host/hc_sl811_rh.c +++ b/drivers/usb/host/hc_sl811_rh.c @@ -559,7 +559,7 @@ static int rh_connect_rh (hci_t * hci) struct usb_device *usb_dev; hci->rh.devnum = 0; - usb_dev = usb_alloc_dev (NULL, hci->bus); + usb_dev = usb_alloc_dev (NULL, hci->bus, 0); if (!usb_dev) return -ENOMEM; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index cc2c9c259197..53c86a42d4ba 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -536,7 +536,7 @@ static int hc_start (struct ohci_hcd *ohci) /* connect the virtual root hub */ bus = hcd_to_bus (&ohci->hcd); - bus->root_hub = udev = usb_alloc_dev (NULL, bus); + bus->root_hub = udev = usb_alloc_dev (NULL, bus, 0); ohci->hcd.state = USB_STATE_RUNNING; if (!udev) { disable (ohci); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 652f85120d75..0678fdf8d7b1 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -2315,7 +2315,7 @@ static int uhci_start(struct usb_hcd *hcd) uhci->rh_numports = port; - hcd->self.root_hub = udev = usb_alloc_dev(NULL, &hcd->self); + hcd->self.root_hub = udev = usb_alloc_dev(NULL, &hcd->self, 0); if (!udev) { err("unable to allocate root hub"); goto err_alloc_root_hub; diff --git a/include/linux/usb.h b/include/linux/usb.h index c16b0125baa0..45caca344ca3 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -274,7 +274,6 @@ struct usb_device { }; #define to_usb_device(d) container_of(d, struct usb_device, dev) -extern struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *); extern struct usb_device *usb_get_dev(struct usb_device *dev); extern void usb_put_dev(struct usb_device *dev); -- cgit v1.2.3 From a1eefc490265c01da425ed7bd2458ca1a67fca8a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 8 Feb 2004 21:18:09 -0800 Subject: [PATCH] I2C: add new chip driver: fscher This is a new ported driver, fscher, which supports the FSC Hermes chip. The original driver was written by Reinhard Nissl, who also ported it to Linux 2.6, as discussed on the lm_sensors mailing list during the last two weeks. I reviewed the code and we made the necessary changes, so that what we have now looks good to me. Please apply on top of your i2c patches stack. --- drivers/i2c/chips/Kconfig | 11 + drivers/i2c/chips/Makefile | 1 + drivers/i2c/chips/fscher.c | 681 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c-id.h | 1 + 4 files changed, 694 insertions(+) create mode 100644 drivers/i2c/chips/fscher.c (limited to 'include/linux') diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 50cdb11a4723..8386041a3e7f 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -45,6 +45,17 @@ config SENSORS_EEPROM This driver can also be built as a module. If so, the module will be called eeprom. +config SENSORS_FSCHER + tristate "FSC Hermes" + depends on I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Fujitsu Siemens + Computers Hermes sensor chips. + + This driver can also be built as a module. If so, the module + will be called fscher. + config SENSORS_GL518SM tristate "Genesys Logic GL518SM" depends on I2C && EXPERIMENTAL diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 11d592ca28ae..c673ed1b4573 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SENSORS_W83781D) += w83781d.o obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o +obj-$(CONFIG_SENSORS_FSCHER) += fscher.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_LM75) += lm75.o diff --git a/drivers/i2c/chips/fscher.c b/drivers/i2c/chips/fscher.c new file mode 100644 index 000000000000..3140b6d2a3d9 --- /dev/null +++ b/drivers/i2c/chips/fscher.c @@ -0,0 +1,681 @@ +/* + * fscher.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2003, 2004 Reinhard Nissl + * + * 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. + */ + +/* + * fujitsu siemens hermes chip, + * module based on fscpos.c + * Copyright (C) 2000 Hermann Jung + * Copyright (C) 1998, 1999 Frodo Looijaard + * and Philip Edelbrock + */ + +#include +#include +#include +#include +#include + +/* + * Addresses to scan + */ + +static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; +static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ + +SENSORS_INSMOD_1(fscher); + +/* + * The FSCHER registers + */ + +/* chip identification */ +#define FSCHER_REG_IDENT_0 0x00 +#define FSCHER_REG_IDENT_1 0x01 +#define FSCHER_REG_IDENT_2 0x02 +#define FSCHER_REG_REVISION 0x03 + +/* global control and status */ +#define FSCHER_REG_EVENT_STATE 0x04 +#define FSCHER_REG_CONTROL 0x05 + +/* watchdog */ +#define FSCHER_REG_WDOG_PRESET 0x28 +#define FSCHER_REG_WDOG_STATE 0x23 +#define FSCHER_REG_WDOG_CONTROL 0x21 + +/* fan 0 */ +#define FSCHER_REG_FAN0_MIN 0x55 +#define FSCHER_REG_FAN0_ACT 0x0e +#define FSCHER_REG_FAN0_STATE 0x0d +#define FSCHER_REG_FAN0_RIPPLE 0x0f + +/* fan 1 */ +#define FSCHER_REG_FAN1_MIN 0x65 +#define FSCHER_REG_FAN1_ACT 0x6b +#define FSCHER_REG_FAN1_STATE 0x62 +#define FSCHER_REG_FAN1_RIPPLE 0x6f + +/* fan 2 */ +#define FSCHER_REG_FAN2_MIN 0xb5 +#define FSCHER_REG_FAN2_ACT 0xbb +#define FSCHER_REG_FAN2_STATE 0xb2 +#define FSCHER_REG_FAN2_RIPPLE 0xbf + +/* voltage supervision */ +#define FSCHER_REG_VOLT_12 0x45 +#define FSCHER_REG_VOLT_5 0x42 +#define FSCHER_REG_VOLT_BATT 0x48 + +/* temperature 0 */ +#define FSCHER_REG_TEMP0_ACT 0x64 +#define FSCHER_REG_TEMP0_STATE 0x71 + +/* temperature 1 */ +#define FSCHER_REG_TEMP1_ACT 0x32 +#define FSCHER_REG_TEMP1_STATE 0x81 + +/* temperature 2 */ +#define FSCHER_REG_TEMP2_ACT 0x35 +#define FSCHER_REG_TEMP2_STATE 0x91 + +/* + * Functions declaration + */ + +static int fscher_attach_adapter(struct i2c_adapter *adapter); +static int fscher_detect(struct i2c_adapter *adapter, int address, int kind); +static int fscher_detach_client(struct i2c_client *client); +static void fscher_update_client(struct i2c_client *client); +static void fscher_init_client(struct i2c_client *client); + +static int fscher_read_value(struct i2c_client *client, u8 reg); +static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver fscher_driver = { + .owner = THIS_MODULE, + .name = "fscher", + .id = I2C_DRIVERID_FSCHER, + .flags = I2C_DF_NOTIFY, + .attach_adapter = fscher_attach_adapter, + .detach_client = fscher_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct fscher_data { + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* register values */ + u8 revision; /* revision of chip */ + u8 global_event; /* global event status */ + u8 global_control; /* global control register */ + u8 watchdog[3]; /* watchdog */ + u8 volt[3]; /* 12, 5, battery voltage */ + u8 temp_act[3]; /* temperature */ + u8 temp_status[3]; /* status of sensor */ + u8 fan_act[3]; /* fans revolutions per second */ + u8 fan_status[3]; /* fan status */ + u8 fan_min[3]; /* fan min value for rps */ + u8 fan_ripple[3]; /* divider for rps */ +}; + +/* + * Internal variables + */ + +static int fscher_id = 0; + +/* + * Sysfs stuff + */ + +#define sysfs_r(kind, offset, reg) \ +static ssize_t show_##kind (struct fscher_data *, char *, int); \ +static ssize_t show_##kind##offset (struct device *, char *); \ +static ssize_t show_##kind##offset (struct device *dev, char *buf) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct fscher_data *data = i2c_get_clientdata(client); \ + fscher_update_client(client); \ + return show_##kind(data, buf, (offset)); \ +} + +#define sysfs_w(kind, offset, reg) \ +static ssize_t set_##kind (struct i2c_client *, struct fscher_data *, const char *, size_t, int, int); \ +static ssize_t set_##kind##offset (struct device *, const char *, size_t); \ +static ssize_t set_##kind##offset (struct device *dev, const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct fscher_data *data = i2c_get_clientdata(client); \ + return set_##kind(client, data, buf, count, (offset), reg); \ +} + +#define sysfs_rw_n(kind, offset, reg) \ +sysfs_r(kind, offset, reg) \ +sysfs_w(kind, offset, reg) \ +static DEVICE_ATTR(kind##offset, S_IRUGO | S_IWUSR, show_##kind##offset, set_##kind##offset); + +#define sysfs_rw(kind, reg) \ +sysfs_r(kind, 0, reg) \ +sysfs_w(kind, 0, reg) \ +static DEVICE_ATTR(kind, S_IRUGO | S_IWUSR, show_##kind##0, set_##kind##0); + +#define sysfs_ro_n(kind, offset, reg) \ +sysfs_r(kind, offset, reg) \ +static DEVICE_ATTR(kind##offset, S_IRUGO, show_##kind##offset, NULL); + +#define sysfs_ro(kind, reg) \ +sysfs_r(kind, 0, reg) \ +static DEVICE_ATTR(kind, S_IRUGO, show_##kind##0, NULL); + +#define sysfs_fan(offset, reg_status, reg_min, reg_ripple, reg_act) \ +sysfs_rw_n(pwm , offset, reg_min) \ +sysfs_rw_n(fan_status, offset, reg_status) \ +sysfs_rw_n(fan_div , offset, reg_ripple) \ +sysfs_ro_n(fan_input , offset, reg_act) + +#define sysfs_temp(offset, reg_status, reg_act) \ +sysfs_rw_n(temp_status, offset, reg_status) \ +sysfs_ro_n(temp_input , offset, reg_act) + +#define sysfs_in(offset, reg_act) \ +sysfs_ro_n(in_input, offset, reg_act) + +#define sysfs_revision(reg_revision) \ +sysfs_ro(revision, reg_revision) + +#define sysfs_alarms(reg_events) \ +sysfs_ro(alarms, reg_events) + +#define sysfs_control(reg_control) \ +sysfs_rw(control, reg_control) + +#define sysfs_watchdog(reg_control, reg_status, reg_preset) \ +sysfs_rw(watchdog_control, reg_control) \ +sysfs_rw(watchdog_status , reg_status) \ +sysfs_rw(watchdog_preset , reg_preset) + +sysfs_fan(1, FSCHER_REG_FAN0_STATE, FSCHER_REG_FAN0_MIN, + FSCHER_REG_FAN0_RIPPLE, FSCHER_REG_FAN0_ACT) +sysfs_fan(2, FSCHER_REG_FAN1_STATE, FSCHER_REG_FAN1_MIN, + FSCHER_REG_FAN1_RIPPLE, FSCHER_REG_FAN1_ACT) +sysfs_fan(3, FSCHER_REG_FAN2_STATE, FSCHER_REG_FAN2_MIN, + FSCHER_REG_FAN2_RIPPLE, FSCHER_REG_FAN2_ACT) + +sysfs_temp(1, FSCHER_REG_TEMP0_STATE, FSCHER_REG_TEMP0_ACT) +sysfs_temp(2, FSCHER_REG_TEMP1_STATE, FSCHER_REG_TEMP1_ACT) +sysfs_temp(3, FSCHER_REG_TEMP2_STATE, FSCHER_REG_TEMP2_ACT) + +sysfs_in(0, FSCHER_REG_VOLT_12) +sysfs_in(1, FSCHER_REG_VOLT_5) +sysfs_in(2, FSCHER_REG_VOLT_BATT) + +sysfs_revision(FSCHER_REG_REVISION) +sysfs_alarms(FSCHER_REG_EVENTS) +sysfs_control(FSCHER_REG_CONTROL) +sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET) + +#define device_create_file_fan(client, offset) \ +do { \ + device_create_file(&client->dev, &dev_attr_fan_status##offset); \ + device_create_file(&client->dev, &dev_attr_pwm##offset); \ + device_create_file(&client->dev, &dev_attr_fan_div##offset); \ + device_create_file(&client->dev, &dev_attr_fan_input##offset); \ +} while (0) + +#define device_create_file_temp(client, offset) \ +do { \ + device_create_file(&client->dev, &dev_attr_temp_status##offset); \ + device_create_file(&client->dev, &dev_attr_temp_input##offset); \ +} while (0) + +#define device_create_file_in(client, offset) \ +do { \ + device_create_file(&client->dev, &dev_attr_in_input##offset); \ +} while (0) + +#define device_create_file_revision(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_revision); \ +} while (0) + +#define device_create_file_alarms(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_alarms); \ +} while (0) + +#define device_create_file_control(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_control); \ +} while (0) + +#define device_create_file_watchdog(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_watchdog_status); \ + device_create_file(&client->dev, &dev_attr_watchdog_control); \ + device_create_file(&client->dev, &dev_attr_watchdog_preset); \ +} while (0) + +/* + * Real code + */ + +static int fscher_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_ADAP_CLASS_SMBUS)) + return 0; + return i2c_detect(adapter, &addr_data, fscher_detect); +} + +static int fscher_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct fscher_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + /* OK. For now, we presume we have a valid client. We now create the + * client structure, even though we cannot fill it completely yet. + * But it allows us to access i2c_smbus_read_byte_data. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct fscher_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(new_client, 0x00, sizeof(struct i2c_client) + + sizeof(struct fscher_data)); + + /* The Hermes-specific data is placed right after the common I2C + * client data. */ + data = (struct fscher_data *) (new_client + 1); + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &fscher_driver; + new_client->flags = 0; + + /* Do the remaining detection unless force or force_fscher parameter */ + if (kind < 0) { + if ((i2c_smbus_read_byte_data(new_client, + FSCHER_REG_IDENT_0) != 0x48) /* 'H' */ + || (i2c_smbus_read_byte_data(new_client, + FSCHER_REG_IDENT_1) != 0x45) /* 'E' */ + || (i2c_smbus_read_byte_data(new_client, + FSCHER_REG_IDENT_2) != 0x52)) /* 'R' */ + goto exit_free; + } + + /* Fill in the remaining client fields and put it into the + * global list */ + strlcpy(new_client->name, "fscher", I2C_NAME_SIZE); + new_client->id = fscher_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + fscher_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file_revision(new_client); + device_create_file_alarms(new_client); + device_create_file_control(new_client); + device_create_file_watchdog(new_client); + + device_create_file_in(new_client, 0); + device_create_file_in(new_client, 1); + device_create_file_in(new_client, 2); + + device_create_file_fan(new_client, 1); + device_create_file_fan(new_client, 2); + device_create_file_fan(new_client, 3); + + device_create_file_temp(new_client, 1); + device_create_file_temp(new_client, 2); + device_create_file_temp(new_client, 3); + + return 0; + +exit_free: + kfree(new_client); +exit: + return err; +} + +static int fscher_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(client); + return 0; +} + +static int fscher_read_value(struct i2c_client *client, u8 reg) +{ + dev_dbg(&client->dev, "read reg 0x%02x\n", reg); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + dev_dbg(&client->dev, "write reg 0x%02x, val 0x%02x\n", + reg, value); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new FSC Hermes. */ +static void fscher_init_client(struct i2c_client *client) +{ + struct fscher_data *data = i2c_get_clientdata(client); + + /* Read revision from chip */ + data->revision = fscher_read_value(client, FSCHER_REG_REVISION); +} + +static void fscher_update_client(struct i2c_client *client) +{ + struct fscher_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 2 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + + dev_dbg(&client->dev, "Starting fscher update\n"); + + data->temp_act[0] = fscher_read_value(client, FSCHER_REG_TEMP0_ACT); + data->temp_act[1] = fscher_read_value(client, FSCHER_REG_TEMP1_ACT); + data->temp_act[2] = fscher_read_value(client, FSCHER_REG_TEMP2_ACT); + data->temp_status[0] = fscher_read_value(client, FSCHER_REG_TEMP0_STATE); + data->temp_status[1] = fscher_read_value(client, FSCHER_REG_TEMP1_STATE); + data->temp_status[2] = fscher_read_value(client, FSCHER_REG_TEMP2_STATE); + + data->volt[0] = fscher_read_value(client, FSCHER_REG_VOLT_12); + data->volt[1] = fscher_read_value(client, FSCHER_REG_VOLT_5); + data->volt[2] = fscher_read_value(client, FSCHER_REG_VOLT_BATT); + + data->fan_act[0] = fscher_read_value(client, FSCHER_REG_FAN0_ACT); + data->fan_act[1] = fscher_read_value(client, FSCHER_REG_FAN1_ACT); + data->fan_act[2] = fscher_read_value(client, FSCHER_REG_FAN2_ACT); + data->fan_status[0] = fscher_read_value(client, FSCHER_REG_FAN0_STATE); + data->fan_status[1] = fscher_read_value(client, FSCHER_REG_FAN1_STATE); + data->fan_status[2] = fscher_read_value(client, FSCHER_REG_FAN2_STATE); + data->fan_min[0] = fscher_read_value(client, FSCHER_REG_FAN0_MIN); + data->fan_min[1] = fscher_read_value(client, FSCHER_REG_FAN1_MIN); + data->fan_min[2] = fscher_read_value(client, FSCHER_REG_FAN2_MIN); + data->fan_ripple[0] = fscher_read_value(client, FSCHER_REG_FAN0_RIPPLE); + data->fan_ripple[1] = fscher_read_value(client, FSCHER_REG_FAN1_RIPPLE); + data->fan_ripple[2] = fscher_read_value(client, FSCHER_REG_FAN2_RIPPLE); + + data->watchdog[0] = fscher_read_value(client, FSCHER_REG_WDOG_PRESET); + data->watchdog[1] = fscher_read_value(client, FSCHER_REG_WDOG_STATE); + data->watchdog[2] = fscher_read_value(client, FSCHER_REG_WDOG_CONTROL); + + data->global_event = fscher_read_value(client, FSCHER_REG_EVENT_STATE); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + + +#define FAN_INDEX_FROM_NUM(nr) ((nr) - 1) + +static ssize_t set_fan_status(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 0..1, 3..7 reserved => mask with 0x04 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x04; + data->fan_status[FAN_INDEX_FROM_NUM(nr)] &= ~v; + + fscher_write_value(client, reg, v); + return count; +} + +static ssize_t show_fan_status(struct fscher_data *data, char *buf, int nr) +{ + /* bits 0..1, 3..7 reserved => mask with 0x04 */ + return sprintf(buf, "%u\n", data->fan_status[FAN_INDEX_FROM_NUM(nr)] & 0x04); +} + +static ssize_t set_pwm(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + data->fan_min[FAN_INDEX_FROM_NUM(nr)] = simple_strtoul(buf, NULL, 10) & 0xff; + + fscher_write_value(client, reg, data->fan_min[FAN_INDEX_FROM_NUM(nr)]); + return count; +} + +static ssize_t show_pwm (struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", data->fan_min[FAN_INDEX_FROM_NUM(nr)]); +} + +static ssize_t set_fan_div(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* supported values: 2, 4, 8 */ + unsigned long v = simple_strtoul(buf, NULL, 10); + + switch (v) { + case 2: v = 1; break; + case 4: v = 2; break; + case 8: v = 3; break; + default: + dev_err(&client->dev, "fan_div value %ld not " + "supported. Choose one of 2, 4 or 8!\n", v); + return -1; + } + + /* bits 2..7 reserved => mask with 0x03 */ + data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] &= ~0x03; + data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] |= v; + + fscher_write_value(client, reg, data->fan_ripple[FAN_INDEX_FROM_NUM(nr)]); + return count; +} + +static ssize_t show_fan_div(struct fscher_data *data, char *buf, int nr) +{ + /* bits 2..7 reserved => mask with 0x03 */ + return sprintf(buf, "%u\n", 1 << (data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] & 0x03)); +} + +#define RPM_FROM_REG(val) (val*60) + +static ssize_t show_fan_input (struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[FAN_INDEX_FROM_NUM(nr)])); +} + + + +#define TEMP_INDEX_FROM_NUM(nr) ((nr) - 1) + +static ssize_t set_temp_status(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 2..7 reserved, 0 read only => mask with 0x02 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; + data->temp_status[TEMP_INDEX_FROM_NUM(nr)] &= ~v; + + fscher_write_value(client, reg, v); + return count; +} + +static ssize_t show_temp_status(struct fscher_data *data, char *buf, int nr) +{ + /* bits 2..7 reserved => mask with 0x03 */ + return sprintf(buf, "%u\n", data->temp_status[TEMP_INDEX_FROM_NUM(nr)] & 0x03); +} + +#define TEMP_FROM_REG(val) (((val) - 128) * 1000) + +static ssize_t show_temp_input(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[TEMP_INDEX_FROM_NUM(nr)])); +} + +/* + * The final conversion is specified in sensors.conf, as it depends on + * mainboard specific values. We export the registers contents as + * pseudo-hundredths-of-Volts (range 0V - 2.55V). Not that it makes much + * sense per se, but it minimizes the conversions count and keeps the + * values within a usual range. + */ +#define VOLT_FROM_REG(val) ((val) * 10) + +static ssize_t show_in_input(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[nr])); +} + + + +static ssize_t show_revision(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", data->revision); +} + + + +static ssize_t show_alarms(struct fscher_data *data, char *buf, int nr) +{ + /* bits 2, 5..6 reserved => mask with 0x9b */ + return sprintf(buf, "%u\n", data->global_event & 0x9b); +} + + + +static ssize_t set_control(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 1..7 reserved => mask with 0x01 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x01; + data->global_control &= ~v; + + fscher_write_value(client, reg, v); + return count; +} + +static ssize_t show_control(struct fscher_data *data, char *buf, int nr) +{ + /* bits 1..7 reserved => mask with 0x01 */ + return sprintf(buf, "%u\n", data->global_control & 0x01); +} + + + +static ssize_t set_watchdog_control(struct i2c_client *client, struct + fscher_data *data, const char *buf, size_t count, + int nr, int reg) +{ + /* bits 0..3 reserved => mask with 0xf0 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0; + data->watchdog[2] &= ~0xf0; + data->watchdog[2] |= v; + + fscher_write_value(client, reg, data->watchdog[2]); + return count; +} + +static ssize_t show_watchdog_control(struct fscher_data *data, char *buf, int nr) +{ + /* bits 0..3 reserved, bit 5 write only => mask with 0xd0 */ + return sprintf(buf, "%u\n", data->watchdog[2] & 0xd0); +} + +static ssize_t set_watchdog_status(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 0, 2..7 reserved => mask with 0x02 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; + data->watchdog[1] &= ~v; + + fscher_write_value(client, reg, v); + return count; +} + +static ssize_t show_watchdog_status(struct fscher_data *data, char *buf, int nr) +{ + /* bits 0, 2..7 reserved => mask with 0x02 */ + return sprintf(buf, "%u\n", data->watchdog[1] & 0x02); +} + +static ssize_t set_watchdog_preset(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + data->watchdog[0] = simple_strtoul(buf, NULL, 10) & 0xff; + + fscher_write_value(client, reg, data->watchdog[0]); + return count; +} + +static ssize_t show_watchdog_preset(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", data->watchdog[0]); +} + +static int __init sensors_fscher_init(void) +{ + return i2c_add_driver(&fscher_driver); +} + +static void __exit sensors_fscher_exit(void) +{ + i2c_del_driver(&fscher_driver); +} + +MODULE_AUTHOR("Reinhard Nissl "); +MODULE_DESCRIPTION("FSC Hermes driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_fscher_init); +module_exit(sensors_fscher_exit); diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index d4dcd2126ce1..e988edbcb5b8 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -156,6 +156,7 @@ #define I2C_DRIVERID_LM83 1040 #define I2C_DRIVERID_LM90 1042 #define I2C_DRIVERID_ASB100 1043 +#define I2C_DRIVERID_FSCHER 1046 #define I2C_DRIVERID_W83L785TS 1047 /* -- cgit v1.2.3 From d9efde4dda528c08db2e64f1624ca13d91910fdd Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 9 Feb 2004 00:25:04 -0800 Subject: Add forward-declaration of "struct nfs4_client" to make nfs_idmap.h independent of CONFIG_NFS4 and other header files. --- include/linux/nfs_idmap.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 50df56b5a01e..a0f1f25e0ead 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -60,6 +60,10 @@ struct idmap_msg { }; #ifdef __KERNEL__ + +/* Forward declaration to make this header independent of others */ +struct nfs4_client; + void nfs_idmap_new(struct nfs4_client *); void nfs_idmap_delete(struct nfs4_client *); -- cgit v1.2.3 From fa36f31ad068e0498c61a1454598be8d8333ee05 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 9 Feb 2004 00:26:00 -0800 Subject: Clean up dentry pointer validation by moving it into a function of its own. This also allows us to do a better job, since slab.c can now do more proper tests. --- fs/dcache.c | 13 ++----------- include/linux/slab.h | 1 + mm/slab.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/dcache.c b/fs/dcache.c index 03825e26dc49..95941d73cd20 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1035,20 +1035,11 @@ struct dentry * __d_lookup(struct dentry * parent, struct qstr * name) int d_validate(struct dentry *dentry, struct dentry *dparent) { - unsigned long dent_addr = (unsigned long) dentry; - unsigned long min_addr = PAGE_OFFSET; - unsigned long align_mask = 0x0F; struct hlist_head *base; struct hlist_node *lhp; - if (dent_addr < min_addr) - goto out; - if (dent_addr > (unsigned long)high_memory - sizeof(struct dentry)) - goto out; - if (dent_addr & align_mask) - goto out; - if ((!kern_addr_valid(dent_addr)) || (!kern_addr_valid(dent_addr -1 + - sizeof(struct dentry)))) + /* Check whether the ptr might be valid at all.. */ + if (!kmem_ptr_validate(dentry_cache, dentry)) goto out; if (dentry->d_parent != dparent) diff --git a/include/linux/slab.h b/include/linux/slab.h index d797c981f37e..69be5b308a11 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -101,6 +101,7 @@ extern void kfree(const void *); extern unsigned int ksize(const void *); extern int FASTCALL(kmem_cache_reap(int)); +extern int FASTCALL(kmem_ptr_validate(kmem_cache_t *cachep, void *ptr)); /* System wide caches */ extern kmem_cache_t *vm_area_cachep; diff --git a/mm/slab.c b/mm/slab.c index 155399a02481..744b70e4e563 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -2073,6 +2073,48 @@ void * kmem_cache_alloc (kmem_cache_t *cachep, int flags) EXPORT_SYMBOL(kmem_cache_alloc); +/** + * kmem_ptr_validate - check if an untrusted pointer might + * be a slab entry. + * @cachep: the cache we're checking against + * @ptr: pointer to validate + * + * This verifies that the untrusted pointer looks sane: + * it is _not_ a guarantee that the pointer is actually + * part of the slab cache in question, but it at least + * validates that the pointer can be dereferenced and + * looks half-way sane. + * + * Currently only used for dentry validation. + */ +int kmem_ptr_validate(kmem_cache_t *cachep, void *ptr) +{ + unsigned long addr = (unsigned long) ptr; + unsigned long min_addr = PAGE_OFFSET; + unsigned long align_mask = BYTES_PER_WORD-1; + unsigned long size = cachep->objsize; + struct page *page; + + if (unlikely(addr < min_addr)) + goto out; + if (unlikely(addr > (unsigned long)high_memory - size)) + goto out; + if (unlikely(addr & align_mask)) + goto out; + if (unlikely(!kern_addr_valid(addr))) + goto out; + if (unlikely(!kern_addr_valid(addr + size - 1))) + goto out; + page = virt_to_page(ptr); + if (unlikely(!PageSlab(page))) + goto out; + if (unlikely(GET_PAGE_CACHE(page) != cachep)) + goto out; + return 1; +out: + return 0; +} + /** * kmalloc - allocate memory * @size: how many bytes of memory are required. -- cgit v1.2.3 From 11c975e549c81716b8329f51f26228381d06eda4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 9 Feb 2004 17:00:29 -0800 Subject: [PATCH] fix build for CONFIG_BLK_DEV_IDEDMA=n Ths "fix duplication of DMA {black,white}list in icside.c" patch broke it. Noticed by Geert Uytterhoeven . --- include/linux/ide.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ide.h b/include/linux/ide.h index d247474de882..1533e9ab256e 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -1626,6 +1626,7 @@ extern int __ide_dma_count(ide_drive_t *); extern int __ide_dma_verbose(ide_drive_t *); extern int __ide_dma_lostirq(ide_drive_t *); extern int __ide_dma_timeout(ide_drive_t *); +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ #ifdef CONFIG_BLK_DEV_IDE_TCQ extern int __ide_dma_queued_on(ide_drive_t *drive); @@ -1634,13 +1635,12 @@ extern ide_startstop_t __ide_dma_queued_read(ide_drive_t *drive); extern ide_startstop_t __ide_dma_queued_write(ide_drive_t *drive); extern ide_startstop_t __ide_dma_queued_start(ide_drive_t *drive); #endif +#endif /* CONFIG_BLK_DEV_IDEDMA */ -#else +#ifndef CONFIG_BLK_DEV_IDEDMA_PCI static inline void ide_release_dma(ide_hwif_t *drive) {;} #endif -#endif /* CONFIG_BLK_DEV_IDEDMA */ - extern int ide_hwif_request_regions(ide_hwif_t *hwif); extern void ide_hwif_release_regions(ide_hwif_t* hwif); extern void ide_unregister (unsigned int index); -- cgit v1.2.3 From 122123c4b298a688d43666d0355ca253070c1ea8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 9 Feb 2004 17:10:33 -0800 Subject: Make a bit more palatable to user program inclusion. It's still wrong to include kernel headers from user programs. Oh, well. --- include/linux/compiler.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/compiler.h b/include/linux/compiler.h index ba3ef774bc17..cc46aa0584f9 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -9,6 +9,8 @@ # define __kernel #endif +#ifdef __KERNEL__ + #ifndef __ASSEMBLY__ #if __GNUC__ > 3 # include /* catch-all for GCC 4, 5, etc. */ @@ -106,4 +108,6 @@ (typeof(ptr)) (__ptr + (off)); }) #endif +#endif /* __KERNEL__ */ + #endif /* __LINUX_COMPILER_H */ -- cgit v1.2.3 From 60ce3fec2394b70870381f348159a29374bd9d11 Mon Sep 17 00:00:00 2001 From: Chas Williams Date: Wed, 11 Feb 2004 06:01:20 -0800 Subject: [ATM]: prevent userspace compilation errors with glibc-kernheaders --- include/linux/atmdev.h | 4 +--- include/linux/sonet.h | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h index 0b371eb0e69d..81f37e55c510 100644 --- a/include/linux/atmdev.h +++ b/include/linux/atmdev.h @@ -200,9 +200,7 @@ struct atm_cirange { "SESSION", "HASSAP", "BOUND", "CLOSE" -#ifndef __KERNEL__ -#undef __AAL_STAT_ITEMS -#else +#ifdef __KERNEL__ #include /* wait_queue_head_t */ #include /* struct timeval */ diff --git a/include/linux/sonet.h b/include/linux/sonet.h index 30c45ec59e2e..753680296e17 100644 --- a/include/linux/sonet.h +++ b/include/linux/sonet.h @@ -56,9 +56,7 @@ struct sonet_stats { #define SONET_FRSENSE_SIZE 6 /* C1[3],H1[3] (0xff for unknown) */ -#ifndef __KERNEL__ -#undef __SONET_ITEMS -#else +#ifdef __KERNEL__ #include -- cgit v1.2.3 From 7a35dff56037cdc1fa0a46b11b48a0776928fc62 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 11 Feb 2004 07:29:54 -0800 Subject: [PATCH] New radeonfb Here is the new radeonfb. It doesn't remove the old one, just in case, though CONFIG_FB_RADEON now builds the new one. The new driver supports recent cards, has better monitor detection, including DDC2, fixes a couple of constants in the old driver, and a lot more. I had to add an empty fb_set_suspend() function to fbmem.c (the real implementation is in James tree and will be here soon). That means that Power Management on Apple laptops isn't completely right yet until the core fbdev fixes get in, but it's good enough for now. --- drivers/video/Kconfig | 25 + drivers/video/Makefile | 3 +- drivers/video/aty/Makefile | 6 + drivers/video/aty/ati_ids.h | 167 +++ drivers/video/aty/radeon_accel.c | 288 +++++ drivers/video/aty/radeon_base.c | 2387 ++++++++++++++++++++++++++++++++++++ drivers/video/aty/radeon_i2c.c | 258 ++++ drivers/video/aty/radeon_monitor.c | 908 ++++++++++++++ drivers/video/aty/radeon_pm.c | 929 ++++++++++++++ drivers/video/aty/radeonfb.h | 566 +++++++++ drivers/video/fbmem.c | 4 + drivers/video/radeonfb.c | 6 +- include/linux/fb.h | 1 + include/video/radeon.h | 1102 ++++++++++++++++- 14 files changed, 6632 insertions(+), 18 deletions(-) create mode 100644 drivers/video/aty/ati_ids.h create mode 100644 drivers/video/aty/radeon_accel.c create mode 100644 drivers/video/aty/radeon_base.c create mode 100644 drivers/video/aty/radeon_i2c.c create mode 100644 drivers/video/aty/radeon_monitor.c create mode 100644 drivers/video/aty/radeon_pm.c create mode 100644 drivers/video/aty/radeonfb.h (limited to 'include/linux') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index ba422c785e8c..cd73949a641a 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -614,6 +614,16 @@ config FB_MATROX_MULTIHEAD There is no need for enabling 'Matrox multihead support' if you have only one Matrox card in the box. +config FB_RADEON_OLD + tristate "ATI Radeon display support (Old driver)" + depends on FB && PCI + help + Choose this option if you want to use an ATI Radeon graphics card as + a framebuffer device. There are both PCI and AGP versions. You + don't need to choose this to run the Radeon in plain VGA mode. + There is a product page at + . + config FB_RADEON tristate "ATI Radeon display support" depends on FB && PCI @@ -621,9 +631,24 @@ config FB_RADEON Choose this option if you want to use an ATI Radeon graphics card as a framebuffer device. There are both PCI and AGP versions. You don't need to choose this to run the Radeon in plain VGA mode. + + If you say Y here and want DDC/I2C support you must first say Y to + "I2C support" and "I2C bit-banging support" in the character devices + section. + + If you say M here then "I2C support" and "I2C bit-banging support" + can be build either as modules or built-in. + There is a product page at . +config FB_RADEON_I2C + bool "DDC/I2C for ATI Radeon support" + depends on FB_RADEON && (I2C_ALGOBIT=FB_RADEON || I2C_ALGOBIT=y) + default y + help + Say Y here if you want DDC/I2C support for your Radeon board. + config FB_ATY128 tristate "ATI Rage128 display support" depends on FB && PCI diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 0be86a27fdc2..6bca6513ccfe 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -21,7 +21,7 @@ obj-$(CONFIG_FB_APOLLO) += dnfb.o cfbfillrect.o cfbimgblt.o obj-$(CONFIG_FB_Q40) += q40fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_ATARI) += atafb.o obj-$(CONFIG_FB_68328) += 68328fb.o -obj-$(CONFIG_FB_RADEON) += radeonfb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o +obj-$(CONFIG_FB_RADEON_OLD) += radeonfb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_NEOMAGIC) += neofb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_IGA) += igafb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_CONTROL) += controlfb.o macmodes.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o @@ -60,6 +60,7 @@ obj-$(CONFIG_FB_RIVA) += riva/ cfbimgblt.o vgastate.o obj-$(CONFIG_FB_SIS) += sis/ cfbcopyarea.o cfbfillrect.o cfbimgblt.o obj-$(CONFIG_FB_ATY) += aty/ cfbcopyarea.o cfbfillrect.o cfbimgblt.o obj-$(CONFIG_FB_ATY128) += aty/ cfbcopyarea.o cfbfillrect.o cfbimgblt.o +obj-$(CONFIG_FB_RADEON) += aty/ cfbcopyarea.o cfbfillrect.o cfbimgblt.o obj-$(CONFIG_FB_I810) += i810/ cfbfillrect.o cfbcopyarea.o \ cfbimgblt.o vgastate.o diff --git a/drivers/video/aty/Makefile b/drivers/video/aty/Makefile index 7515715f75cc..8aa5368736ac 100644 --- a/drivers/video/aty/Makefile +++ b/drivers/video/aty/Makefile @@ -1,7 +1,13 @@ obj-$(CONFIG_FB_ATY) += atyfb.o obj-$(CONFIG_FB_ATY128) += aty128fb.o +obj-$(CONFIG_FB_RADEON) += radeonfb.o atyfb-y := atyfb_base.o mach64_accel.o atyfb-$(CONFIG_FB_ATY_GX) += mach64_gx.o atyfb-$(CONFIG_FB_ATY_CT) += mach64_ct.o mach64_cursor.o atyfb-objs := $(atyfb-y) + +radeonfb-y := radeon_base.o radeon_pm.o radeon_monitor.o radeon_accel.o +radeonfb-$(CONFIG_FB_RADEON_I2C) += radeon_i2c.o +radeonfb-objs := $(radeonfb-y) + diff --git a/drivers/video/aty/ati_ids.h b/drivers/video/aty/ati_ids.h new file mode 100644 index 000000000000..d99e518aa5f2 --- /dev/null +++ b/drivers/video/aty/ati_ids.h @@ -0,0 +1,167 @@ +/* + * ATI PCI IDs from XFree86, kept here to make sync'ing with + * XFree much simpler. Currently, this list is only used by + * radeonfb + */ + +#define PCI_CHIP_RS100_4136 0x4136 +#define PCI_CHIP_RS200_4137 0x4137 +#define PCI_CHIP_R300_AD 0x4144 +#define PCI_CHIP_R300_AE 0x4145 +#define PCI_CHIP_R300_AF 0x4146 +#define PCI_CHIP_R300_AG 0x4147 +#define PCI_CHIP_R350_AH 0x4148 +#define PCI_CHIP_R350_AI 0x4149 +#define PCI_CHIP_R350_AJ 0x414A +#define PCI_CHIP_R350_AK 0x414B +#define PCI_CHIP_RV350_AP 0x4150 +#define PCI_CHIP_RV350_AQ 0x4151 +#define PCI_CHIP_RV360_AR 0x4152 +#define PCI_CHIP_RV350_AS 0x4153 +#define PCI_CHIP_RV350_AT 0x4154 +#define PCI_CHIP_RV350_AV 0x4156 +#define PCI_CHIP_MACH32 0x4158 +#define PCI_CHIP_RS250_4237 0x4237 +#define PCI_CHIP_R200_BB 0x4242 +#define PCI_CHIP_R200_BC 0x4243 +#define PCI_CHIP_RS100_4336 0x4336 +#define PCI_CHIP_RS200_4337 0x4337 +#define PCI_CHIP_MACH64CT 0x4354 +#define PCI_CHIP_MACH64CX 0x4358 +#define PCI_CHIP_RS250_4437 0x4437 +#define PCI_CHIP_MACH64ET 0x4554 +#define PCI_CHIP_MACH64GB 0x4742 +#define PCI_CHIP_MACH64GD 0x4744 +#define PCI_CHIP_MACH64GI 0x4749 +#define PCI_CHIP_MACH64GL 0x474C +#define PCI_CHIP_MACH64GM 0x474D +#define PCI_CHIP_MACH64GN 0x474E +#define PCI_CHIP_MACH64GO 0x474F +#define PCI_CHIP_MACH64GP 0x4750 +#define PCI_CHIP_MACH64GQ 0x4751 +#define PCI_CHIP_MACH64GR 0x4752 +#define PCI_CHIP_MACH64GS 0x4753 +#define PCI_CHIP_MACH64GT 0x4754 +#define PCI_CHIP_MACH64GU 0x4755 +#define PCI_CHIP_MACH64GV 0x4756 +#define PCI_CHIP_MACH64GW 0x4757 +#define PCI_CHIP_MACH64GX 0x4758 +#define PCI_CHIP_MACH64GY 0x4759 +#define PCI_CHIP_MACH64GZ 0x475A +#define PCI_CHIP_RV250_Id 0x4964 +#define PCI_CHIP_RV250_Ie 0x4965 +#define PCI_CHIP_RV250_If 0x4966 +#define PCI_CHIP_RV250_Ig 0x4967 +#define PCI_CHIP_MACH64LB 0x4C42 +#define PCI_CHIP_MACH64LD 0x4C44 +#define PCI_CHIP_RAGE128LE 0x4C45 +#define PCI_CHIP_RAGE128LF 0x4C46 +#define PCI_CHIP_MACH64LG 0x4C47 +#define PCI_CHIP_MACH64LI 0x4C49 +#define PCI_CHIP_MACH64LM 0x4C4D +#define PCI_CHIP_MACH64LN 0x4C4E +#define PCI_CHIP_MACH64LP 0x4C50 +#define PCI_CHIP_MACH64LQ 0x4C51 +#define PCI_CHIP_MACH64LR 0x4C52 +#define PCI_CHIP_MACH64LS 0x4C53 +#define PCI_CHIP_RADEON_LW 0x4C57 +#define PCI_CHIP_RADEON_LX 0x4C58 +#define PCI_CHIP_RADEON_LY 0x4C59 +#define PCI_CHIP_RADEON_LZ 0x4C5A +#define PCI_CHIP_RV250_Ld 0x4C64 +#define PCI_CHIP_RV250_Le 0x4C65 +#define PCI_CHIP_RV250_Lf 0x4C66 +#define PCI_CHIP_RV250_Lg 0x4C67 +#define PCI_CHIP_RAGE128MF 0x4D46 +#define PCI_CHIP_RAGE128ML 0x4D4C +#define PCI_CHIP_R300_ND 0x4E44 +#define PCI_CHIP_R300_NE 0x4E45 +#define PCI_CHIP_R300_NF 0x4E46 +#define PCI_CHIP_R300_NG 0x4E47 +#define PCI_CHIP_R350_NH 0x4E48 +#define PCI_CHIP_R350_NI 0x4E49 +#define PCI_CHIP_R360_NJ 0x4E4A +#define PCI_CHIP_R350_NK 0x4E4B +#define PCI_CHIP_RV350_NP 0x4E50 +#define PCI_CHIP_RV350_NQ 0x4E51 +#define PCI_CHIP_RV350_NR 0x4E52 +#define PCI_CHIP_RV350_NS 0x4E53 +#define PCI_CHIP_RV350_NT 0x4E54 +#define PCI_CHIP_RV350_NV 0x4E56 +#define PCI_CHIP_RAGE128PA 0x5041 +#define PCI_CHIP_RAGE128PB 0x5042 +#define PCI_CHIP_RAGE128PC 0x5043 +#define PCI_CHIP_RAGE128PD 0x5044 +#define PCI_CHIP_RAGE128PE 0x5045 +#define PCI_CHIP_RAGE128PF 0x5046 +#define PCI_CHIP_RAGE128PG 0x5047 +#define PCI_CHIP_RAGE128PH 0x5048 +#define PCI_CHIP_RAGE128PI 0x5049 +#define PCI_CHIP_RAGE128PJ 0x504A +#define PCI_CHIP_RAGE128PK 0x504B +#define PCI_CHIP_RAGE128PL 0x504C +#define PCI_CHIP_RAGE128PM 0x504D +#define PCI_CHIP_RAGE128PN 0x504E +#define PCI_CHIP_RAGE128PO 0x504F +#define PCI_CHIP_RAGE128PP 0x5050 +#define PCI_CHIP_RAGE128PQ 0x5051 +#define PCI_CHIP_RAGE128PR 0x5052 +#define PCI_CHIP_RAGE128PS 0x5053 +#define PCI_CHIP_RAGE128PT 0x5054 +#define PCI_CHIP_RAGE128PU 0x5055 +#define PCI_CHIP_RAGE128PV 0x5056 +#define PCI_CHIP_RAGE128PW 0x5057 +#define PCI_CHIP_RAGE128PX 0x5058 +#define PCI_CHIP_RADEON_QD 0x5144 +#define PCI_CHIP_RADEON_QE 0x5145 +#define PCI_CHIP_RADEON_QF 0x5146 +#define PCI_CHIP_RADEON_QG 0x5147 +#define PCI_CHIP_R200_QH 0x5148 +#define PCI_CHIP_R200_QI 0x5149 +#define PCI_CHIP_R200_QJ 0x514A +#define PCI_CHIP_R200_QK 0x514B +#define PCI_CHIP_R200_QL 0x514C +#define PCI_CHIP_R200_QM 0x514D +#define PCI_CHIP_R200_QN 0x514E +#define PCI_CHIP_R200_QO 0x514F +#define PCI_CHIP_RV200_QW 0x5157 +#define PCI_CHIP_RV200_QX 0x5158 +#define PCI_CHIP_RV100_QY 0x5159 +#define PCI_CHIP_RV100_QZ 0x515A +#define PCI_CHIP_RAGE128RE 0x5245 +#define PCI_CHIP_RAGE128RF 0x5246 +#define PCI_CHIP_RAGE128RG 0x5247 +#define PCI_CHIP_RAGE128RK 0x524B +#define PCI_CHIP_RAGE128RL 0x524C +#define PCI_CHIP_RAGE128SE 0x5345 +#define PCI_CHIP_RAGE128SF 0x5346 +#define PCI_CHIP_RAGE128SG 0x5347 +#define PCI_CHIP_RAGE128SH 0x5348 +#define PCI_CHIP_RAGE128SK 0x534B +#define PCI_CHIP_RAGE128SL 0x534C +#define PCI_CHIP_RAGE128SM 0x534D +#define PCI_CHIP_RAGE128SN 0x534E +#define PCI_CHIP_RAGE128TF 0x5446 +#define PCI_CHIP_RAGE128TL 0x544C +#define PCI_CHIP_RAGE128TR 0x5452 +#define PCI_CHIP_RAGE128TS 0x5453 +#define PCI_CHIP_RAGE128TT 0x5454 +#define PCI_CHIP_RAGE128TU 0x5455 +#define PCI_CHIP_MACH64VT 0x5654 +#define PCI_CHIP_MACH64VU 0x5655 +#define PCI_CHIP_MACH64VV 0x5656 +#define PCI_CHIP_RS300_5834 0x5834 +#define PCI_CHIP_RS300_5835 0x5835 +#define PCI_CHIP_RS300_5836 0x5836 +#define PCI_CHIP_RS300_5837 0x5837 +#define PCI_CHIP_RV280_5960 0x5960 +#define PCI_CHIP_RV280_5961 0x5961 +#define PCI_CHIP_RV280_5962 0x5962 +#define PCI_CHIP_RV280_5963 0x5963 +#define PCI_CHIP_RV280_5964 0x5964 +#define PCI_CHIP_RV280_5968 0x5968 +#define PCI_CHIP_RV280_5969 0x5969 +#define PCI_CHIP_RV280_596A 0x596A +#define PCI_CHIP_RV280_596B 0x596B +#define PCI_CHIP_RV280_5C61 0x5C61 +#define PCI_CHIP_RV280_5C63 0x5C63 diff --git a/drivers/video/aty/radeon_accel.c b/drivers/video/aty/radeon_accel.c new file mode 100644 index 000000000000..f05a2d50fb6f --- /dev/null +++ b/drivers/video/aty/radeon_accel.c @@ -0,0 +1,288 @@ +#include "radeonfb.h" + +/* the accelerated functions here are patterned after the + * "ACCEL_MMIO" ifdef branches in XFree86 + * --dte + */ +static void radeonfb_prim_fillrect(struct radeonfb_info *rinfo, + const struct fb_fillrect *region) +{ + radeon_fifo_wait(4); + + OUTREG(DP_GUI_MASTER_CNTL, + rinfo->dp_gui_master_cntl /* contains, like GMC_DST_32BPP */ + | GMC_BRUSH_SOLID_COLOR + | ROP3_P); + OUTREG(DP_BRUSH_FRGD_CLR, region->color); + OUTREG(DP_WRITE_MSK, 0xffffffff); + OUTREG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM)); + + radeon_fifo_wait(2); + OUTREG(DST_Y_X, (region->dy << 16) | region->dx); + OUTREG(DST_WIDTH_HEIGHT, (region->width << 16) | region->height); +} + +void radeonfb_fillrect(struct fb_info *info, const struct fb_fillrect *region) +{ + struct radeonfb_info *rinfo = info->par; + struct fb_fillrect modded; + int vxres, vyres; + + if (rinfo->asleep) + return; + if (radeon_accel_disabled()) { + cfb_fillrect(info, region); + return; + } + + vxres = info->var.xres; + vyres = info->var.yres; + + memcpy(&modded, region, sizeof(struct fb_fillrect)); + + if(!modded.width || !modded.height || + modded.dx >= vxres || modded.dy >= vyres) + return; + + if(modded.dx + modded.width > vxres) modded.width = vxres - modded.dx; + if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy; + + radeonfb_prim_fillrect(rinfo, &modded); +} + +static void radeonfb_prim_copyarea(struct radeonfb_info *rinfo, + const struct fb_copyarea *area) +{ + radeon_fifo_wait(3); + OUTREG(DP_GUI_MASTER_CNTL, + rinfo->dp_gui_master_cntl /* i.e. GMC_DST_32BPP */ + | GMC_SRC_DSTCOLOR + | ROP3_S + | DP_SRC_RECT ); + OUTREG(DP_WRITE_MSK, 0xffffffff); + OUTREG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM)); + + radeon_fifo_wait(3); + OUTREG(SRC_Y_X, (area->sy << 16) | area->sx); + OUTREG(DST_Y_X, (area->dy << 16) | area->dx); + OUTREG(DST_HEIGHT_WIDTH, (area->height << 16) | area->width); +} + + +void radeonfb_copyarea(struct fb_info *info, const struct fb_copyarea *area) +{ + struct radeonfb_info *rinfo = info->par; + struct fb_copyarea modded; + u32 vxres, vyres; + modded.sx = area->sx; + modded.sy = area->sy; + modded.dx = area->dx; + modded.dy = area->dy; + modded.width = area->width; + modded.height = area->height; + + if (rinfo->asleep) + return; + if (radeon_accel_disabled()) { + cfb_copyarea(info, area); + return; + } + + vxres = info->var.xres; + vyres = info->var.yres; + + if(!modded.width || !modded.height || + modded.sx >= vxres || modded.sy >= vyres || + modded.dx >= vxres || modded.dy >= vyres) + return; + + if(modded.sx + modded.width > vxres) modded.width = vxres - modded.sx; + if(modded.dx + modded.width > vxres) modded.width = vxres - modded.dx; + if(modded.sy + modded.height > vyres) modded.height = vyres - modded.sy; + if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy; + + radeonfb_prim_copyarea(rinfo, &modded); +} + +void radeonfb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct radeonfb_info *rinfo = info->par; + + if (rinfo->asleep) + return; + radeon_engine_idle(); + + cfb_imageblit(info, image); +} + +int radeonfb_sync(struct fb_info *info) +{ + struct radeonfb_info *rinfo = info->par; + + if (rinfo->asleep) + return 0; + radeon_engine_idle(); + + return 0; +} + +void radeon_engine_reset(struct radeonfb_info *rinfo) +{ + u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset; + u32 host_path_cntl; + + radeon_engine_flush (rinfo); + + /* Some ASICs have bugs with dynamic-on feature, which are + * ASIC-version dependent, so we force all blocks on for now + * -- from XFree86 + * We don't do that on macs, things just work here with dynamic + * clocking... --BenH + */ +#ifdef CONFIG_ALL_PPC + if (_machine != _MACH_Pmac && rinfo->hasCRTC2) +#else + if (rinfo->has_CRTC2) +#endif + { + u32 tmp; + + tmp = INPLL(SCLK_CNTL); + OUTPLL(SCLK_CNTL, ((tmp & ~DYN_STOP_LAT_MASK) | + CP_MAX_DYN_STOP_LAT | + SCLK_FORCEON_MASK)); + + if (rinfo->family == CHIP_FAMILY_RV200) + { + tmp = INPLL(SCLK_MORE_CNTL); + OUTPLL(SCLK_MORE_CNTL, tmp | SCLK_MORE_FORCEON); + } + } + + clock_cntl_index = INREG(CLOCK_CNTL_INDEX); + mclk_cntl = INPLL(MCLK_CNTL); + + OUTPLL(MCLK_CNTL, (mclk_cntl | + FORCEON_MCLKA | + FORCEON_MCLKB | + FORCEON_YCLKA | + FORCEON_YCLKB | + FORCEON_MC | + FORCEON_AIC)); + + host_path_cntl = INREG(HOST_PATH_CNTL); + rbbm_soft_reset = INREG(RBBM_SOFT_RESET); + + if (rinfo->family == CHIP_FAMILY_R300 || + rinfo->family == CHIP_FAMILY_R350 || + rinfo->family == CHIP_FAMILY_RV350) { + u32 tmp; + + OUTREG(RBBM_SOFT_RESET, (rbbm_soft_reset | + SOFT_RESET_CP | + SOFT_RESET_HI | + SOFT_RESET_E2)); + INREG(RBBM_SOFT_RESET); + OUTREG(RBBM_SOFT_RESET, 0); + tmp = INREG(RB2D_DSTCACHE_MODE); + OUTREG(RB2D_DSTCACHE_MODE, tmp | (1 << 17)); /* FIXME */ + } else { + OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset | + SOFT_RESET_CP | + SOFT_RESET_HI | + SOFT_RESET_SE | + SOFT_RESET_RE | + SOFT_RESET_PP | + SOFT_RESET_E2 | + SOFT_RESET_RB); + INREG(RBBM_SOFT_RESET); + OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset & (u32) + ~(SOFT_RESET_CP | + SOFT_RESET_HI | + SOFT_RESET_SE | + SOFT_RESET_RE | + SOFT_RESET_PP | + SOFT_RESET_E2 | + SOFT_RESET_RB)); + INREG(RBBM_SOFT_RESET); + } + + OUTREG(HOST_PATH_CNTL, host_path_cntl | HDP_SOFT_RESET); + INREG(HOST_PATH_CNTL); + OUTREG(HOST_PATH_CNTL, host_path_cntl); + + if (rinfo->family != CHIP_FAMILY_R300 || + rinfo->family != CHIP_FAMILY_R350 || + rinfo->family != CHIP_FAMILY_RV350) + OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset); + + OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index); + OUTPLL(MCLK_CNTL, mclk_cntl); + if (rinfo->R300_cg_workaround) + R300_cg_workardound(rinfo); +} + +void radeon_engine_init (struct radeonfb_info *rinfo) +{ + unsigned long temp; + + /* disable 3D engine */ + OUTREG(RB3D_CNTL, 0); + + radeon_engine_reset(rinfo); + + radeon_fifo_wait (1); + if ((rinfo->family != CHIP_FAMILY_R300) && + (rinfo->family != CHIP_FAMILY_R350) && + (rinfo->family != CHIP_FAMILY_RV350)) + OUTREG(RB2D_DSTCACHE_MODE, 0); + + radeon_fifo_wait (3); + /* We re-read MC_FB_LOCATION from card as it can have been + * modified by XFree drivers (ouch !) + */ + rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16; + + OUTREG(DEFAULT_PITCH_OFFSET, (rinfo->pitch << 0x16) | + (rinfo->fb_local_base >> 10)); + OUTREG(DST_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10)); + OUTREG(SRC_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10)); + + radeon_fifo_wait (1); +#if defined(__BIG_ENDIAN) + OUTREGP(DP_DATATYPE, HOST_BIG_ENDIAN_EN, ~HOST_BIG_ENDIAN_EN); +#else + OUTREGP(DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN); +#endif + radeon_fifo_wait (2); + OUTREG(DEFAULT_SC_TOP_LEFT, 0); + OUTREG(DEFAULT_SC_BOTTOM_RIGHT, (DEFAULT_SC_RIGHT_MAX | + DEFAULT_SC_BOTTOM_MAX)); + + temp = radeon_get_dstbpp(rinfo->depth); + rinfo->dp_gui_master_cntl = ((temp << 8) | GMC_CLR_CMP_CNTL_DIS); + + radeon_fifo_wait (1); + OUTREG(DP_GUI_MASTER_CNTL, (rinfo->dp_gui_master_cntl | + GMC_BRUSH_SOLID_COLOR | + GMC_SRC_DATATYPE_COLOR)); + + radeon_fifo_wait (7); + + /* clear line drawing regs */ + OUTREG(DST_LINE_START, 0); + OUTREG(DST_LINE_END, 0); + + /* set brush color regs */ + OUTREG(DP_BRUSH_FRGD_CLR, 0xffffffff); + OUTREG(DP_BRUSH_BKGD_CLR, 0x00000000); + + /* set source color regs */ + OUTREG(DP_SRC_FRGD_CLR, 0xffffffff); + OUTREG(DP_SRC_BKGD_CLR, 0x00000000); + + /* default write mask */ + OUTREG(DP_WRITE_MSK, 0xffffffff); + + radeon_engine_idle (); +} diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c new file mode 100644 index 000000000000..c8b6c1835214 --- /dev/null +++ b/drivers/video/aty/radeon_base.c @@ -0,0 +1,2387 @@ +/* + * drivers/video/radeonfb.c + * framebuffer driver for ATI Radeon chipset video boards + * + * Copyright 2003 Ben. Herrenschmidt + * Copyright 2000 Ani Joshi + * + * i2c bits from Luca Tettamanti + * + * Special thanks to ATI DevRel team for their hardware donations. + * + * ...Insert GPL boilerplate here... + * + * Significant portions of this driver apdated from XFree86 Radeon + * driver which has the following copyright notice: + * + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + * VA Linux Systems Inc., Fremont, California. + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * 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 + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * XFree86 driver authors: + * + * Kevin E. Martin + * Rickard E. Faith + * Alan Hourihane + * + */ + + +#define RADEON_VERSION "0.2.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_PPC_OF + +#include +#include +#include "../macmodes.h" + +#ifdef CONFIG_PMAC_BACKLIGHT +#include +#endif + +#ifdef CONFIG_BOOTX_TEXT +#include +#endif + +#endif /* CONFIG_PPC_OF */ + +#ifdef CONFIG_MTRR +#include +#endif + +#include