Skip to contentSkip to conversation

Add disk hotswap support to libata

PATCH RESEND In review Lukasz Kosewski 2 participants 3 messages Jul 22, 2005 – Jul 31, 2005

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH RESEND 3/3 Add disk hotswap support to libata

Hey Jeff, everyone.

This is a resend of patch 3 of my libata hotswap series, wherein I found a silly flaw in my logic. I don't know what kind of crack I was smoking, but I somehow turned "a = a & ~b" in the Promise driver into "a = a ^ b" in my hotswap code, which is clearly wrong. I don't know how it worked through my testing, but I'm willing to bet this is a coincidence. Please apply this patch instead of the previously sent patch 03.

Luke Kosewski
Human Cannonball
Net Integration Technologies

21.07.05 Luke Kosewski <[email protected]>

* A full implementation of hotplug on a libata controller, this being the Promise Tx4/Tx2 Plus controller line (both SATA150 and SATAII150). Almost all of the code pertaining to how to talk to the hotplug registers has been stolen from the pdc-ulsata2 and ultra-1.0.8 Promise drivers. This involves detecting when we have an interrupt pending and on what device, as well as the bit where a hard SATA reset gets a SATAII150 controller to re-spew a plug interrupt.
* Note that the hotplug handling code comes AFTER the normal interrupt handling code in pdc_interrupt_common; this is because we're much more likely to receive normal interrupts, so this drops the AVERAGE interrupt handling time down a lot.

drivers/scsi/sata_promise.c renamed from drivers/scsi/sata_promise.c.old +79 -13
@@ -84,6 +84,7 @@ static void pdc_eng_timeout(struct ata_p
 static int pdc_port_start(struct ata_port *ap);
 static void pdc_port_stop(struct ata_port *ap);
 static void pdc_phy_reset(struct ata_port *ap);
+static void pdc2_phy_reset(struct ata_port *ap);
 static void pdc_pata_phy_reset(struct ata_port *ap);
 static void pdc_pata_cbl_detect(struct ata_port *ap);
 static void pdc_qc_prep(struct ata_queued_cmd *qc);
@@ -139,7 +140,7 @@ static struct ata_port_operations pdc2_a
 	.check_status		= ata_check_status,
 	.exec_command		= pdc_exec_command_mmio,
 	.dev_select		= ata_std_dev_select,
-	.phy_reset		= pdc_phy_reset,
+	.phy_reset		= pdc2_phy_reset,
 	.qc_prep		= pdc_qc_prep,
 	.qc_issue		= pdc_qc_issue_prot,
 	.eng_timeout		= pdc_eng_timeout,
@@ -325,6 +326,48 @@ static void pdc_phy_reset(struct ata_por
 		pdc_pata_phy_reset(ap);
 }
 
+/* Mask hotplug interrupts for one channel (ap) */
+static inline void pdc2_disable_channel_hotplug_interrupts(struct ata_port *ap)
+{
+	void *mmio = ap->host_set->mmio_base + PDC2_SATA_PLUG_CSR + 2;
+
+	u8 maskflags = readb(mmio);
+	maskflags |= (0x11 << (u8)ap->hard_port_no);
+	writeb(maskflags, mmio);
+}
+
+static inline void pdc2_enable_channel_hotplug_interrupts(struct ata_port *ap)
+{
+
+	void *mmio = ap->host_set->mmio_base + PDC2_SATA_PLUG_CSR;
+
+	//Clear channel hotplug interrupts
+	u8 maskflags = readb(mmio);
+	maskflags = (0x11 << (u8)ap->hard_port_no);
+	writeb(maskflags, mmio);
+
+	//Unmask channel hotplug interrupts
+	maskflags = readb(mmio + 2);
+	maskflags &= ~(0x11 << (u8)ap->hard_port_no);
+	writeb(maskflags, mmio + 2);
+}
+
+static void pdc2_phy_reset(struct ata_port *ap)
+{
+	/* As observed on the Promise SATAII150 Tx2 Plus/Tx4, giving the
+	 * controller a hard reset triggers another hotplug interrupt.  So
+	 * disable them for the hard reset, and re-enable afterwards.
+	 *
+	 * No PATA support here yet
+	 */
+	if (ap->flags & ATA_FLAG_SATA_RESET && ap->flags & ATA_FLAG_SATA) {
+		pdc2_disable_channel_hotplug_interrupts(ap);
+		pdc_phy_reset(ap);
+		pdc2_enable_channel_hotplug_interrupts(ap);
+	} else
+		pdc_phy_reset(ap);
+}
+
 static void pdc_pata_cbl_detect(struct ata_port *ap)
 {
 	u8 tmp;
@@ -483,6 +526,7 @@ static inline unsigned int pdc_interrupt
 	struct ata_host_set *host_set = dev_instance;
 	struct ata_port *ap;
 	void *mmio_base;
+	u8 plugdata, maskflags;
 	u32 mask = 0;
 	unsigned int i, tmp, handled = 0;
 	unsigned long flags;
@@ -492,21 +536,18 @@ static inline unsigned int pdc_interrupt
 	}
 
 	mmio_base = host_set->mmio_base;
-	
+
 	spin_lock_irqsave(&host_set->lock, flags);
 
 	/* reading should also clear interrupts */
 	mask = readl(mmio_base + PDC_INT_SEQMASK);
 
-	if (mask == 0xffffffff) {
-		VPRINTK("QUICK EXIT 2\n");
-		goto done_irq;
-	}
+	if (mask == 0xffffffff)
+		goto try_hotplug;
+
 	mask &= 0xffff;		/* only 16 tags possible */
-	if (!mask) {
-		VPRINTK("QUICK EXIT 3\n");
-		goto done_irq;
-	}
+	if (!mask)
+		goto try_hotplug;
 
 	writel(mask, mmio_base + PDC_INT_SEQMASK);
 
@@ -522,7 +563,32 @@ static inline unsigned int pdc_interrupt
 				handled += pdc_host_intr(ap, qc);
 		}
 	}
-       
+
+	if (handled)
+		goto done_irq;
+
+try_hotplug:
+	plugdata = readb(mmio_base + hotplug_regs);
+	maskflags = readb(mmio_base + hotplug_regs + 2);
+	plugdata &= ~maskflags;
+	if (plugdata) {
+		writeb(plugdata, mmio_base + hotplug_regs);
+		for (i = 0; i < host_set->n_ports; ++i) {
+			ap = host_set->ports[i];
+			if (!(ap->flags & ATA_FLAG_SATA))
+				continue;  //No PATA support here... yet
+			// Check unplug flag
+			if (plugdata & 0x01) {
+				ata_hotplug_unplug(ap);
+				handled = 1;
+			} else if ((plugdata >> 4) & 0x01) {  //Check plug flag
+				ata_hotplug_plug(ap);
+				handled = 1;
+			}
+			plugdata >>= 1;
+		}
+	}
+
 done_irq:
 	spin_unlock_irqrestore(&host_set->lock, flags);
 	return handled;
@@ -636,9 +702,9 @@ static void pdc_host_init(unsigned int c
 	tmp = readl(offset);
 	writel(tmp | 0xff, offset);
 
-	/* mask plug/unplug ints */
+	/* unmask plug/unplug ints */
 	tmp = readl(offset);
-	writel(tmp | 0xff0000, offset);
+	writel(tmp & 0xff00ffff, offset);
 
 	/* reduce TBG clock to 133 Mhz. */
 	tmp = readl(mmio + PDC_TBG_MODE);
#1 lore raw
Quoted 87 lines · Lukasz Kosewski

Hey Jeff, everyone.

This is a resend of patch 3 of my libata hotswap series, wherein I found a silly flaw in my logic. I don't know what kind of crack I was smoking, but I somehow turned "a = a & ~b" in the Promise driver into "a = a ^ b" in my hotswap code, which is clearly wrong. I don't know how it worked through my testing, but I'm willing to bet this is a coincidence. Please apply this patch instead of the previously sent patch 03.

Luke Kosewski
Human Cannonball
Net Integration Technologies

------------------------------------------------------------------------

21.07.05 Luke Kosewski <[email protected]>

* A full implementation of hotplug on a libata controller, this being the Promise Tx4/Tx2 Plus controller line (both SATA150 and SATAII150). Almost all of the code pertaining to how to talk to the hotplug registers has been stolen from the pdc-ulsata2 and ultra-1.0.8 Promise drivers. This involves detecting when we have an interrupt pending and on what device, as well as the bit where a hard SATA reset gets a SATAII150 controller to re-spew a plug interrupt.
* Note that the hotplug handling code comes AFTER the normal interrupt handling code in pdc_interrupt_common; this is because we're much more likely to receive normal interrupts, so this drops the AVERAGE interrupt handling time down a lot.

drivers/scsi/sata_promise.c renamed from drivers/scsi/sata_promise.c.old +33 -1
@@ -84,6 +84,7 @@ static void pdc_eng_timeout(struct ata_p
 static int pdc_port_start(struct ata_port *ap);
 static void pdc_port_stop(struct ata_port *ap);
 static void pdc_phy_reset(struct ata_port *ap);
+static void pdc2_phy_reset(struct ata_port *ap);
 static void pdc_pata_phy_reset(struct ata_port *ap);
 static void pdc_pata_cbl_detect(struct ata_port *ap);
 static void pdc_qc_prep(struct ata_queued_cmd *qc);
@@ -139,7 +140,7 @@ static struct ata_port_operations pdc2_a
 	.check_status		= ata_check_status,
 	.exec_command		= pdc_exec_command_mmio,
 	.dev_select		= ata_std_dev_select,
-	.phy_reset		= pdc_phy_reset,
+	.phy_reset		= pdc2_phy_reset,
 	.qc_prep		= pdc_qc_prep,
 	.qc_issue		= pdc_qc_issue_prot,
 	.eng_timeout		= pdc_eng_timeout,
@@ -325,6 +326,48 @@ static void pdc_phy_reset(struct ata_por
 		pdc_pata_phy_reset(ap);
 }
 
+/* Mask hotplug interrupts for one channel (ap) */
+static inline void pdc2_disable_channel_hotplug_interrupts(struct ata_port *ap)
+{
+	void *mmio = ap->host_set->mmio_base + PDC2_SATA_PLUG_CSR + 2;
+
+	u8 maskflags = readb(mmio);
+	maskflags |= (0x11 << (u8)ap->hard_port_no);
+	writeb(maskflags, mmio);
+}
+
+static inline void pdc2_enable_channel_hotplug_interrupts(struct ata_port *ap)
+{
+
+	void *mmio = ap->host_set->mmio_base + PDC2_SATA_PLUG_CSR;
+
+	//Clear channel hotplug interrupts
+	u8 maskflags = readb(mmio);
+	maskflags = (0x11 << (u8)ap->hard_port_no);
+	writeb(maskflags, mmio);
+
+	//Unmask channel hotplug interrupts
+	maskflags = readb(mmio + 2);
+	maskflags &= ~(0x11 << (u8)ap->hard_port_no);
+	writeb(maskflags, mmio + 2);
+}
+
+static void pdc2_phy_reset(struct ata_port *ap)
+{
+	/* As observed on the Promise SATAII150 Tx2 Plus/Tx4, giving the
+	 * controller a hard reset triggers another hotplug interrupt.  So
+	 * disable them for the hard reset, and re-enable afterwards.

drivers/scsi/sata_promise.c static void pdc_phy_reset(struct ata_por · line 369

Quoted 10 lines
drivers/scsi/sata_promise.c renamed from drivers/scsi/sata_promise.c.old +10 -0
+	 *
+	 * No PATA support here yet
+	 */
+	if (ap->flags & ATA_FLAG_SATA_RESET && ap->flags & ATA_FLAG_SATA) {
+		pdc2_disable_channel_hotplug_interrupts(ap);
+		pdc_phy_reset(ap);
+		pdc2_enable_channel_hotplug_interrupts(ap);
+	} else
+		pdc_phy_reset(ap);
+}

Don't special case this. disable/enable hotplug interrupts for all controllers.

Quoted 18 lines
drivers/scsi/sata_promise.c renamed from drivers/scsi/sata_promise.c.old +2 -1
 static void pdc_pata_cbl_detect(struct ata_port *ap)
 {
 	u8 tmp;
@@ -483,6 +526,7 @@ static inline unsigned int pdc_interrupt
 	struct ata_host_set *host_set = dev_instance;
 	struct ata_port *ap;
 	void *mmio_base;
+	u8 plugdata, maskflags;
 	u32 mask = 0;
 	unsigned int i, tmp, handled = 0;
 	unsigned long flags;
@@ -492,21 +536,18 @@ static inline unsigned int pdc_interrupt
 	}
 
 	mmio_base = host_set->mmio_base;
-	
+
 	spin_lock_irqsave(&host_set->lock, flags);

drivers/scsi/sata_promise.c static inline unsigned int pdc_interrupt · line 546

Quoted 10 lines
drivers/scsi/sata_promise.c renamed from drivers/scsi/sata_promise.c.old +2 -4
 
 	/* reading should also clear interrupts */
 	mask = readl(mmio_base + PDC_INT_SEQMASK);
 
-	if (mask == 0xffffffff) {
-		VPRINTK("QUICK EXIT 2\n");
-		goto done_irq;
-	}
+	if (mask == 0xffffffff)
+		goto try_hotplug;

you are confusing controller hotplug (mask == 0xffffffff) and device hotplug.

controller hotplug should already be handled correct, by testing 0xffffffff and supporting the PCI ->remove hook.

Quoted 28 lines
drivers/scsi/sata_promise.c renamed from drivers/scsi/sata_promise.c.old +15 -5
 	mask &= 0xffff;		/* only 16 tags possible */
-	if (!mask) {
-		VPRINTK("QUICK EXIT 3\n");
-		goto done_irq;
-	}
+	if (!mask)
+		goto try_hotplug;
 
 	writel(mask, mmio_base + PDC_INT_SEQMASK);
 
@@ -522,7 +563,32 @@ static inline unsigned int pdc_interrupt
 				handled += pdc_host_intr(ap, qc);
 		}
 	}
-       
+
+	if (handled)
+		goto done_irq;
+
+try_hotplug:
+	plugdata = readb(mmio_base + hotplug_regs);
+	maskflags = readb(mmio_base + hotplug_regs + 2);
+	plugdata &= ~maskflags;
+	if (plugdata) {
+		writeb(plugdata, mmio_base + hotplug_regs);
+		for (i = 0; i < host_set->n_ports; ++i) {
+			ap = host_set->ports[i];
+			if (!(ap->flags & ATA_FLAG_SATA))

drivers/scsi/sata_promise.c static inline unsigned int pdc_interrupt · line 588

Quoted 10 lines
drivers/scsi/sata_promise.c renamed from drivers/scsi/sata_promise.c.old +10 -0
+				continue;  //No PATA support here... yet
+			// Check unplug flag
+			if (plugdata & 0x01) {
+				ata_hotplug_unplug(ap);
+				handled = 1;
+			} else if ((plugdata >> 4) & 0x01) {  //Check plug flag
+				ata_hotplug_plug(ap);
+				handled = 1;
+			}
+			plugdata >>= 1;

you probably need a debounce timer; see the SiI email I am about to send.

Quoted 15 lines
drivers/scsi/sata_promise.c renamed from drivers/scsi/sata_promise.c.old +2 -2
 done_irq:
 	spin_unlock_irqrestore(&host_set->lock, flags);
 	return handled;
@@ -636,9 +702,9 @@ static void pdc_host_init(unsigned int c
 	tmp = readl(offset);
 	writel(tmp | 0xff, offset);
 
-	/* mask plug/unplug ints */
+	/* unmask plug/unplug ints */
 	tmp = readl(offset);
-	writel(tmp | 0xff0000, offset);
+	writel(tmp & 0xff00ffff, offset);
 
 	/* reduce TBG clock to 133 Mhz. */
 	tmp = readl(mmio + PDC_TBG_MODE);
#2 lore raw
Quoted 10 lines · Jeff Garzik
Quoted 10 lines
(quoted diff) +10 -0
+static void pdc2_phy_reset(struct ata_port *ap)
+{
+     /* As observed on the Promise SATAII150 Tx2 Plus/Tx4, giving the
+      * controller a hard reset triggers another hotplug interrupt.  So
+      * disable them for the hard reset, and re-enable afterwards.
+      *
+      * No PATA support here yet
+      */
+     if (ap->flags & ATA_FLAG_SATA_RESET && ap->flags & ATA_FLAG_SATA) {
+             pdc2_disable_channel_hotplug_interrupts(ap);
Quoted 9 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+             pdc_phy_reset(ap);
+             pdc2_enable_channel_hotplug_interrupts(ap);
+     } else
+             pdc_phy_reset(ap);
+}

Don't special case this. disable/enable hotplug interrupts for all controllers.

OK, I've done this in my new patches.

<snip>

Quoted 9 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+     if (mask == 0xffffffff)
+             goto try_hotplug;

you are confusing controller hotplug (mask == 0xffffffff) and device hotplug.

controller hotplug should already be handled correct, by testing 0xffffffff and supporting the PCI ->remove hook.

Yes. Done this as well.

<snip>

Quoted 16 lines
Quoted 5 lines
(quoted diff) +5 -0
+     if (plugdata) {
+             writeb(plugdata, mmio_base + hotplug_regs);
+             for (i = 0; i < host_set->n_ports; ++i) {
+                     ap = host_set->ports[i];
+                     if (!(ap->flags & ATA_FLAG_SATA))

Review

Quoted 10 lines
(quoted diff) +10 -0
+                             continue;  //No PATA support here... yet
+                     // Check unplug flag
+                     if (plugdata & 0x01) {
+                             ata_hotplug_unplug(ap);
+                             handled = 1;
+                     } else if ((plugdata >> 4) & 0x01) {  //Check plug flag
+                             ata_hotplug_plug(ap);
+                             handled = 1;
+                     }
+                     plugdata >>= 1;
Quoted 1 line

you probably need a debounce timer; see the SiI email I am about to send.

Keep in mind that I sent three patches:
- 01 - sata_promise properly supporting SATAII150 controllers
- 02 - libata hotswap infrastructure
- 03 - sata_promise supporting the infrastructure

I believe the debounce timer should really go into patch 02. This is actually something that's probably needed by every controller... if you're just plugging drives in/out of a controller, what you're doing is connecting metal pins, they can be imprecise and trigger a whole host of interrupts. Hence, I propose changing patch 02 (attached for all of your convenience) to be as follows:
- on calling ata_hotplug_unplug: if a debounce timer is running, stop it. Conditionally call ap->ops->unplug_janitor or something like that, for cases (like the Sil driver, it would seem) that need to do something special like hard-reset the channel. Start a new debounce_timer (say, with an interval of 500ms, as suggested by the Sil guys?
- on calling ata_hotplug_plug: if a debounce_timer is running, stop it. Conditionally call ap->ops->plug_janitor, if any driver needs anything special done on a plug action. Start a new one.
- when the debounce_timer goes off, the first thing it does is call ap->ops->hotplug_action, which returns whether it should be plugging or unplugging. On a plug, do an ata_bus_probe, an an ata_scsi_plug on success, ata_scsi_unplug on failure. On an unplug event, ata_port_disable and then call ata_scsi_unplug (kill any outstanding taskfiles).

This means that patch 02 will no longer have a workqueue, but a timer.

Any suggestions/comments on this approach?

Luke Kosewski

21.07.05 Luke Kosewski <[email protected]>

* A patch to add a general-purpose hotswap framework to libata. From the point of view of the driver writer, we only have two new API functions; 'ata_hotplug_plug' and 'ata_hotplug_unplug', which have rather self-explanatory functions.
* A few misc changes are necessary to properly reset flags which are set by devices when they are present to make sure that new devices (for instance, disks with different max UDMA transfer rates, not using LBA48, etc.) being swapped in do not assume values for the old devices.
* Gratuitous comments here that can probably be removed, so that anyone looking at this can understand this and poke holes in my logic.

drivers/scsi/libata-core.c renamed from drivers/scsi/libata-core.c.old +85 -1
@@ -44,7 +44,6 @@
 #include <scsi/scsi_host.h>
 #include <linux/libata.h>
 #include <asm/io.h>
-#include <asm/semaphore.h>
 #include <asm/byteorder.h>
 
 #include "libata.h"
@@ -65,6 +64,7 @@ static void __ata_qc_complete(struct ata
 
 static unsigned int ata_unique_id = 1;
 static struct workqueue_struct *ata_wq;
+static struct workqueue_struct *ata_irq_wq;
 
 MODULE_AUTHOR("Jeff Garzik");
 MODULE_DESCRIPTION("Library module for ATA devices");
@@ -1134,6 +1134,11 @@ static void ata_dev_identify(struct ata_
 		return;
 	}
 
+	/* Necessary if we had an LBA48 drive in, we pulled it out, and put in
+	 * a non-LBA48 drive to replace it.
+	 */
+	dev->flags &= ~ATA_DFLAG_LBA48;
+
 	if (ap->flags & (ATA_FLAG_SRST | ATA_FLAG_SATA_RESET))
 		using_edd = 0;
 	else
@@ -3635,6 +3640,73 @@ idle_irq:
 	return 0;	/* irq not handled */
 }
 
+void ata_check_kill_qc(struct ata_port *ap)
+{
+	struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
+
+	if (unlikely(qc)) {
+		/* This is SO bad.  But we can't just run
+		 * ata_qc_complete without doing this, because
+		 * ata_scsi_qc_complete wants to talk to the device,
+		 * and we can't let it do that since it doesn't exist
+		 * anymore.
+		 */
+		ata_scsi_prepare_qc_abort(qc);
+		ata_qc_complete(qc, ATA_ERR);
+	}
+}
+
+static void ata_hotplug_unplug_func(void *_data)
+{
+	struct ata_port *ap = (struct ata_port *)_data;
+	DPRINTK("Got an unplug request on port %d\n", ap->id);
+
+	down(&ap->hotplug_mutex);
+
+	ata_scsi_handle_unplug(ap);
+
+	up(&ap->hotplug_mutex);
+}
+
+static void ata_hotplug_plug_func(void *_data)
+{
+	struct ata_port *ap = (struct ata_port *)_data;
+	DPRINTK("Got a plug request on port %d\n", ap->id);
+
+	down(&ap->hotplug_mutex);
+	/* Pure evil.  Suppose that you have an 'unplug' waiting on your
+	 * queue, and this function executes while it's there (because 
+	 * you unplugged/plugged in a disk on an SMP system VERY FAST).
+	 * REALLY bad news, because when you unplugged your disk, you
+	 * might have had a pending qc which will now sit there and time
+	 * out like the mofo it is.  Check to see if we have one sitting
+	 * around and KILL IT if this is so.
+	 */
+	ata_check_kill_qc(ap);
+	// Observed necessary on some Seagate drives.
+	ap->flags |= ATA_FLAG_SATA_RESET;
+	ap->udma_mask = ap->orig_udma_mask;
+
+	if (ata_bus_probe(ap) /* Does its own locking */)
+		ata_scsi_handle_unplug(ap);  //might be necessary on SMP
+	else
+		ata_scsi_handle_plug(ap);
+	up(&ap->hotplug_mutex);
+}
+
+/* Should be protected by host_set->lock */
+void ata_hotplug_unplug(struct ata_port *ap)
+{
+	ata_port_disable(ap); //disable this NOW, device is gone
+	queue_work(ata_irq_wq, &ap->hotplug_unplug_task);
+}
+
+/* Should be protected by host_set->lock */
+void ata_hotplug_plug(struct ata_port *ap)
+{
+	queue_work(ata_irq_wq, &ap->hotplug_plug_task);
+}
+
 /**
  *	ata_interrupt - Default ATA host interrupt handler
  *	@irq: irq line (unused)
@@ -3860,7 +3932,11 @@ static void ata_host_init(struct ata_por
 	ap->cbl = ATA_CBL_NONE;
 	ap->active_tag = ATA_TAG_POISON;
 	ap->last_ctl = 0xFF;
+	ap->orig_udma_mask = ent->udma_mask;
 
+	init_MUTEX(&ap->hotplug_mutex);
+	INIT_WORK(&ap->hotplug_plug_task, ata_hotplug_plug_func, ap);
+	INIT_WORK(&ap->hotplug_unplug_task, ata_hotplug_unplug_func, ap);
 	INIT_WORK(&ap->packet_task, atapi_packet_task, ap);
 	INIT_WORK(&ap->pio_task, ata_pio_task, ap);
 
@@ -4468,6 +4544,11 @@ static int __init ata_init(void)
 	ata_wq = create_workqueue("ata");
 	if (!ata_wq)
 		return -ENOMEM;
+	ata_irq_wq = create_workqueue("ata_irq");
+	if (!ata_irq_wq) {
+		destroy_workqueue(ata_wq);
+		return -ENOMEM;
+	}
 
 	printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n");
 	return 0;
@@ -4476,6 +4557,7 @@ static int __init ata_init(void)
 static void __exit ata_exit(void)
 {
 	destroy_workqueue(ata_wq);
+	destroy_workqueue(ata_irq_wq);
 }
 
 module_init(ata_init);
@@ -4531,6 +4613,8 @@ EXPORT_SYMBOL_GPL(ata_dev_classify);
 EXPORT_SYMBOL_GPL(ata_dev_id_string);
 EXPORT_SYMBOL_GPL(ata_dev_config);
 EXPORT_SYMBOL_GPL(ata_scsi_simulate);
+EXPORT_SYMBOL_GPL(ata_hotplug_unplug);
+EXPORT_SYMBOL_GPL(ata_hotplug_plug);
 
 #ifdef CONFIG_PCI
 EXPORT_SYMBOL_GPL(pci_test_config_bits);
drivers/scsi/libata-scsi.c renamed from drivers/scsi/libata-scsi.c.old +47 -0

53 lines not on this page · raw message

drivers/scsi/libata.h renamed from drivers/scsi/libata.h.old +4 -0
@@ -40,6 +40,7 @@ extern struct ata_queued_cmd *ata_qc_new
 extern void ata_qc_free(struct ata_queued_cmd *qc);
 extern int ata_qc_issue(struct ata_queued_cmd *qc);
 extern int ata_check_atapi_dma(struct ata_queued_cmd *qc);
+extern void ata_check_kill_qc(struct ata_port *ap);
 extern void ata_dev_select(struct ata_port *ap, unsigned int device,
                            unsigned int wait, unsigned int can_sleep);
 extern void ata_tf_to_host_nolock(struct ata_port *ap, struct ata_taskfile *tf);
@@ -76,6 +77,9 @@ extern void ata_scsi_badcmd(struct scsi_
 extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args, 
                         unsigned int (*actor) (struct ata_scsi_args *args,
                                            u8 *rbuf, unsigned int buflen));
+extern void ata_scsi_prepare_qc_abort(struct ata_queued_cmd *qc);
+extern void ata_scsi_handle_plug(struct ata_port *ap);
+extern void ata_scsi_handle_unplug(struct ata_port *ap);
 
 static inline void ata_bad_scsiop(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
 {
include/linux/libata.h renamed from include/linux/libata.h.old +8 -0
@@ -29,6 +29,7 @@
 #include <asm/io.h>
 #include <linux/ata.h>
 #include <linux/workqueue.h>
+#include <asm/semaphore.h>
 
 /*
  * compile-time options
@@ -319,6 +320,9 @@ struct ata_port {
 	struct ata_host_stats	stats;
 	struct ata_host_set	*host_set;
 
+	struct semaphore	hotplug_mutex;
+	struct work_struct	hotplug_plug_task;
+	struct work_struct	hotplug_unplug_task;
 	struct work_struct	packet_task;
 
 	struct work_struct	pio_task;
@@ -326,6 +330,8 @@ struct ata_port {
 	unsigned long		pio_task_timeout;
 
 	void			*private_data;
+
+	unsigned int		orig_udma_mask;
 };
 
 struct ata_port_operations {
@@ -402,6 +408,8 @@ extern int ata_scsi_queuecmd(struct scsi
 extern int ata_scsi_error(struct Scsi_Host *host);
 extern int ata_scsi_release(struct Scsi_Host *host);
 extern unsigned int ata_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
+extern void ata_hotplug_unplug(struct ata_port *ap);
+extern void ata_hotplug_plug(struct ata_port *ap);
 /*
  * Default driver ops implementations
  */