linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Elias Oltmanns <eo@nebensachen.de>
To: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: linux-ide@vger.kernel.org, linux-kernel@vger.kernel.org,
	Jens Axboe <jens.axboe@oracle.com>
Subject: [PATCH 1/4] disk-protect: Add disk shock protection helpers to libata
Date: Fri, 07 Mar 2008 19:25:16 +0100	[thread overview]
Message-ID: <20080307181750.9981.88028.stgit@denkblock.local> (raw)
In-Reply-To: 87bq5qfm2v.fsf@denkblock.local

This patch adds some helper functions to libata in order to provide low
level suport for disk shock protection. The user interface to this
functionality will be implemented in the block layer in a subsequent patch.

Signed-off-by: Elias Oltmanns <eo@nebensachen.de>
---

 drivers/ata/libata-core.c |   69 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/ata/libata-eh.c   |    9 ++++++
 drivers/ata/libata-scsi.c |   31 ++++++++++++++++++++
 drivers/ata/libata.h      |    1 +
 include/linux/ata.h       |   12 ++++++++
 include/linux/libata.h    |    4 ++-
 6 files changed, 125 insertions(+), 1 deletions(-)

diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 6380726..74c3e7e 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -6634,6 +6634,75 @@ int ata_port_start(struct ata_port *ap)
 }
 
 /**
+ *	ata_protect_dev - Stop I/O to device and unload disk heads
+ *	@dev: Device to be protected
+ *
+ *	Issue an IDLE IMMEDIATE command with UNLOAD FEATURE.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep)
+ *
+ *	RETURNS:
+ *	0 on success, -EIO on failure.
+ */
+int ata_protect_dev(struct ata_device *dev)
+{
+	struct ata_port *ap = dev->link->ap;
+	struct ata_taskfile tf;
+	unsigned long flags;
+	unsigned int err_mask;
+	int unload = ata_id_has_unload(dev->id);
+	/* We need a mechanism to set unload to 1 for devices that
+	 * support the unload feature of idle immediate but don't
+	 * report that capability in dev->id. Is dev->horkage or
+	 * dev->flags the right place for such a flag?
+	 */
+
+	spin_lock_irqsave(ap->lock, flags);
+	if (unlikely(!(dev->flags & ATA_DFLAG_PROTECTED))) {
+		spin_unlock_irqrestore(ap->lock, flags);
+		goto out;
+	}
+	spin_unlock_irqrestore(ap->lock, flags);
+
+	ata_tf_init(dev, &tf);
+	tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR;
+	tf.protocol |= ATA_PROT_NODATA;
+	if (unload) {
+		tf.command = ATA_CMD_IDLEIMMEDIATE;
+		tf.feature = 0x44;
+		tf.lbal = 0x4c;
+		tf.lbam = 0x4e;
+		tf.lbah = 0x55;
+	} else
+		tf.command = ATA_CMD_STANDBYNOW1;
+
+	err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
+	if (err_mask)
+		goto abort;
+
+	if (unload) {
+		if (tf.lbal == 0xc4)
+			ata_dev_printk(dev, KERN_DEBUG,
+				       "head parked\n");
+		else
+			goto abort;
+	} else
+		ata_dev_printk(dev, KERN_DEBUG,
+			       "head park not requested, used standby\n");
+
+out:
+	return 0;
+
+abort:
+	ata_dev_printk(dev, KERN_DEBUG, "head NOT parked\n");
+	spin_lock_irqsave(ap->lock, flags);
+	dev->flags &= ~ATA_DFLAG_PROTECTED;
+	spin_unlock_irqrestore(ap->lock, flags);
+	return -EIO;
+}
+
+/**
  *	ata_dev_init - Initialize an ata_device structure
  *	@dev: Device structure to initialize
  *
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 21a81cd..edb92dc 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -2584,6 +2584,15 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
 			ata_link_for_each_dev(dev, link)
 				ata_dev_enable_pm(dev, ap->pm_policy);
 
+		ata_link_for_each_dev(dev, link)
+			if (ehc->i.dev_action[dev->devno] & ATA_EH_PROTECT) {
+				ata_eh_about_to_do(link, dev, ATA_EH_PROTECT);
+				rc = ata_protect_dev(dev);
+				if (rc)
+					goto dev_fail;
+				ata_eh_done(link, dev, ATA_EH_PROTECT);
+			}
+
 		/* this link is okay now */
 		ehc->i.flags = 0;
 		continue;
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 14daf48..d0fd762 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -856,6 +856,34 @@ static void ata_scsi_dev_config(struct scsi_device *sdev,
 	}
 }
 
+static void ata_scsi_protect_dev(struct ata_device *dev)
+{
+	struct ata_link *link = dev->link;
+	struct ata_eh_info *ehi = &link->eh_info;
+
+	if (dev->flags & ATA_DFLAG_PROTECTED)
+		return;
+
+	dev->flags |= ATA_DFLAG_PROTECTED;
+	ehi->dev_action[dev->devno] |= ATA_EH_PROTECT;
+	ehi->flags |= ATA_EHI_QUIET;
+	ata_port_schedule_eh(link->ap);
+}
+
+static void ata_scsi_unprotect_dev(struct ata_device *dev)
+{
+	struct ata_link *link = dev->link;
+	struct ata_eh_info *ehi = &link->eh_info;
+
+	if (!(dev->flags & ATA_DFLAG_PROTECTED))
+		return;
+	dev->flags &= ~ATA_DFLAG_PROTECTED;
+
+	ehi->dev_action[dev->devno] |= ATA_EH_REVALIDATE;
+	ehi->flags |= ATA_EHI_QUIET;
+	ata_port_schedule_eh(link->ap);
+}
+
 /**
  *	ata_scsi_slave_config - Set SCSI device attributes
  *	@sdev: SCSI device to examine
@@ -1517,6 +1545,9 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
 
 	VPRINTK("ENTER\n");
 
+	if (unlikely(dev->flags & ATA_DFLAG_PROTECTED))
+		return SCSI_MLQUEUE_DEVICE_BUSY;
+
 	qc = ata_scsi_qc_new(dev, cmd, done);
 	if (!qc)
 		goto err_mem;
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index bbe59c2..807cf2f 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -95,6 +95,7 @@ extern void ata_dev_select(struct ata_port *ap, unsigned int device,
                            unsigned int wait, unsigned int can_sleep);
 extern void swap_buf_le16(u16 *buf, unsigned int buf_words);
 extern int ata_flush_cache(struct ata_device *dev);
+extern int ata_protect_dev(struct ata_device *dev);
 extern void ata_dev_init(struct ata_device *dev);
 extern void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp);
 extern int sata_link_init_spd(struct ata_link *link);
diff --git a/include/linux/ata.h b/include/linux/ata.h
index e672e80..aa16cbb 100644
--- a/include/linux/ata.h
+++ b/include/linux/ata.h
@@ -395,6 +395,18 @@ struct ata_taskfile {
 
 #define ata_id_cdb_intr(id)	(((id)[0] & 0x60) == 0x20)
 
+static inline int ata_id_has_unload(const u16 *id)
+{
+	/* ATA-7 specifies two places to indicate unload feature support.
+	 * Since I don't really understand the difference, I'll just check
+	 * both and only return zero if none of them indicates otherwise. */
+	if ((id[84] & 0xC000) == 0x4000 && id[84] & (1 << 13))
+		return id[84] & (1 << 13);
+	if ((id[87] & 0xC000) == 0x4000)
+		return id[87] & (1 << 13);
+	return 0;
+}
+
 static inline bool ata_id_has_hipm(const u16 *id)
 {
 	u16 val = id[76];
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 124033c..2db23c3 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -147,6 +147,7 @@ enum {
 
 	ATA_DFLAG_DETACH	= (1 << 16),
 	ATA_DFLAG_DETACHED	= (1 << 17),
+	ATA_DFLAG_PROTECTED	= (1 << 18),
 
 	ATA_DEV_UNKNOWN		= 0,	/* unknown device */
 	ATA_DEV_ATA		= 1,	/* ATA device */
@@ -299,9 +300,10 @@ enum {
 	ATA_EH_SOFTRESET	= (1 << 1),
 	ATA_EH_HARDRESET	= (1 << 2),
 	ATA_EH_ENABLE_LINK	= (1 << 3),
+	ATA_EH_PROTECT		= (1 << 4),
 
 	ATA_EH_RESET_MASK	= ATA_EH_SOFTRESET | ATA_EH_HARDRESET,
-	ATA_EH_PERDEV_MASK	= ATA_EH_REVALIDATE,
+	ATA_EH_PERDEV_MASK	= ATA_EH_REVALIDATE | ATA_EH_PROTECT,
 
 	/* ata_eh_info->flags */
 	ATA_EHI_HOTPLUGGED	= (1 << 0),  /* could have been hotplugged */



  reply	other threads:[~2008-03-07 18:25 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-02-25 23:56 [RFC] Disk shock protection (revisited) Elias Oltmanns
2008-02-26  0:02 ` Jeff Garzik
2008-02-26  0:30   ` Elias Oltmanns
2008-02-26  1:33     ` Henrique de Moraes Holschuh
2008-02-26 12:39 ` Alan Cox
2008-02-28  8:24   ` Elias Oltmanns
2008-02-28 11:13     ` Alan Cox
2008-02-24 18:03       ` Pavel Machek
2008-02-28 17:00       ` Greg Freemyer
2008-03-07 18:03       ` Elias Oltmanns
2008-03-07 18:25         ` Elias Oltmanns [this message]
2008-03-15 12:39           ` [PATCH 1/4] disk-protect: Add disk shock protection helpers to libata Pavel Machek
2008-03-20 14:13           ` Alan Cox
2008-03-07 18:25         ` [PATCH 2/4] disk-protect: SCSI support for REQ_TYPE_LINUX_BLOCK requests Elias Oltmanns
2008-03-07 18:26         ` [PATCH 3/4] disk-protect: Add a REQ_TYPE_LINUX_BLOCK request handler to libata Elias Oltmanns
2008-03-15 12:42           ` Pavel Machek
2008-03-07 18:26         ` [PATCH 4/4] disk-protect: Add a generic block layer queue freezing facility Elias Oltmanns
2008-03-15 12:49           ` Pavel Machek
2008-03-16 16:16             ` Elias Oltmanns
2008-03-17 23:00               ` Elias Oltmanns
2008-03-07 22:43         ` [RFC] Disk shock protection (revisited) Alan Cox
2008-03-13 14:51         ` Elias Oltmanns
2008-03-15 14:30           ` Alan Cox
2008-02-26 20:47 ` Willy Tarreau
2008-02-28 10:10   ` Elias Oltmanns

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=20080307181750.9981.88028.stgit@denkblock.local \
    --to=eo@nebensachen.de \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=jens.axboe@oracle.com \
    --cc=linux-ide@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /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).