linux-raid.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Mark Lord <liml@rtr.ca>
To: Tejun Heo <htejun@gmail.com>
Cc: Johny Mail list <maillist.johny@gmail.com>,
	Brad Campbell <brad@wasp.net.au>,
	linux-raid@vger.kernel.org, linux-ide@vger.kernel.org
Subject: Re: Linux Software RAID is really RAID?
Date: Wed, 04 Jul 2007 14:56:09 -0400	[thread overview]
Message-ID: <468BED49.2030301@rtr.ca> (raw)
In-Reply-To: <468AF5F8.9060703@gmail.com>

Tejun Heo wrote:
> Mark Lord wrote:
>> I believe he said it was ICH5 (different post/thread).
>>
>> My observation on ICH5 is that if one unplugs a drive,
>> then the chipset/cpu locks up hard when toggling SRST
>> in the EH code.
>>
>> Specifically, it locks up at the instruction
>> which restores SRST back to the non-asserted state,
>> which likely corresponds to the chipset finally actually
>> sending a FIS to the drive.
>>
>> A hard(ware) lockup, not software.
>> That's why Intel says ICH5 doesn't do hotplug.
> 
> OIC.  I don't think there's much left to do from the driver side then.
> Or is there any workaround?

The workaround I have, for 2.6.18.8, is to provide an "offline()" method
for ICH5 that polls for device present before attempting SRST.

I hope to eventually clean this up and submit it for you,
after your existing polling-hp code goes upstream.

Here's my present hack (below).  Feel free to use/ignore.

***

Implement ICH5 chipset handling for drive hot insertion/removal.
This cannot go upstream, as it conflicts with a more generic
polled-hotplug framework that is currently in development.

Hot-inserted drives are automatically detected within a second or two,
and are ready-to-use within 30 seconds or so.

Hot-removed drives are *not* noticed by the kernel until the next
time they are accessed.  If you want this to happen quickly,
then just launch a script like this from /etc/inittab at boot time:

   #!/bin/bash
   ( while ( /bin/true ) ; do /sbin/hdparm -C /dev/sd[a-z] ; sleep 5 ; done ) &>/dev/null &

Signed-off-by: Mark Lord <mlord@pobox.com>
---

diff -u --recursive --new-file --exclude-from=old/Documentation/dontdiff old/drivers/scsi/ata_piix.c linux/drivers/scsi/ata_piix.c
--- old/drivers/scsi/ata_piix.c	2007-04-20 14:08:46.000000000 -0400
+++ linux/drivers/scsi/ata_piix.c	2007-06-26 07:23:21.000000000 -0400
@@ -106,6 +106,8 @@
 	PIIX_FLAG_AHCI		= (1 << 27), /* AHCI possible */
 	PIIX_FLAG_CHECKINTR	= (1 << 28), /* make sure PCI INTx enabled */
 
+	PIIX_HOTPLUG_POLL_TM	= (2 * (HZ)),	/* polling interval for hotplug */
+
 	/* combined mode.  if set, PATA is channel 0.
 	 * if clear, PATA is channel 1.
 	 */
@@ -150,6 +152,171 @@
 	const struct piix_map_db *map_db;
 };
 
+struct piix_port_priv {
+	int pcs_hotplug_supported;
+	struct timer_list hotplug_timer;
+	u16 old_pcs;
+};
+
+static u32 ich_scr_read (struct ata_port *ap, unsigned int reg)
+{
+	u32 scr = 0;
+
+	if (reg == SCR_STATUS) {
+		struct piix_port_priv *pp = ap->private_data;
+		if (pp && pp->pcs_hotplug_supported) {
+			u16 pcs, port_bit = (1 << ap->hard_port_no);
+			struct pci_dev *pdev = to_pci_dev(ap->dev);
+
+			pci_read_config_word(pdev, ICH5_PCS, &pcs);
+			if (pcs & (port_bit << 4))
+				scr = 0x113;
+		}
+	}
+	return scr;
+}
+
+static int ich_port_offline (struct ata_port *ap)
+{
+	struct pci_dev *pdev;
+	u16 pcs, port_bit = (1 << ap->hard_port_no);
+	struct piix_port_priv *pp = ap->private_data;
+	u8 ostatus;
+	unsigned int offline;
+
+	if (!pp || !pp->pcs_hotplug_supported) {
+		u32 sstatus;
+		if (!sata_scr_read(ap, SCR_STATUS, &sstatus) && (sstatus & 0xf) != 0x3)
+			return 1;
+		return 0;
+	}
+
+	/*
+	 * ICH5 with a mostly good/working PCS register.
+	 * The only flaw is, it doesn't seem to detect *removed* drives
+	 * unless we toggle the enable line before checking.
+	 */
+	ostatus = ata_altstatus(ap);
+	pdev = to_pci_dev(ap->dev);
+	pci_read_config_word(pdev, ICH5_PCS, &pcs);
+	offline = ((pcs & (port_bit << 4)) == 0);
+
+	if (!offline) {
+		unsigned int usecs;
+
+		/* Cycle PCS register to force it to redetect devices: */
+		pci_write_config_word(pdev, ICH5_PCS, pcs & ~port_bit);
+		udelay(1);
+		pci_write_config_word(pdev, ICH5_PCS, 0x0003);
+
+		/* Wait for SATA PHY to sync up; typically 5->6 usecs */
+		for (usecs = 0; usecs < 100; ++usecs) {
+			pci_read_config_word(pdev,  ICH5_PCS, &pcs);
+			offline = ((pcs & (port_bit << 4)) == 0);
+			if (!offline)
+				break;
+			udelay(1);
+		}
+		if (!offline) {
+			unsigned int msecs;
+			/* Wait for drive to become not-BUSY, typically 10->62 msecs */
+			for (msecs = 1; msecs < 150; msecs += 3) {
+				u8 status;
+				msleep(3);
+				status = ata_altstatus(ap);
+				if (status && !(status & ATA_BUSY))
+					break;
+			}
+			usecs += msecs * 1000;
+		}
+		printk("ata%u (port %u): status=%02x pcs=0x%04x offline=%u delay=%u usecs\n",
+			ap->id, ap->hard_port_no, ostatus, pcs, offline, usecs);
+	}
+	if (offline)
+		ata_port_disable(ap);
+	return offline;
+}
+
+static void pcs_hotplug_poll (unsigned long data)
+{
+	struct ata_port *ap = (void *)data;
+	struct pci_dev *pdev = to_pci_dev(ap->dev);
+	u16 old, new, port_bit = ((1 << ap->hard_port_no) << 4);
+	struct piix_port_priv *pp = ap->private_data;
+	int check_hotplug = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(ap->lock, flags);
+
+	if (!ap->qc_active) {
+		pci_read_config_word(pdev, ICH5_PCS, &new);
+		old = pp->old_pcs;
+		pp->old_pcs = new;
+
+		//printk("pcs_hotplug_poll(%d.%d) old=%04x new=%04x\n", ap->id, ap->hard_port_no, old, new);
+
+		if ((new & port_bit) != (old & port_bit)) {
+			check_hotplug = 1;
+		} else if (old & port_bit) {
+			//if (ap->hard_port_no == 1)	//FIXME FIXME FIXME
+			//	check_hotplug = 1;
+		}
+
+		if (check_hotplug) {
+			struct ata_eh_info *ehi = &ap->eh_info;
+
+			ata_port_printk(ap, KERN_INFO, "pcs_hotplug_poll: old=%04x new=%04x\n", old, new);
+			ata_ehi_clear_desc(ehi);
+			ata_ehi_hotplugged(ehi);
+			ata_ehi_push_desc(ehi, "hotplug event");
+			ata_port_freeze(ap);
+		}
+	}
+	if (pp->pcs_hotplug_supported)
+		mod_timer(&pp->hotplug_timer, jiffies + PIIX_HOTPLUG_POLL_TM);
+	spin_unlock_irqrestore(ap->lock, flags);
+}
+
+static int ich_port_start (struct ata_port *ap)
+{
+	struct pci_dev *pdev = to_pci_dev(ap->dev);
+	int rc;
+
+	rc = ata_port_start(ap);
+	if (rc == 0) {
+		if (pdev->vendor == 0x8086 && pdev->device == 0x24d1) {
+			struct piix_port_priv *pp;
+			pp = kzalloc(sizeof(*pp), GFP_KERNEL);
+			if (pp) {
+				pp->pcs_hotplug_supported = 1;
+				if (ap->private_data)
+					printk(KERN_ERR "port_start: huh? private_data=%p instead of NULL\n", ap->private_data);
+				ap->private_data = pp;
+				setup_timer(&pp->hotplug_timer, pcs_hotplug_poll, (unsigned long)ap);
+				pp->hotplug_timer.expires = jiffies + PIIX_HOTPLUG_POLL_TM;
+				add_timer(&pp->hotplug_timer);
+			} else {
+				printk(KERN_ERR "ich_port_start: failed to alloc %d bytes for port_priv\n", sizeof(*pp));
+			}
+		}
+	} else {
+		printk(KERN_ERR "ich_port_start: ata_port_start failed, rc=%d\n", rc);
+	}
+	return rc;
+}
+
+static void ich_port_stop (struct ata_port *ap)
+{
+	struct piix_port_priv *pp = ap->private_data;
+
+	if (pp) {
+		pp->pcs_hotplug_supported = 0;
+		del_timer_sync(&pp->hotplug_timer);
+		ap->private_data = NULL;
+		kfree(pp);
+	}
+}
+
 static int piix_init_one (struct pci_dev *pdev,
 				    const struct pci_device_id *ent);
 static void piix_host_stop(struct ata_host_set *host_set);
@@ -289,8 +456,11 @@
 	.irq_handler		= ata_interrupt,
 	.irq_clear		= ata_bmdma_irq_clear,
 
-	.port_start		= ata_port_start,
-	.port_stop		= ata_port_stop,
+	.scr_read		= ich_scr_read,
+
+	.port_offline		= ich_port_offline,
+	.port_start		= ich_port_start,
+	.port_stop		= ich_port_stop,
 	.host_stop		= piix_host_stop,
 };
 
diff -u --recursive --new-file --exclude-from=old/Documentation/dontdiff old/drivers/scsi/libata-core.c linux/drivers/scsi/libata-core.c
--- old/drivers/scsi/libata-core.c	2007-04-20 14:08:45.000000000 -0400
+++ linux/drivers/scsi/libata-core.c	2007-06-26 07:22:19.000000000 -0400
@@ -4914,7 +4914,7 @@
  */
 int sata_scr_write(struct ata_port *ap, int reg, u32 val)
 {
-	if (sata_scr_valid(ap)) {
+	if (sata_scr_valid(ap) && ap->ops->scr_write) {
 		ap->ops->scr_write(ap, reg, val);
 		return 0;
 	}
@@ -4987,6 +4987,8 @@
 {
 	u32 sstatus;
 
+	if (ap->ops->port_offline)
+		return ap->ops->port_offline(ap);
 	if (!sata_scr_read(ap, SCR_STATUS, &sstatus) && (sstatus & 0xf) != 0x3)
 		return 1;
 	return 0;
diff -u --recursive --new-file --exclude-from=old/Documentation/dontdiff old/include/linux/libata.h linux/include/linux/libata.h
--- old/include/linux/libata.h	2007-06-26 07:22:26.000000000 -0400
+++ linux/include/linux/libata.h	2007-06-26 07:22:19.000000000 -0400
@@ -614,6 +614,7 @@
 
 	int (*port_start) (struct ata_port *ap);
 	void (*port_stop) (struct ata_port *ap);
+	int (*port_offline) (struct ata_port *ap);
 
 	void (*host_stop) (struct ata_host_set *host_set);
 

  parent reply	other threads:[~2007-07-04 18:56 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-06-26 17:43 Linux Software RAID is really RAID? Johny Mail list
2007-06-26 18:34 ` Brad Campbell
2007-06-27 11:54   ` Johny Mail list
2007-07-03  8:15   ` Tejun Heo
2007-07-03 12:17     ` Johny Mail list
2007-07-03 23:08       ` Mark Lord
2007-07-04  1:20         ` Tejun Heo
2007-07-04  9:14           ` Alan Cox
2007-07-04 18:56           ` Mark Lord [this message]
2007-07-12 16:27             ` Johny Mail list

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=468BED49.2030301@rtr.ca \
    --to=liml@rtr.ca \
    --cc=brad@wasp.net.au \
    --cc=htejun@gmail.com \
    --cc=linux-ide@vger.kernel.org \
    --cc=linux-raid@vger.kernel.org \
    --cc=maillist.johny@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).