From 0bcdd9489f3c853cdb38291f1d5385f6ab660223 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Mon, 7 Mar 2005 06:52:55 -0800 Subject: [PATCH] USB: add usbmon, a USB monitoring framework This patch adds so-called "usbmon", or USB monitoring framework, similar to what tcpdump provides for Ethernet. This is an initial version, but it should be safe and useful. It adds an overhead of an if () statement into submission and giveback paths even when not monitoring, but this was deemed a lesser evil than stealth manipulation of function pointers. The patch makes two changes to hcd.c which make usbmon more useful: - Change the way we determine that DMA should not be mapped for root hubs, so that usbmon knows easily when it's safe to capture data. - Return exports of usb_bus_list and usb_bus_list_lock for those who wish to build usbmon as a module. This version of the patch changes #define to inlines for hooks and drops extra mod_ops. Signed-off-by: Pete Zaitcev Signed-off-by: Greg Kroah-Hartman --- include/linux/usb.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/usb.h b/include/linux/usb.h index 7dbcc054c7dc..d4403982dafd 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -285,6 +285,10 @@ struct usb_bus { struct class_device class_dev; /* class device for this bus */ void (*release)(struct usb_bus *bus); /* function to destroy this bus's memory */ +#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) + struct mon_bus *mon_bus; /* non-null when associated */ + int monitored; /* non-zero when monitored */ +#endif }; #define to_usb_bus(d) container_of(d, struct usb_bus, class_dev) -- cgit v1.2.3 From 9ab81c3936b5e6ff6fd2cf40eccc8d74463f46fc Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 7 Mar 2005 06:55:19 -0800 Subject: [PATCH] USB: add This adds a new header file, with definitions for the CDC class constants and structures used by various drivers. For now this only has the ones Linux actually uses. Each one is used in at least two or three different drivers, so sharing the definitions helps reduce errors. It's also a good excuse to make sure there "sparse -Wbitwise" doesn't report errors in how these are used! Patches to those drivers will follow as I have time to verify the updates: - CDC ACM (for serial lines and modems) * Host side support in "cdc-acm" * Peripheral side support in "g_serial" - CDC Ethernet (cable modems, PDAs, etc) * Host side support in "usbnet" * Peripheral side support in "g_ether" Also, Microsoft's RNDIS is a variant of CDC ACM, providing an Ethernet model and implemented by g_ether; it uses these definitions too. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- include/linux/usb_cdc.h | 162 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 include/linux/usb_cdc.h (limited to 'include/linux') diff --git a/include/linux/usb_cdc.h b/include/linux/usb_cdc.h new file mode 100644 index 000000000000..0b8d9a78a51a --- /dev/null +++ b/include/linux/usb_cdc.h @@ -0,0 +1,162 @@ +/* + * USB Communications Device Class (CDC) definitions + * + * CDC says how to talk to lots of different types of network adapters, + * notably ethernet adapters and various modems. It's used mostly with + * firmware based USB peripherals. + */ + +#define USB_CDC_SUBCLASS_ACM 2 +#define USB_CDC_SUBCLASS_ETHERNET 6 + +#define USB_CDC_PROTO_NONE 0 + +#define USB_CDC_ACM_PROTO_AT_V25TER 1 +#define USB_CDC_ACM_PROTO_AT_PCCA101 2 +#define USB_CDC_ACM_PROTO_AT_PCCA101_WAKE 3 +#define USB_CDC_ACM_PROTO_AT_GSM 4 +#define USB_CDC_ACM_PROTO_AT_3G 5 +#define USB_CDC_ACM_PROTO_AT_CDMA 6 +#define USB_CDC_ACM_PROTO_VENDOR 0xff + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific descriptors ... there are a couple dozen of them + */ + +#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */ +#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */ +#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */ +#define USB_CDC_UNION_TYPE 0x06 /* union_desc */ +#define USB_CDC_COUNTRY_TYPE 0x07 +#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */ + +/* "Header Functional Descriptor" from CDC spec 5.2.3.1 */ +struct usb_cdc_header_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __le16 bcdCDC; +} __attribute__ ((packed)); + +/* "Call Management Descriptor" from CDC spec 5.2.3.2 */ +struct usb_cdc_call_mgmt_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bmCapabilities; +#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT 0x01 +#define USB_CDC_CALL_MGMT_CAP_DATA_INTF 0x02 + + __u8 bDataInterface; +} __attribute__ ((packed)); + +/* "Abstract Control Management Descriptor" from CDC spec 5.2.3.3 */ +struct usb_cdc_acm_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bmCapabilities; +} __attribute__ ((packed)); + +/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */ +struct usb_cdc_union_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bMasterInterface0; + __u8 bSlaveInterface0; + /* ... and there could be other slave interfaces */ +} __attribute__ ((packed)); + +/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */ +struct usb_cdc_ether_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 iMACAddress; + __le32 bmEthernetStatistics; + __le16 wMaxSegmentSize; + __le16 wNumberMCFilters; + __u8 bNumberPowerFilters; +} __attribute__ ((packed)); + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific Control Requests (6.2) + * + * section 3.6.2.1 table 4 has the ACM profile, for modems. + * section 3.8.2 table 10 has the ethernet profile. + * + * Microsoft's RNDIS stack for Ethernet is a vendor-specific CDC ACM variant, + * heavily dependent on the encapsulated (proprietary) command mechanism. + */ + +#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 +#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 +#define USB_CDC_REQ_SET_LINE_CODING 0x20 +#define USB_CDC_REQ_GET_LINE_CODING 0x21 +#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define USB_CDC_REQ_SEND_BREAK 0x23 +#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 +#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 +#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 +#define USB_CDC_GET_ETHERNET_STATISTIC 0x44 + +/* Line Coding Structure from CDC spec 6.2.13 */ +struct usb_cdc_line_coding { + __le32 dwDTERate; + __u8 bCharFormat; +#define USB_CDC_1_STOP_BITS 0 +#define USB_CDC_1_5_STOP_BITS 1 +#define USB_CDC_2_STOP_BITS 2 + + __u8 bParityType; +#define USB_CDC_NO_PARITY 0 +#define USB_CDC_ODD_PARITY 1 +#define USB_CDC_EVEN_PARITY 2 +#define USB_CDC_MARK_PARITY 3 +#define USB_CDC_SPACE_PARITY 4 + + __u8 bDataBits; +} __attribute__ ((packed)); + +/* table 62; bits in multicast filter */ +#define USB_CDC_PACKET_TYPE_PROMISCUOUS (1 << 0) +#define USB_CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ +#define USB_CDC_PACKET_TYPE_DIRECTED (1 << 2) +#define USB_CDC_PACKET_TYPE_BROADCAST (1 << 3) +#define USB_CDC_PACKET_TYPE_MULTICAST (1 << 4) /* filtered */ + + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific Notifications (6.3) sent by interrupt transfers + * + * section 3.8.2 table 11 of the CDC spec lists Ethernet notifications + * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS + * RNDIS also defines its own bit-incompatible notifications + */ + +#define USB_CDC_NOTIFY_NETWORK_CONNECTION 0x00 +#define USB_CDC_NOTIFY_RESPONSE_AVAILABLE 0x01 +#define USB_CDC_NOTIFY_SERIAL_STATE 0x20 +#define USB_CDC_NOTIFY_SPEED_CHANGE 0x2a + +struct usb_cdc_notification { + __u8 bmRequestType; + __u8 bNotificationType; + __le16 wValue; + __le16 wIndex; + __le16 wLength; +} __attribute__ ((packed)); + -- cgit v1.2.3 From 56e7cec4797e80a91ab754991a063f5d0282808d Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Mon, 7 Mar 2005 07:19:01 -0800 Subject: [PATCH] include/usb: change USB_CTRL_{SET,GET}_TIMEOUT to msecs Change the units of the timeout constants in usb.h to correspond to the new parameter units for usb_{control,bulk}_msg(), in this case from seconds to milliseconds. Signed-off-by: Nishanth Aravamudan Signed-off-by: Greg Kroah-Hartman --- include/linux/usb.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/usb.h b/include/linux/usb.h index d4403982dafd..837fadde8d3b 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -990,13 +990,13 @@ extern int usb_reset_configuration(struct usb_device *dev); extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); /* - * timeouts, in seconds, used for sending/receiving control messages + * timeouts, in milliseconds, used for sending/receiving control messages * they typically complete within a few frames (msec) after they're issued * USB identifies 5 second timeouts, maybe more in a few cases, and a few * slow devices (like some MGE Ellipse UPSes) actually push that limit. */ -#define USB_CTRL_GET_TIMEOUT 5 -#define USB_CTRL_SET_TIMEOUT 5 +#define USB_CTRL_GET_TIMEOUT 5000 +#define USB_CTRL_SET_TIMEOUT 5000 /** -- cgit v1.2.3 From 1d20dd36774235b0b3c39853f26fc46d776bd2ae Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 7 Mar 2005 08:35:50 -0800 Subject: [PATCH] USB: ehci updates for TDI/ATG silicon This patch updates support for the TDI EHCI controller, which is mostly used on non-PCI systems: - Correctly initialize the latest chip, which has both host (EHCI) and peripheral modes. - Initialize split isochronous transfers to use the integrated TT. Most of the patch, by volume, just changes the company name from ARC to TDI in the source code; TransDimension bought ARC's peripheral connectivity business. From: Craig Nadler Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 2 +- drivers/usb/host/ehci-hcd.c | 53 +++++++++++++++++++++++++++++++------------ drivers/usb/host/ehci-hub.c | 4 ++-- drivers/usb/host/ehci-q.c | 6 ++--- drivers/usb/host/ehci-sched.c | 8 +++++-- drivers/usb/host/ehci.h | 8 +++---- include/linux/pci_ids.h | 4 ++-- 7 files changed, 56 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 55f5cb9004f8..56fefe16b3ee 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -70,7 +70,7 @@ config USB_EHCI_ROOT_HUB_TT controller is needed. It's safe to say "y" even if your controller doesn't support this feature. - This supports the EHCI implementation from ARC International. + This supports the EHCI implementation from TransDimension Inc. config USB_OHCI_HCD tristate "OHCI HCD support" diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 681b7e25b8eb..b0443f3f6cb1 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -191,9 +191,22 @@ static int ehci_halt (struct ehci_hcd *ehci) return handshake (&ehci->regs->status, STS_HALT, STS_HALT, 16 * 125); } +/* put TDI/ARC silicon into EHCI mode */ +static void tdi_reset (struct ehci_hcd *ehci) +{ + u32 __iomem *reg_ptr; + u32 tmp; + + reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + 0x68); + tmp = readl (reg_ptr); + tmp |= 0x3; + writel (tmp, reg_ptr); +} + /* reset a non-running (STS_HALT == 1) controller */ static int ehci_reset (struct ehci_hcd *ehci) { + int retval; u32 command = readl (&ehci->regs->command); command |= CMD_RESET; @@ -201,7 +214,15 @@ static int ehci_reset (struct ehci_hcd *ehci) writel (command, &ehci->regs->command); ehci_to_hcd(ehci)->state = USB_STATE_HALT; ehci->next_statechange = jiffies; - return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000); + retval = handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000); + + if (retval) + return retval; + + if (ehci_is_TDI(ehci)) + tdi_reset (ehci); + + return retval; } /* idle the controller (from running) */ @@ -346,11 +367,20 @@ static int ehci_hc_reset (struct usb_hcd *hcd) if (hcd->self.controller->bus == &pci_bus_type) { struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - /* AMD8111 EHCI doesn't work, according to AMD errata */ - if ((pdev->vendor == PCI_VENDOR_ID_AMD) - && (pdev->device == 0x7463)) { - ehci_info (ehci, "ignoring AMD8111 (errata)\n"); - return -EIO; + switch (pdev->vendor) { + case PCI_VENDOR_ID_TDI: + if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { + ehci->is_tdi_rh_tt = 1; + tdi_reset (ehci); + } + break; + case PCI_VENDOR_ID_AMD: + /* AMD8111 EHCI doesn't work, according to AMD errata */ + if (pdev->device == 0x7463) { + ehci_info (ehci, "ignoring AMD8111 (errata)\n"); + return -EIO; + } + break; } temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params)); @@ -380,6 +410,8 @@ static int ehci_hc_reset (struct usb_hcd *hcd) ehci_err (ehci, "bogus capabilities ... PCI problems!\n"); return -EIO; } + if (ehci_is_TDI(ehci)) + ehci_reset (ehci); #endif /* cache this readonly data; minimize PCI reads */ @@ -481,15 +513,6 @@ static int ehci_start (struct usb_hcd *hcd) /* help hc dma work well with cachelines */ pci_set_mwi (pdev); - - /* chip-specific init */ - switch (pdev->vendor) { - case PCI_VENDOR_ID_ARC: - if (pdev->device == PCI_DEVICE_ID_ARC_EHCI) - ehci->is_arc_rh_tt = 1; - break; - } - } #endif diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 8a1c130f9448..5d2271314994 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -178,7 +178,7 @@ static int check_reset_complete ( if (!(port_status & PORT_PE)) { /* with integrated TT, there's nobody to hand it to! */ - if (ehci_is_ARC(ehci)) { + if (ehci_is_TDI(ehci)) { ehci_dbg (ehci, "Failed to enable port %d on root hub TT\n", index+1); @@ -517,7 +517,7 @@ static int ehci_hub_control ( * transaction translator built in. */ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT - && !ehci_is_ARC(ehci) + && !ehci_is_TDI(ehci) && PORT_USB11 (temp)) { ehci_dbg (ehci, "port %d low speed --> companion\n", diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index b284c4c896af..e9300dbcd229 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -198,7 +198,7 @@ static void qtd_copy_status ( && urb->dev->tt && !usb_pipeint (urb->pipe) && ((token & QTD_STS_MMF) != 0 || QTD_CERR(token) == 0) - && (!ehci_is_ARC(ehci) + && (!ehci_is_TDI(ehci) || urb->dev->tt->hub != ehci_to_hcd(ehci)->self.root_hub)) { #ifdef DEBUG @@ -714,10 +714,10 @@ qh_make ( info2 |= (EHCI_TUNE_MULT_TT << 30); info2 |= urb->dev->ttport << 23; - /* set the address of the TT; for ARC's integrated + /* set the address of the TT; for TDI's integrated * root hub tt, leave it zeroed. */ - if (!ehci_is_ARC(ehci) + if (!ehci_is_TDI(ehci) || urb->dev->tt->hub != ehci_to_hcd(ehci)->self.root_hub) info2 |= urb->dev->tt->hub->devnum << 16; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 3cdfb95c3800..9cbde9568218 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -650,6 +650,7 @@ iso_stream_alloc (int mem_flags) static void iso_stream_init ( + struct ehci_hcd *ehci, struct ehci_iso_stream *stream, struct usb_device *dev, int pipe, @@ -701,7 +702,10 @@ iso_stream_init ( u32 addr; addr = dev->ttport << 24; - addr |= dev->tt->hub->devnum << 16; + if (!ehci_is_TDI(ehci) + || (dev->tt->hub != + ehci_to_hcd(ehci)->self.root_hub)) + addr |= dev->tt->hub->devnum << 16; addr |= epnum << 8; addr |= dev->devnum; stream->usecs = HS_USECS_ISO (maxp); @@ -819,7 +823,7 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) /* dev->ep owns the initial refcount */ ep->hcpriv = stream; stream->ep = ep; - iso_stream_init(stream, urb->dev, urb->pipe, + iso_stream_init(ehci, stream, urb->dev, urb->pipe, urb->interval); } diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index e28d19724f56..671fe3810a1a 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -82,7 +82,7 @@ struct ehci_hcd { /* one per controller */ unsigned long next_statechange; u32 command; - unsigned is_arc_rh_tt:1; /* ARC roothub with TT */ + unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */ /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; @@ -599,13 +599,13 @@ struct ehci_fstn { * needed (mostly in root hub code). */ -#define ehci_is_ARC(e) ((e)->is_arc_rh_tt) +#define ehci_is_TDI(e) ((e)->is_tdi_rh_tt) /* Returns the speed of a device attached to a port on the root hub. */ static inline unsigned int ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) { - if (ehci_is_ARC(ehci)) { + if (ehci_is_TDI(ehci)) { switch ((portsc>>26)&3) { case 0: return 0; @@ -621,7 +621,7 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) #else -#define ehci_is_ARC(e) (0) +#define ehci_is_TDI(e) (0) #define ehci_port_speed(ehci, portsc) (1< Date: Mon, 7 Mar 2005 08:53:47 -0800 Subject: [PATCH] USB: cache the product, manufacturer, and serial number strings at device insertion. This should fix a lot of issues with broken devices that can't handle retrieving strings while they are doing something else (strings would be fetched from usbfs and sysfs entries.) Based on a patch that has been in the SuSE kernel tree for a long time from Olaf Hering Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devices.c | 24 ++++++------------------ drivers/usb/core/hub.c | 42 ++++++++++++++++++++++++------------------ drivers/usb/core/sysfs.c | 16 ++++++++-------- drivers/usb/core/usb.c | 5 ++++- include/linux/usb.h | 3 +++ 5 files changed, 45 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index a6961efc2cf0..92970d1cd589 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -362,33 +362,21 @@ static char *usb_dump_device_descriptor(char *start, char *end, const struct usb */ static char *usb_dump_device_strings (char *start, char *end, struct usb_device *dev) { - char *buf; - if (start > end) return start; - buf = kmalloc(128, GFP_KERNEL); - if (!buf) - return start; - if (dev->descriptor.iManufacturer) { - if (usb_string(dev, dev->descriptor.iManufacturer, buf, 128) > 0) - start += sprintf(start, format_string_manufacturer, buf); - } + if (dev->manufacturer) + start += sprintf(start, format_string_manufacturer, dev->manufacturer); if (start > end) goto out; - if (dev->descriptor.iProduct) { - if (usb_string(dev, dev->descriptor.iProduct, buf, 128) > 0) - start += sprintf(start, format_string_product, buf); - } + if (dev->product) + start += sprintf(start, format_string_product, dev->product); if (start > end) goto out; #ifdef ALLOW_SERIAL_NUMBER - if (dev->descriptor.iSerialNumber) { - if (usb_string(dev, dev->descriptor.iSerialNumber, buf, 128) > 0) - start += sprintf(start, format_string_serialnumber, buf); - } + if (dev->serial) + start += sprintf(start, format_string_serialnumber, dev->serial); #endif out: - kfree(buf); return start; } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c9cad3855744..16348da4e621 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1102,23 +1102,31 @@ static int choose_configuration(struct usb_device *udev) } #ifdef DEBUG -static void show_string(struct usb_device *udev, char *id, int index) +static void show_string(struct usb_device *udev, char *id, char *string) +{ + if (!string) + return; + dev_printk(KERN_INFO, &udev->dev, "%s: %s\n", id, string); +} + +#else +static inline void show_string(struct usb_device *udev, char *id, int index) +{} +#endif + +static void get_string(struct usb_device *udev, char **string, int index) { char *buf; if (!index) return; - if (!(buf = kmalloc(256, GFP_KERNEL))) + buf = kmalloc(256, GFP_KERNEL); + if (!buf) return; if (usb_string(udev, index, buf, 256) > 0) - dev_printk(KERN_INFO, &udev->dev, "%s: %s\n", id, buf); - kfree(buf); + *string = buf; } -#else -static inline void show_string(struct usb_device *udev, char *id, int index) -{} -#endif #ifdef CONFIG_USB_OTG #include "otg_whitelist.h" @@ -1156,22 +1164,20 @@ int usb_new_device(struct usb_device *udev) goto fail; } + /* read the standard strings and cache them if present */ + get_string(udev, &udev->product, udev->descriptor.iProduct); + get_string(udev, &udev->manufacturer, udev->descriptor.iManufacturer); + get_string(udev, &udev->serial, udev->descriptor.iSerialNumber); + /* Tell the world! */ dev_dbg(&udev->dev, "new device strings: Mfr=%d, Product=%d, " "SerialNumber=%d\n", udev->descriptor.iManufacturer, udev->descriptor.iProduct, udev->descriptor.iSerialNumber); - - if (udev->descriptor.iProduct) - show_string(udev, "Product", - udev->descriptor.iProduct); - if (udev->descriptor.iManufacturer) - show_string(udev, "Manufacturer", - udev->descriptor.iManufacturer); - if (udev->descriptor.iSerialNumber) - show_string(udev, "SerialNumber", - udev->descriptor.iSerialNumber); + show_string(udev, "Product", udev->product); + show_string(udev, "Manufacturer", udev->manufacturer); + show_string(udev, "SerialNumber", udev->serial); #ifdef CONFIG_USB_OTG /* diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index ce71f7af7478..c41171b1f97c 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -89,14 +89,14 @@ static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR, show_bConfigurationValue, set_bConfigurationValue); /* String fields */ -#define usb_string_attr(name, field) \ +#define usb_string_attr(name) \ static ssize_t show_##name(struct device *dev, char *buf) \ { \ struct usb_device *udev; \ int len; \ \ udev = to_usb_device (dev); \ - len = usb_string(udev, udev->descriptor.field, buf, PAGE_SIZE); \ + len = snprintf(buf, 256, "%s", udev->name); \ if (len < 0) \ return 0; \ buf[len] = '\n'; \ @@ -105,9 +105,9 @@ static ssize_t show_##name(struct device *dev, char *buf) \ } \ static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); -usb_string_attr(product, iProduct); -usb_string_attr(manufacturer, iManufacturer); -usb_string_attr(serial, iSerialNumber); +usb_string_attr(product); +usb_string_attr(manufacturer); +usb_string_attr(serial); static ssize_t show_speed (struct device *dev, char *buf) @@ -230,11 +230,11 @@ void usb_create_sysfs_dev_files (struct usb_device *udev) sysfs_create_group(&dev->kobj, &dev_attr_grp); - if (udev->descriptor.iManufacturer) + if (udev->manufacturer) device_create_file (dev, &dev_attr_manufacturer); - if (udev->descriptor.iProduct) + if (udev->product) device_create_file (dev, &dev_attr_product); - if (udev->descriptor.iSerialNumber) + if (udev->serial) device_create_file (dev, &dev_attr_serial); device_create_file (dev, &dev_attr_configuration); } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 99268c2e5164..9fd8b495427f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -647,7 +647,10 @@ static void usb_release_dev(struct device *dev) usb_destroy_configuration(udev); usb_bus_put(udev->bus); - kfree (udev); + kfree(udev->product); + kfree(udev->manufacturer); + kfree(udev->serial); + kfree(udev); } /** diff --git a/include/linux/usb.h b/include/linux/usb.h index 837fadde8d3b..44d1d292c76f 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -342,6 +342,9 @@ struct usb_device { int have_langid; /* whether string_langid is valid yet */ int string_langid; /* language ID for strings */ + char *product; + char *manufacturer; + char *serial; /* static strings from the device */ struct list_head filelist; struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */ -- cgit v1.2.3 From 4ed5e73976cfa2755ee7a972e54fbc2fc610fc44 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 7 Mar 2005 08:54:56 -0800 Subject: [PATCH] USB: make iInterface string cached Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 9 +++++++++ drivers/usb/core/sysfs.c | 39 ++++++++++++++++++--------------------- include/linux/usb.h | 1 + 3 files changed, 28 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 17a382deaea9..f6b2c309e55d 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -988,6 +988,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) dev_dbg (&dev->dev, "unregistering interface %s\n", interface->dev.bus_id); usb_remove_sysfs_intf_files(interface); + kfree(interface->cur_altsetting->string); + interface->cur_altsetting->string = NULL; device_del (&interface->dev); } @@ -1434,6 +1436,13 @@ free_interfaces: ret); continue; } + if ((intf->cur_altsetting->desc.iInterface) && + (intf->cur_altsetting->string == NULL)) { + intf->cur_altsetting->string = kmalloc(256, GFP_KERNEL); + if (intf->cur_altsetting->string) + usb_string(dev, intf->cur_altsetting->desc.iInterface, + intf->cur_altsetting->string, 256); + } usb_create_sysfs_intf_files (intf); } } diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index c41171b1f97c..3f5ae2de70e2 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -272,25 +272,22 @@ usb_intf_attr (bInterfaceClass, "%02x\n") usb_intf_attr (bInterfaceSubClass, "%02x\n") usb_intf_attr (bInterfaceProtocol, "%02x\n") -#define usb_intf_str(name, field) \ -static ssize_t show_##name(struct device *dev, char *buf) \ -{ \ - struct usb_interface *intf; \ - struct usb_device *udev; \ - int len; \ - \ - intf = to_usb_interface (dev); \ - udev = interface_to_usbdev (intf); \ - len = usb_string(udev, intf->cur_altsetting->desc.field, buf, PAGE_SIZE);\ - if (len < 0) \ - return 0; \ - buf[len] = '\n'; \ - buf[len+1] = 0; \ - return len+1; \ -} \ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); - -usb_intf_str (interface, iInterface); +static ssize_t show_interface_string(struct device *dev, char *buf) +{ + struct usb_interface *intf; + struct usb_device *udev; + int len; + + intf = to_usb_interface (dev); + udev = interface_to_usbdev (intf); + len = snprintf(buf, 256, "%s", intf->cur_altsetting->string); + if (len < 0) + return 0; + buf[len] = '\n'; + buf[len+1] = 0; + return len+1; +} +static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL); static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceNumber.attr, @@ -309,7 +306,7 @@ void usb_create_sysfs_intf_files (struct usb_interface *intf) { sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); - if (intf->cur_altsetting->desc.iInterface) + if (intf->cur_altsetting->string) device_create_file(&intf->dev, &dev_attr_interface); } @@ -318,7 +315,7 @@ void usb_remove_sysfs_intf_files (struct usb_interface *intf) { sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); - if (intf->cur_altsetting->desc.iInterface) + if (intf->cur_altsetting->string) device_remove_file(&intf->dev, &dev_attr_interface); } diff --git a/include/linux/usb.h b/include/linux/usb.h index 44d1d292c76f..f5ec1f5d8920 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -70,6 +70,7 @@ struct usb_host_interface { */ struct usb_host_endpoint *endpoint; + char *string; /* iInterface string, if present */ unsigned char *extra; /* Extra descriptors */ int extralen; }; -- cgit v1.2.3 From b3bda4b9e513aef5e580c19f98a528260c41c036 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 7 Mar 2005 08:55:16 -0800 Subject: [PATCH] USB: cache the iConfiguration string, if present. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 3 +++ drivers/usb/core/message.c | 7 +++++++ drivers/usb/core/sysfs.c | 37 +++++++++++++++++-------------------- include/linux/usb.h | 3 +++ 4 files changed, 30 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 42e9f8466272..837bad132449 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -420,6 +420,9 @@ void usb_destroy_configuration(struct usb_device *dev) for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { struct usb_host_config *cf = &dev->config[c]; + if (cf->string) + kfree(cf->string); + for (i = 0; i < cf->desc.bNumInterfaces; i++) { if (cf->intf_cache[i]) kref_put(&cf->intf_cache[i]->ref, diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index f6b2c309e55d..b4624501cd58 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1413,6 +1413,13 @@ free_interfaces: } kfree(new_interfaces); + if ((cp->desc.iConfiguration) && + (cp->string == NULL)) { + cp->string = kmalloc(256, GFP_KERNEL); + if (cp->string) + usb_string(dev, cp->desc.iConfiguration, cp->string, 256); + } + /* Now that all the interfaces are set up, register them * to trigger binding of drivers to interfaces. probe() * routines may install different altsettings and may diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 3f5ae2de70e2..ec9b3bde8ae5 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -46,27 +46,24 @@ usb_actconfig_attr (bNumInterfaces, 1, "%2d\n") usb_actconfig_attr (bmAttributes, 1, "%2x\n") usb_actconfig_attr (bMaxPower, 2, "%3dmA\n") -#define usb_actconfig_str(name, field) \ -static ssize_t show_##name(struct device *dev, char *buf) \ -{ \ - struct usb_device *udev; \ - struct usb_host_config *actconfig; \ - int len; \ - \ - udev = to_usb_device (dev); \ - actconfig = udev->actconfig; \ - if (!actconfig) \ - return 0; \ - len = usb_string(udev, actconfig->desc.field, buf, PAGE_SIZE); \ - if (len < 0) \ - return 0; \ - buf[len] = '\n'; \ - buf[len+1] = 0; \ - return len+1; \ -} \ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); +static ssize_t show_configuration_string(struct device *dev, char *buf) +{ + struct usb_device *udev; + struct usb_host_config *actconfig; + int len; -usb_actconfig_str (configuration, iConfiguration) + udev = to_usb_device (dev); + actconfig = udev->actconfig; + if ((!actconfig) || (!actconfig->string)) + return 0; + len = sprintf(buf, actconfig->string, PAGE_SIZE); + if (len < 0) + return 0; + buf[len] = '\n'; + buf[len+1] = 0; + return len+1; +} +static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL); /* configuration value is always present, and r/w */ usb_actconfig_show(bConfigurationValue, 1, "%u\n"); diff --git a/include/linux/usb.h b/include/linux/usb.h index f5ec1f5d8920..8d2687ae39ff 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -190,6 +190,8 @@ struct usb_interface_cache { /** * struct usb_host_config - representation of a device's configuration * @desc: the device's configuration descriptor. + * @string: pointer to the cached version of the iConfiguration string, if + * present for this configuration. * @interface: array of pointers to usb_interface structures, one for each * interface in the configuration. The number of interfaces is stored * in desc.bNumInterfaces. These pointers are valid only while the @@ -226,6 +228,7 @@ struct usb_interface_cache { struct usb_host_config { struct usb_config_descriptor desc; + char *string; /* the interfaces associated with this configuration, * stored in no particular order */ struct usb_interface *interface[USB_MAXINTERFACES]; -- cgit v1.2.3 From 40f1626986f711eb7d9b281a6f374b575aa4221c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 7 Mar 2005 09:10:37 -0800 Subject: [PATCH] USB: compat ioctl for submiting URB From: Christopher Li - Let usbdevfs directly handle 32 bit URB ioctl. More specifically: USBDEVFS_SUBMITURB32, USBDEVFS_REAPURB32 and USBDEVFS_REAPURBNDELAY32. Those asynchronous ioctls are too complicate to handle by the compatible layer. Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 251 +++++++++++++++++++++++++++++++------------ fs/compat_ioctl.c | 229 +-------------------------------------- include/linux/compat_ioctl.h | 3 + include/linux/usbdevice_fs.h | 23 +++- 4 files changed, 214 insertions(+), 292 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 547c485e0876..a047bc392983 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -813,9 +813,11 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg) return status; } -static int proc_submiturb(struct dev_state *ps, void __user *arg) + +static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, + struct usbdevfs_iso_packet_desc __user *iso_frame_desc, + void __user *arg) { - struct usbdevfs_urb uurb; struct usbdevfs_iso_packet_desc *isopkt = NULL; struct usb_host_endpoint *ep; struct async *as; @@ -823,42 +825,40 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) unsigned int u, totlen, isofrmlen; int ret, interval = 0, ifnum = -1; - if (copy_from_user(&uurb, arg, sizeof(uurb))) - return -EFAULT; - if (uurb.flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK| + if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK| URB_NO_FSBR|URB_ZERO_PACKET)) return -EINVAL; - if (!uurb.buffer) + if (!uurb->buffer) return -EINVAL; - if (uurb.signr != 0 && (uurb.signr < SIGRTMIN || uurb.signr > SIGRTMAX)) + if (uurb->signr != 0 && (uurb->signr < SIGRTMIN || uurb->signr > SIGRTMAX)) return -EINVAL; - if (!(uurb.type == USBDEVFS_URB_TYPE_CONTROL && (uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { - if ((ifnum = findintfep(ps->dev, uurb.endpoint)) < 0) + if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { + if ((ifnum = findintfep(ps->dev, uurb->endpoint)) < 0) return ifnum; if ((ret = checkintf(ps, ifnum))) return ret; } - if ((uurb.endpoint & USB_ENDPOINT_DIR_MASK) != 0) - ep = ps->dev->ep_in [uurb.endpoint & USB_ENDPOINT_NUMBER_MASK]; + if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) + ep = ps->dev->ep_in [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; else - ep = ps->dev->ep_out [uurb.endpoint & USB_ENDPOINT_NUMBER_MASK]; + ep = ps->dev->ep_out [uurb->endpoint & USB_ENDPOINT_NUMBER_MASK]; if (!ep) return -ENOENT; - switch(uurb.type) { + switch(uurb->type) { case USBDEVFS_URB_TYPE_CONTROL: if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_CONTROL) return -EINVAL; /* min 8 byte setup packet, max arbitrary */ - if (uurb.buffer_length < 8 || uurb.buffer_length > PAGE_SIZE) + if (uurb->buffer_length < 8 || uurb->buffer_length > PAGE_SIZE) return -EINVAL; if (!(dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL))) return -ENOMEM; - if (copy_from_user(dr, uurb.buffer, 8)) { + if (copy_from_user(dr, uurb->buffer, 8)) { kfree(dr); return -EFAULT; } - if (uurb.buffer_length < (le16_to_cpup(&dr->wLength) + 8)) { + if (uurb->buffer_length < (le16_to_cpup(&dr->wLength) + 8)) { kfree(dr); return -EINVAL; } @@ -866,11 +866,11 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) kfree(dr); return ret; } - uurb.endpoint = (uurb.endpoint & ~USB_ENDPOINT_DIR_MASK) | (dr->bRequestType & USB_ENDPOINT_DIR_MASK); - uurb.number_of_packets = 0; - uurb.buffer_length = le16_to_cpup(&dr->wLength); - uurb.buffer += 8; - if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length)) { + uurb->endpoint = (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) | (dr->bRequestType & USB_ENDPOINT_DIR_MASK); + uurb->number_of_packets = 0; + uurb->buffer_length = le16_to_cpup(&dr->wLength); + uurb->buffer += 8; + if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) { kfree(dr); return -EFAULT; } @@ -883,29 +883,29 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) return -EINVAL; /* allow single-shot interrupt transfers, at bogus rates */ } - uurb.number_of_packets = 0; - if (uurb.buffer_length > MAX_USBFS_BUFFER_SIZE) + uurb->number_of_packets = 0; + if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; - if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length)) + if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) return -EFAULT; break; case USBDEVFS_URB_TYPE_ISO: /* arbitrary limit */ - if (uurb.number_of_packets < 1 || uurb.number_of_packets > 128) + if (uurb->number_of_packets < 1 || uurb->number_of_packets > 128) return -EINVAL; if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) return -EINVAL; interval = 1 << min (15, ep->desc.bInterval - 1); - isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb.number_of_packets; + isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb->number_of_packets; if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL))) return -ENOMEM; - if (copy_from_user(isopkt, &((struct usbdevfs_urb __user *)arg)->iso_frame_desc, isofrmlen)) { + if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) { kfree(isopkt); return -EFAULT; } - for (totlen = u = 0; u < uurb.number_of_packets; u++) { + for (totlen = u = 0; u < uurb->number_of_packets; u++) { if (isopkt[u].length > 1023) { kfree(isopkt); return -EINVAL; @@ -916,11 +916,11 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) kfree(isopkt); return -EINVAL; } - uurb.buffer_length = totlen; + uurb->buffer_length = totlen; break; case USBDEVFS_URB_TYPE_INTERRUPT: - uurb.number_of_packets = 0; + uurb->number_of_packets = 0; if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) return -EINVAL; @@ -928,23 +928,23 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) interval = 1 << min (15, ep->desc.bInterval - 1); else interval = ep->desc.bInterval; - if (uurb.buffer_length > MAX_USBFS_BUFFER_SIZE) + if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; - if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length)) + if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) return -EFAULT; break; default: return -EINVAL; } - if (!(as = alloc_async(uurb.number_of_packets))) { + if (!(as = alloc_async(uurb->number_of_packets))) { if (isopkt) kfree(isopkt); if (dr) kfree(dr); return -ENOMEM; } - if (!(as->urb->transfer_buffer = kmalloc(uurb.buffer_length, GFP_KERNEL))) { + if (!(as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL))) { if (isopkt) kfree(isopkt); if (dr) @@ -953,16 +953,16 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) return -ENOMEM; } as->urb->dev = ps->dev; - as->urb->pipe = (uurb.type << 30) | __create_pipe(ps->dev, uurb.endpoint & 0xf) | (uurb.endpoint & USB_DIR_IN); - as->urb->transfer_flags = uurb.flags; - as->urb->transfer_buffer_length = uurb.buffer_length; + as->urb->pipe = (uurb->type << 30) | __create_pipe(ps->dev, uurb->endpoint & 0xf) | (uurb->endpoint & USB_DIR_IN); + as->urb->transfer_flags = uurb->flags; + as->urb->transfer_buffer_length = uurb->buffer_length; as->urb->setup_packet = (unsigned char*)dr; - as->urb->start_frame = uurb.start_frame; - as->urb->number_of_packets = uurb.number_of_packets; + as->urb->start_frame = uurb->start_frame; + as->urb->number_of_packets = uurb->number_of_packets; as->urb->interval = interval; as->urb->context = as; as->urb->complete = async_completed; - for (totlen = u = 0; u < uurb.number_of_packets; u++) { + for (totlen = u = 0; u < uurb->number_of_packets; u++) { as->urb->iso_frame_desc[u].offset = totlen; as->urb->iso_frame_desc[u].length = isopkt[u].length; totlen += isopkt[u].length; @@ -971,15 +971,15 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) kfree(isopkt); as->ps = ps; as->userurb = arg; - if (uurb.endpoint & USB_DIR_IN) - as->userbuffer = uurb.buffer; + if (uurb->endpoint & USB_DIR_IN) + as->userbuffer = uurb->buffer; else as->userbuffer = NULL; - as->signr = uurb.signr; + as->signr = uurb->signr; as->ifnum = ifnum; as->task = current; - if (!(uurb.endpoint & USB_DIR_IN)) { - if (copy_from_user(as->urb->transfer_buffer, uurb.buffer, as->urb->transfer_buffer_length)) { + if (!(uurb->endpoint & USB_DIR_IN)) { + if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, as->urb->transfer_buffer_length)) { free_async(as); return -EFAULT; } @@ -994,6 +994,16 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg) return 0; } +static int proc_submiturb(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_urb uurb; + + if (copy_from_user(&uurb, arg, sizeof(uurb))) + return -EFAULT; + + return proc_do_submiturb(ps, &uurb, (((struct usbdevfs_urb __user *)arg)->iso_frame_desc), arg); +} + static int proc_unlinkurb(struct dev_state *ps, void __user *arg) { struct async *as; @@ -1005,10 +1015,11 @@ static int proc_unlinkurb(struct dev_state *ps, void __user *arg) return 0; } -static int processcompl(struct async *as) +static int processcompl(struct async *as, void __user * __user *arg) { struct urb *urb = as->urb; struct usbdevfs_urb __user *userurb = as->userurb; + void __user *addr = as->userurb; unsigned int i; if (as->userbuffer) @@ -1031,16 +1042,19 @@ static int processcompl(struct async *as) &userurb->iso_frame_desc[i].status)) return -EFAULT; } + + free_async(as); + + if (put_user(addr, (void __user * __user *)arg)) + return -EFAULT; return 0; } -static int proc_reapurb(struct dev_state *ps, void __user *arg) +static struct async* reap_as(struct dev_state *ps) { DECLARE_WAITQUEUE(wait, current); struct async *as = NULL; - void __user *addr; struct usb_device *dev = ps->dev; - int ret; add_wait_queue(&ps->wait, &wait); for (;;) { @@ -1055,16 +1069,14 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg) } remove_wait_queue(&ps->wait, &wait); set_current_state(TASK_RUNNING); - if (as) { - ret = processcompl(as); - addr = as->userurb; - free_async(as); - if (ret) - return ret; - if (put_user(addr, (void __user * __user *)arg)) - return -EFAULT; - return 0; - } + return as; +} + +static int proc_reapurb(struct dev_state *ps, void __user *arg) +{ + struct async *as = reap_as(ps); + if (as) + return processcompl(as, (void __user * __user *)arg); if (signal_pending(current)) return -EINTR; return -EIO; @@ -1073,21 +1085,107 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg) static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg) { struct async *as; - void __user *addr; - int ret; if (!(as = async_getcompleted(ps))) return -EAGAIN; - ret = processcompl(as); - addr = as->userurb; + return processcompl(as, (void __user * __user *)arg); +} + +#ifdef CONFIG_COMPAT + +static int get_urb32(struct usbdevfs_urb *kurb, + struct usbdevfs_urb32 __user *uurb) +{ + __u32 uptr; + if (get_user(kurb->type, &uurb->type) || + __get_user(kurb->endpoint, &uurb->endpoint) || + __get_user(kurb->status, &uurb->status) || + __get_user(kurb->flags, &uurb->flags) || + __get_user(kurb->buffer_length, &uurb->buffer_length) || + __get_user(kurb->actual_length, &uurb->actual_length) || + __get_user(kurb->start_frame, &uurb->start_frame) || + __get_user(kurb->number_of_packets, &uurb->number_of_packets) || + __get_user(kurb->error_count, &uurb->error_count) || + __get_user(kurb->signr, &uurb->signr)) + return -EFAULT; + + if (__get_user(uptr, &uurb->buffer)) + return -EFAULT; + kurb->buffer = compat_ptr(uptr); + if (__get_user(uptr, &uurb->buffer)) + return -EFAULT; + kurb->usercontext = compat_ptr(uptr); + + return 0; +} + +static int proc_submiturb_compat(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_urb uurb; + + if (get_urb32(&uurb,(struct usbdevfs_urb32 *)arg)) + return -EFAULT; + + return proc_do_submiturb(ps, &uurb, ((struct usbdevfs_urb __user *)arg)->iso_frame_desc, arg); +} + +static int processcompl_compat(struct async *as, void __user * __user *arg) +{ + struct urb *urb = as->urb; + struct usbdevfs_urb32 __user *userurb = as->userurb; + void __user *addr = as->userurb; + unsigned int i; + + if (as->userbuffer) + if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length)) + return -EFAULT; + if (put_user(urb->status, &userurb->status)) + return -EFAULT; + if (put_user(urb->actual_length, &userurb->actual_length)) + return -EFAULT; + if (put_user(urb->error_count, &userurb->error_count)) + return -EFAULT; + + if (!(usb_pipeisoc(urb->pipe))) + return 0; + for (i = 0; i < urb->number_of_packets; i++) { + if (put_user(urb->iso_frame_desc[i].actual_length, + &userurb->iso_frame_desc[i].actual_length)) + return -EFAULT; + if (put_user(urb->iso_frame_desc[i].status, + &userurb->iso_frame_desc[i].status)) + return -EFAULT; + } + free_async(as); - if (ret) - return ret; - if (put_user(addr, (void __user * __user *)arg)) + if (put_user((u32)(u64)addr, (u32 __user *)arg)) return -EFAULT; return 0; } +static int proc_reapurb_compat(struct dev_state *ps, void __user *arg) +{ + struct async *as = reap_as(ps); + if (as) + return processcompl_compat(as, (void __user * __user *)arg); + if (signal_pending(current)) + return -EINTR; + return -EIO; +} + +static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg) +{ + struct async *as; + + printk("reapurbnblock\n"); + if (!(as = async_getcompleted(ps))) + return -EAGAIN; + printk("reap got as %p\n", as); + return processcompl_compat(as, (void __user * __user *)arg); +} + +#endif + static int proc_disconnectsignal(struct dev_state *ps, void __user *arg) { struct usbdevfs_disconnectsignal ds; @@ -1299,6 +1397,27 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd inode->i_mtime = CURRENT_TIME; break; +#ifdef CONFIG_COMPAT + + case USBDEVFS_SUBMITURB32: + snoop(&dev->dev, "%s: SUBMITURB32\n", __FUNCTION__); + ret = proc_submiturb_compat(ps, p); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_REAPURB32: + snoop(&dev->dev, "%s: REAPURB32\n", __FUNCTION__); + ret = proc_reapurb_compat(ps, p); + break; + + case USBDEVFS_REAPURBNDELAY32: + snoop(&dev->dev, "%s: REAPURBDELAY32\n", __FUNCTION__); + ret = proc_reapurbnonblock_compat(ps, p); + break; + +#endif + case USBDEVFS_DISCARDURB: snoop(&dev->dev, "%s: DISCARDURB\n", __FUNCTION__); ret = proc_unlinkurb(ps, p); diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index a14dc212e301..db23d05df174 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -2575,229 +2575,11 @@ static int do_usbdevfs_bulk(unsigned int fd, unsigned int cmd, unsigned long arg return sys_ioctl(fd, USBDEVFS_BULK, (unsigned long)p); } -/* This needs more work before we can enable it. Unfortunately - * because of the fancy asynchronous way URB status/error is written - * back to userspace, we'll need to fiddle with USB devio internals - * and/or reimplement entirely the frontend of it ourselves. -DaveM - * - * The issue is: - * - * When an URB is submitted via usbdevicefs it is put onto an - * asynchronous queue. When the URB completes, it may be reaped - * via another ioctl. During this reaping the status is written - * back to userspace along with the length of the transfer. - * - * We must translate into 64-bit kernel types so we pass in a kernel - * space copy of the usbdevfs_urb structure. This would mean that we - * must do something to deal with the async entry reaping. First we - * have to deal somehow with this transitory memory we've allocated. - * This is problematic since there are many call sites from which the - * async entries can be destroyed (and thus when we'd need to free up - * this kernel memory). One of which is the close() op of usbdevicefs. - * To handle that we'd need to make our own file_operations struct which - * overrides usbdevicefs's release op with our own which runs usbdevicefs's - * real release op then frees up the kernel memory. - * - * But how to keep track of these kernel buffers? We'd need to either - * keep track of them in some table _or_ know about usbdevicefs internals - * (ie. the exact layout of its file private, which is actually defined - * in linux/usbdevice_fs.h, the layout of the async queues are private to - * devio.c) - * - * There is one possible other solution I considered, also involving knowledge - * of usbdevicefs internals: - * - * After an URB is submitted, we "fix up" the address back to the user - * space one. This would work if the status/length fields written back - * by the async URB completion lines up perfectly in the 32-bit type with - * the 64-bit kernel type. Unfortunately, it does not because the iso - * frame descriptors, at the end of the struct, can be written back. - * - * I think we'll just need to simply duplicate the devio URB engine here. - */ -#if 0 -struct usbdevfs_urb32 { - unsigned char type; - unsigned char endpoint; - compat_int_t status; - compat_uint_t flags; - compat_caddr_t buffer; - compat_int_t buffer_length; - compat_int_t actual_length; - compat_int_t start_frame; - compat_int_t number_of_packets; - compat_int_t error_count; - compat_uint_t signr; - compat_caddr_t usercontext; /* unused */ - struct usbdevfs_iso_packet_desc iso_frame_desc[0]; -}; - -#define USBDEVFS_SUBMITURB32 _IOR('U', 10, struct usbdevfs_urb32) - -static int get_urb32(struct usbdevfs_urb *kurb, - struct usbdevfs_urb32 *uurb) -{ - if (get_user(kurb->type, &uurb->type) || - __get_user(kurb->endpoint, &uurb->endpoint) || - __get_user(kurb->status, &uurb->status) || - __get_user(kurb->flags, &uurb->flags) || - __get_user(kurb->buffer_length, &uurb->buffer_length) || - __get_user(kurb->actual_length, &uurb->actual_length) || - __get_user(kurb->start_frame, &uurb->start_frame) || - __get_user(kurb->number_of_packets, &uurb->number_of_packets) || - __get_user(kurb->error_count, &uurb->error_count) || - __get_user(kurb->signr, &uurb->signr)) - return -EFAULT; - - kurb->usercontext = 0; /* unused currently */ - - return 0; -} - -/* Just put back the values which usbdevfs actually changes. */ -static int put_urb32(struct usbdevfs_urb *kurb, - struct usbdevfs_urb32 *uurb) -{ - if (put_user(kurb->status, &uurb->status) || - __put_user(kurb->actual_length, &uurb->actual_length) || - __put_user(kurb->error_count, &uurb->error_count)) - return -EFAULT; - - if (kurb->number_of_packets != 0) { - int i; - - for (i = 0; i < kurb->number_of_packets; i++) { - if (__put_user(kurb->iso_frame_desc[i].actual_length, - &uurb->iso_frame_desc[i].actual_length) || - __put_user(kurb->iso_frame_desc[i].status, - &uurb->iso_frame_desc[i].status)) - return -EFAULT; - } - } - - return 0; -} - -static int get_urb32_isoframes(struct usbdevfs_urb *kurb, - struct usbdevfs_urb32 *uurb) -{ - unsigned int totlen; - int i; - - if (kurb->type != USBDEVFS_URB_TYPE_ISO) { - kurb->number_of_packets = 0; - return 0; - } - - if (kurb->number_of_packets < 1 || - kurb->number_of_packets > 128) - return -EINVAL; - - if (copy_from_user(&kurb->iso_frame_desc[0], - &uurb->iso_frame_desc[0], - sizeof(struct usbdevfs_iso_packet_desc) * - kurb->number_of_packets)) - return -EFAULT; - - totlen = 0; - for (i = 0; i < kurb->number_of_packets; i++) { - unsigned int this_len; - - this_len = kurb->iso_frame_desc[i].length; - if (this_len > 1023) - return -EINVAL; - - totlen += this_len; - } - - if (totlen > 32768) - return -EINVAL; - - kurb->buffer_length = totlen; - - return 0; -} - -static int do_usbdevfs_urb(unsigned int fd, unsigned int cmd, unsigned long arg) -{ - struct usbdevfs_urb *kurb; - struct usbdevfs_urb32 *uurb; - mm_segment_t old_fs; - __u32 udata; - void *uptr, *kptr; - unsigned int buflen; - int err; - - uurb = compat_ptr(arg); - - err = -ENOMEM; - kurb = kmalloc(sizeof(struct usbdevfs_urb) + - (sizeof(struct usbdevfs_iso_packet_desc) * 128), - GFP_KERNEL); - if (!kurb) - goto out; - - err = -EFAULT; - if (get_urb32(kurb, uurb)) - goto out; - - err = get_urb32_isoframes(kurb, uurb); - if (err) - goto out; - - err = -EFAULT; - if (__get_user(udata, &uurb->buffer)) - goto out; - uptr = compat_ptr(udata); - - buflen = kurb->buffer_length; - err = verify_area(VERIFY_WRITE, uptr, buflen); - if (err) - goto out; - - - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_ioctl(fd, USBDEVFS_SUBMITURB, (unsigned long) kurb); - set_fs(old_fs); - if (err >= 0) { - /* RED-PEN Shit, this doesn't work for async URBs :-( XXX */ - if (put_urb32(kurb, uurb)) { - err = -EFAULT; - } - } - -out: - kfree(kurb); - return err; -} -#endif - -#define USBDEVFS_REAPURB32 _IOW('U', 12, u32) -#define USBDEVFS_REAPURBNDELAY32 _IOW('U', 13, u32) - -static int do_usbdevfs_reapurb(unsigned int fd, unsigned int cmd, unsigned long arg) -{ - mm_segment_t old_fs; - void *kptr; - int err; - - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = sys_ioctl(fd, - (cmd == USBDEVFS_REAPURB32 ? - USBDEVFS_REAPURB : - USBDEVFS_REAPURBNDELAY), - (unsigned long) &kptr); - set_fs(old_fs); - - if (err >= 0 && - put_user((u32)(u64)kptr, (u32 __user *)compat_ptr(arg))) - err = -EFAULT; - - return err; -} +/* + * USBDEVFS_SUBMITURB, USBDEVFS_REAPURB and USBDEVFS_REAPURBNDELAY + * are handled in usbdevfs core. -Christopher Li + */ struct usbdevfs_disconnectsignal32 { compat_int_t signr; @@ -3331,9 +3113,6 @@ HANDLE_IOCTL(TIOCSSERIAL, serial_struct_ioctl) /* Usbdevfs */ HANDLE_IOCTL(USBDEVFS_CONTROL32, do_usbdevfs_control) HANDLE_IOCTL(USBDEVFS_BULK32, do_usbdevfs_bulk) -/*HANDLE_IOCTL(USBDEVFS_SUBMITURB32, do_usbdevfs_urb)*/ -HANDLE_IOCTL(USBDEVFS_REAPURB32, do_usbdevfs_reapurb) -HANDLE_IOCTL(USBDEVFS_REAPURBNDELAY32, do_usbdevfs_reapurb) HANDLE_IOCTL(USBDEVFS_DISCSIGNAL32, do_usbdevfs_discsignal) /* i2c */ HANDLE_IOCTL(I2C_FUNCS, w_long) diff --git a/include/linux/compat_ioctl.h b/include/linux/compat_ioctl.h index 383275bf924e..19f15d807ba5 100644 --- a/include/linux/compat_ioctl.h +++ b/include/linux/compat_ioctl.h @@ -692,6 +692,9 @@ COMPATIBLE_IOCTL(USBDEVFS_RELEASEINTERFACE) COMPATIBLE_IOCTL(USBDEVFS_CONNECTINFO) COMPATIBLE_IOCTL(USBDEVFS_HUB_PORTINFO) COMPATIBLE_IOCTL(USBDEVFS_RESET) +COMPATIBLE_IOCTL(USBDEVFS_SUBMITURB32) +COMPATIBLE_IOCTL(USBDEVFS_REAPURB32) +COMPATIBLE_IOCTL(USBDEVFS_REAPURBNDELAY32) COMPATIBLE_IOCTL(USBDEVFS_CLEAR_HALT) /* MTD */ COMPATIBLE_IOCTL(MEMGETINFO) diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index aed8193eb420..fb57c2217468 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -32,6 +32,7 @@ #define _LINUX_USBDEVICE_FS_H #include +#include /* --------------------------------------------------------------------- */ @@ -123,6 +124,24 @@ struct usbdevfs_hub_portinfo { char port [127]; /* e.g. port 3 connects to device 27 */ }; +#ifdef CONFIG_COMPAT +struct usbdevfs_urb32 { + unsigned char type; + unsigned char endpoint; + compat_int_t status; + compat_uint_t flags; + compat_caddr_t buffer; + compat_int_t buffer_length; + compat_int_t actual_length; + compat_int_t start_frame; + compat_int_t number_of_packets; + compat_int_t error_count; + compat_uint_t signr; + compat_caddr_t usercontext; /* unused */ + struct usbdevfs_iso_packet_desc iso_frame_desc[0]; +}; +#endif + #define USBDEVFS_CONTROL _IOWR('U', 0, struct usbdevfs_ctrltransfer) #define USBDEVFS_BULK _IOWR('U', 2, struct usbdevfs_bulktransfer) #define USBDEVFS_RESETEP _IOR('U', 3, unsigned int) @@ -130,9 +149,12 @@ struct usbdevfs_hub_portinfo { #define USBDEVFS_SETCONFIGURATION _IOR('U', 5, unsigned int) #define USBDEVFS_GETDRIVER _IOW('U', 8, struct usbdevfs_getdriver) #define USBDEVFS_SUBMITURB _IOR('U', 10, struct usbdevfs_urb) +#define USBDEVFS_SUBMITURB32 _IOR('U', 10, struct usbdevfs_urb32) #define USBDEVFS_DISCARDURB _IO('U', 11) #define USBDEVFS_REAPURB _IOW('U', 12, void *) +#define USBDEVFS_REAPURB32 _IOW('U', 12, u32) #define USBDEVFS_REAPURBNDELAY _IOW('U', 13, void *) +#define USBDEVFS_REAPURBNDELAY32 _IOW('U', 13, u32) #define USBDEVFS_DISCSIGNAL _IOR('U', 14, struct usbdevfs_disconnectsignal) #define USBDEVFS_CLAIMINTERFACE _IOR('U', 15, unsigned int) #define USBDEVFS_RELEASEINTERFACE _IOR('U', 16, unsigned int) @@ -143,5 +165,4 @@ struct usbdevfs_hub_portinfo { #define USBDEVFS_CLEAR_HALT _IOR('U', 21, unsigned int) #define USBDEVFS_DISCONNECT _IO('U', 22) #define USBDEVFS_CONNECT _IO('U', 23) - #endif /* _LINUX_USBDEVICE_FS_H */ -- cgit v1.2.3 From d9795da7533d783a7bd2b9973ef864f0aa6cc7bd Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 8 Mar 2005 16:30:33 -0800 Subject: [PATCH] barrier rework updates As promised to Andrew, here are the latest bits that fixup the block io barrier handling. - Add io scheduler ->deactivate hook to tell the io scheduler is a request is suspended from the block layer. cfq and as needs this hook. - Locking updates - Make sure a driver doesn't reuse the flush rq before a previous one has completed - Typo in the scsi_io_completion() function, the bit shift was wrong - sd needs proper timeout on the flush - remove silly debug leftover in ide-disk wrt "hdc" Signed-off-by: Jens Axboe Signed-off-by: Linus Torvalds --- drivers/block/as-iosched.c | 30 ++++++++++++++++-------------- drivers/block/cfq-iosched.c | 14 ++++++++++---- drivers/block/elevator.c | 28 +++++++++++++++++++++++++++- drivers/block/ll_rw_blk.c | 21 ++++++++++----------- drivers/ide/ide-disk.c | 20 +++++++------------- drivers/scsi/scsi_lib.c | 2 +- drivers/scsi/sd.c | 18 +++++++++++++++--- include/linux/blkdev.h | 2 ++ include/linux/elevator.h | 3 +++ 9 files changed, 91 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/as-iosched.c b/drivers/block/as-iosched.c index fb625b49b831..17f02aa95271 100644 --- a/drivers/block/as-iosched.c +++ b/drivers/block/as-iosched.c @@ -1463,34 +1463,35 @@ static void as_add_request(struct as_data *ad, struct as_rq *arq) arq->state = AS_RQ_QUEUED; } -/* - * requeue the request. The request has not been completed, nor is it a - * new request, so don't touch accounting. - */ -static void as_requeue_request(request_queue_t *q, struct request *rq) +static void as_deactivate_request(request_queue_t *q, struct request *rq) { struct as_data *ad = q->elevator->elevator_data; struct as_rq *arq = RQ_DATA(rq); if (arq) { - if (arq->state != AS_RQ_REMOVED) { - printk("arq->state %d\n", arq->state); - WARN_ON(1); + if (arq->state == AS_RQ_REMOVED) { + arq->state = AS_RQ_DISPATCHED; + if (arq->io_context && arq->io_context->aic) + atomic_inc(&arq->io_context->aic->nr_dispatched); } - - arq->state = AS_RQ_DISPATCHED; - if (arq->io_context && arq->io_context->aic) - atomic_inc(&arq->io_context->aic->nr_dispatched); } else WARN_ON(blk_fs_request(rq) && (!(rq->flags & (REQ_HARDBARRIER|REQ_SOFTBARRIER))) ); - list_add(&rq->queuelist, ad->dispatch); - /* Stop anticipating - let this request get through */ as_antic_stop(ad); } +/* + * requeue the request. The request has not been completed, nor is it a + * new request, so don't touch accounting. + */ +static void as_requeue_request(request_queue_t *q, struct request *rq) +{ + as_deactivate_request(q, rq); + list_add(&rq->queuelist, &q->queue_head); +} + /* * Account a request that is inserted directly onto the dispatch queue. * arq->io_context->aic->nr_dispatched should not need to be incremented @@ -2080,6 +2081,7 @@ static struct elevator_type iosched_as = { .elevator_add_req_fn = as_insert_request, .elevator_remove_req_fn = as_remove_request, .elevator_requeue_req_fn = as_requeue_request, + .elevator_deactivate_req_fn = as_deactivate_request, .elevator_queue_empty_fn = as_queue_empty, .elevator_completed_req_fn = as_completed_request, .elevator_former_req_fn = as_former_request, diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c index 4234508565d6..d9aaa42500d0 100644 --- a/drivers/block/cfq-iosched.c +++ b/drivers/block/cfq-iosched.c @@ -607,10 +607,7 @@ out: return NULL; } -/* - * make sure the service time gets corrected on reissue of this request - */ -static void cfq_requeue_request(request_queue_t *q, struct request *rq) +static void cfq_deactivate_request(request_queue_t *q, struct request *rq) { struct cfq_rq *crq = RQ_DATA(rq); @@ -627,6 +624,14 @@ static void cfq_requeue_request(request_queue_t *q, struct request *rq) cfqq->cfqd->rq_in_driver--; } } +} + +/* + * make sure the service time gets corrected on reissue of this request + */ +static void cfq_requeue_request(request_queue_t *q, struct request *rq) +{ + cfq_deactivate_request(q, rq); list_add(&rq->queuelist, &q->queue_head); } @@ -1804,6 +1809,7 @@ static struct elevator_type iosched_cfq = { .elevator_add_req_fn = cfq_insert_request, .elevator_remove_req_fn = cfq_remove_request, .elevator_requeue_req_fn = cfq_requeue_request, + .elevator_deactivate_req_fn = cfq_deactivate_request, .elevator_queue_empty_fn = cfq_queue_empty, .elevator_completed_req_fn = cfq_completed_request, .elevator_former_req_fn = cfq_former_request, diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 241fdbcb29bc..85c72dd3dfee 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -255,8 +255,16 @@ void elv_merge_requests(request_queue_t *q, struct request *rq, e->ops->elevator_merge_req_fn(q, rq, next); } -void elv_requeue_request(request_queue_t *q, struct request *rq) +/* + * For careful internal use by the block layer. Essentially the same as + * a requeue in that it tells the io scheduler that this request is not + * active in the driver or hardware anymore, but we don't want the request + * added back to the scheduler. Function is not exported. + */ +void elv_deactivate_request(request_queue_t *q, struct request *rq) { + elevator_t *e = q->elevator; + /* * it already went through dequeue, we need to decrement the * in_flight count again @@ -264,6 +272,24 @@ void elv_requeue_request(request_queue_t *q, struct request *rq) if (blk_account_rq(rq)) q->in_flight--; + rq->flags &= ~REQ_STARTED; + + if (e->ops->elevator_deactivate_req_fn) + e->ops->elevator_deactivate_req_fn(q, rq); +} + +void elv_requeue_request(request_queue_t *q, struct request *rq) +{ + elv_deactivate_request(q, rq); + + /* + * if this is the flush, requeue the original instead and drop the flush + */ + if (rq->flags & REQ_BAR_FLUSH) { + clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags); + rq = rq->end_io_data; + } + /* * if iosched has an explicit requeue hook, then use that. otherwise * just put the request at the front of the queue diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 28977a7bad16..0135ec79dc83 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -356,6 +356,7 @@ static void blk_pre_flush_end_io(struct request *flush_rq) else { q->end_flush_fn(q, flush_rq); clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags); + q->request_fn(q); } } @@ -366,15 +367,9 @@ static void blk_post_flush_end_io(struct request *flush_rq) rq->flags |= REQ_BAR_POSTFLUSH; - /* - * called from end_that_request_last(), so we know that the queue - * lock is held - */ - spin_unlock(q->queue_lock); q->end_flush_fn(q, flush_rq); - spin_lock(q->queue_lock); - clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags); + q->request_fn(q); } struct request *blk_start_pre_flush(request_queue_t *q, struct request *rq) @@ -383,9 +378,12 @@ struct request *blk_start_pre_flush(request_queue_t *q, struct request *rq) BUG_ON(!blk_barrier_rq(rq)); + if (test_and_set_bit(QUEUE_FLAG_FLUSH, &q->queue_flags)) + return NULL; + rq_init(q, flush_rq); flush_rq->elevator_private = NULL; - flush_rq->flags = 0; + flush_rq->flags = REQ_BAR_FLUSH; flush_rq->rq_disk = rq->rq_disk; flush_rq->rl = NULL; @@ -395,11 +393,10 @@ struct request *blk_start_pre_flush(request_queue_t *q, struct request *rq) */ if (!q->prepare_flush_fn(q, flush_rq)) { rq->flags |= REQ_BAR_PREFLUSH | REQ_BAR_POSTFLUSH; + clear_bit(QUEUE_FLAG_FLUSH, &q->queue_flags); return rq; } - set_bit(QUEUE_FLAG_FLUSH, &q->queue_flags); - /* * some drivers dequeue requests right away, some only after io * completion. make sure the request is dequeued. @@ -407,6 +404,8 @@ struct request *blk_start_pre_flush(request_queue_t *q, struct request *rq) if (!list_empty(&rq->queuelist)) blkdev_dequeue_request(rq); + elv_deactivate_request(q, rq); + flush_rq->end_io_data = rq; flush_rq->end_io = blk_pre_flush_end_io; @@ -422,7 +421,7 @@ static void blk_start_post_flush(request_queue_t *q, struct request *rq) rq_init(q, flush_rq); flush_rq->elevator_private = NULL; - flush_rq->flags = 0; + flush_rq->flags = REQ_BAR_FLUSH; flush_rq->rq_disk = rq->rq_disk; flush_rq->rl = NULL; diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 36b90f85968c..c256feebd1bd 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -712,12 +712,13 @@ static void idedisk_end_flush(request_queue_t *q, struct request *flush_rq) blk_queue_issue_flush_fn(drive->queue, NULL); good_sectors = 0; } else if (flush_rq->errors) { - sector = ide_get_error_location(drive, flush_rq->buffer); - if ((sector >= rq->hard_sector) && - (sector < rq->hard_sector + rq->hard_nr_sectors)) - good_sectors = sector - rq->hard_sector; - else - good_sectors = 0; + good_sectors = 0; + if (blk_barrier_preflush(rq)) { + sector = ide_get_error_location(drive,flush_rq->buffer); + if ((sector >= rq->hard_sector) && + (sector < rq->hard_sector + rq->hard_nr_sectors)) + good_sectors = sector - rq->hard_sector; + } } if (flush_rq->errors) @@ -729,14 +730,10 @@ static void idedisk_end_flush(request_queue_t *q, struct request *flush_rq) bad_sectors = rq->hard_nr_sectors - good_sectors; - spin_lock(&ide_lock); - if (good_sectors) __ide_end_request(drive, rq, 1, good_sectors); if (bad_sectors) __ide_end_request(drive, rq, 0, bad_sectors); - - spin_unlock(&ide_lock); } static int idedisk_prepare_flush(request_queue_t *q, struct request *rq) @@ -1150,9 +1147,6 @@ static void idedisk_setup (ide_drive_t *drive) barrier = 0; } - if (!strncmp(drive->name, "hdc", 3)) - barrier = 1; - printk(KERN_INFO "%s: cache flushes %ssupported\n", drive->name, barrier ? "" : "not "); if (barrier) { diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index e8405375877f..da63045c165d 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -697,7 +697,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, int sense_valid = 0; int sense_deferred = 0; - if (blk_complete_barrier_rq(q, req, good_bytes << 9)) + if (blk_complete_barrier_rq(q, req, good_bytes >> 9)) return; /* diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index fe8a079b00fb..0fb36703292f 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -745,10 +745,21 @@ static void sd_end_flush(request_queue_t *q, struct request *flush_rq) struct scsi_cmnd *cmd = rq->special; unsigned int bytes = rq->hard_nr_sectors << 9; - if (!flush_rq->errors) + if (!flush_rq->errors) { + spin_unlock(q->queue_lock); scsi_io_completion(cmd, bytes, 0); - else + spin_lock(q->queue_lock); + } else if (blk_barrier_postflush(rq)) { + spin_unlock(q->queue_lock); scsi_io_completion(cmd, 0, bytes); + spin_lock(q->queue_lock); + } else { + /* + * force journal abort of barriers + */ + end_that_request_first(rq, -EOPNOTSUPP, rq->hard_nr_sectors); + end_that_request_last(rq); + } } static int sd_prepare_flush(request_queue_t *q, struct request *rq) @@ -758,7 +769,8 @@ static int sd_prepare_flush(request_queue_t *q, struct request *rq) if (sdkp->WCE) { memset(rq->cmd, 0, sizeof(rq->cmd)); - rq->flags = REQ_BLOCK_PC | REQ_SOFTBARRIER; + rq->flags |= REQ_BLOCK_PC | REQ_SOFTBARRIER; + rq->timeout = SD_TIMEOUT; rq->cmd[0] = SYNCHRONIZE_CACHE; return 1; } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 83eef4fde873..266b44fcfaa0 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -219,6 +219,7 @@ enum rq_flag_bits { __REQ_PM_SHUTDOWN, /* shutdown request */ __REQ_BAR_PREFLUSH, /* barrier pre-flush done */ __REQ_BAR_POSTFLUSH, /* barrier post-flush */ + __REQ_BAR_FLUSH, /* rq is the flush request */ __REQ_NR_BITS, /* stops here */ }; @@ -246,6 +247,7 @@ enum rq_flag_bits { #define REQ_PM_SHUTDOWN (1 << __REQ_PM_SHUTDOWN) #define REQ_BAR_PREFLUSH (1 << __REQ_BAR_PREFLUSH) #define REQ_BAR_POSTFLUSH (1 << __REQ_BAR_POSTFLUSH) +#define REQ_BAR_FLUSH (1 << __REQ_BAR_FLUSH) /* * State information carried for REQ_PM_SUSPEND and REQ_PM_RESUME diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 8cf0e3f290bf..ee54f81faad5 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -20,6 +20,7 @@ typedef int (elevator_may_queue_fn) (request_queue_t *, int); typedef int (elevator_set_req_fn) (request_queue_t *, struct request *, int); typedef void (elevator_put_req_fn) (request_queue_t *, struct request *); +typedef void (elevator_deactivate_req_fn) (request_queue_t *, struct request *); typedef int (elevator_init_fn) (request_queue_t *, elevator_t *); typedef void (elevator_exit_fn) (elevator_t *); @@ -34,6 +35,7 @@ struct elevator_ops elevator_add_req_fn *elevator_add_req_fn; elevator_remove_req_fn *elevator_remove_req_fn; elevator_requeue_req_fn *elevator_requeue_req_fn; + elevator_deactivate_req_fn *elevator_deactivate_req_fn; elevator_queue_empty_fn *elevator_queue_empty_fn; elevator_completed_req_fn *elevator_completed_req_fn; @@ -87,6 +89,7 @@ extern void elv_merge_requests(request_queue_t *, struct request *, extern void elv_merged_request(request_queue_t *, struct request *); extern void elv_remove_request(request_queue_t *, struct request *); extern void elv_requeue_request(request_queue_t *, struct request *); +extern void elv_deactivate_request(request_queue_t *, struct request *); extern int elv_queue_empty(request_queue_t *); extern struct request *elv_next_request(struct request_queue *q); extern struct request *elv_former_request(request_queue_t *, struct request *); -- cgit v1.2.3