Skip to contentSkip to conversation

libata: check for AN support

PATCH New Kristen Carlson Accardi 1 participant 3 messages Mar 21, 2007 1 message not in the archive

Subsystems (3)
lore
Share & tools
lore
#1 lore raw

PATCH 1/3 libata: check for AN support

In reply to a message not in the archive

Check to see if an ATAPI device supports Asynchronous Notification. If so, enable it.

drivers/ata/libata-core.c +54 -0
Index: 2.6-git/drivers/ata/libata-core.c
===================================================================
@@ -67,6 +67,7 @@
 static unsigned int ata_dev_init_params(struct ata_device *dev,
 					u16 heads, u16 sectors);
 static unsigned int ata_dev_set_xfermode(struct ata_device *dev);
+static unsigned int ata_dev_set_an(struct ata_device *dev);
 static void ata_dev_xfermask(struct ata_device *dev);
 
 static unsigned int ata_unique_id = 1;
@@ -1739,6 +1740,23 @@
 		}
 		dev->cdb_len = (unsigned int) rc;
 
+		/*
+		 * check to see if this ATAPI device supports
+		 * Asynchronous Notification
+		 */
+		if (ata_id_has_an(id))
+		{
+			/* issue SET feature command to turn this on */
+			rc = ata_dev_set_an(dev);
+			if (rc) {
+				ata_dev_printk(dev, KERN_ERR,
+						"unable to set AN\n");
+				rc = -EINVAL;
+				goto err_out_nosup;
+			}
+			dev->flags |= ATA_DFLAG_AN;
+		}
+
 		if (ata_id_cdb_intr(dev->id)) {
 			dev->flags |= ATA_DFLAG_CDB_INTR;
 			cdb_intr_string = ", CDB intr";
@@ -3479,6 +3497,42 @@
 }
 
 /**
+ *	ata_dev_set_an - Issue SET FEATURES - SATA FEATURES
+ *                       with sector count set to indicate
+ *                       Asynchronous Notification feature
+ *	@dev: Device to which command will be sent
+ *
+ *	Issue SET FEATURES - SATA FEATURES command to device @dev
+ *	on port @ap.
+ *
+ *	LOCKING:
+ *	PCI/etc. bus probe sem.
+ *
+ *	RETURNS:
+ *	0 on success, AC_ERR_* mask otherwise.
+ */
+static unsigned int ata_dev_set_an(struct ata_device *dev)
+{
+	struct ata_taskfile tf;
+	unsigned int err_mask;
+
+	/* set up set-features taskfile */
+	DPRINTK("set features - SATA features\n");
+
+	ata_tf_init(dev, &tf);
+	tf.command = ATA_CMD_SET_FEATURES;
+	tf.feature = SETFEATURES_SATA_ENABLE;
+	tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
+	tf.protocol = ATA_PROT_NODATA;
+	tf.nsect = SATA_AN;
+
+	err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+
+	DPRINTK("EXIT, err_mask=%x\n", err_mask);
+	return err_mask;
+}
+
+/**
  *	ata_dev_init_params - Issue INIT DEV PARAMS command
  *	@dev: Device to which command will be sent
  *	@heads: Number of heads (taskfile parameter)
include/linux/ata.h +8 -0
Index: 2.6-git/include/linux/ata.h
===================================================================
@@ -190,6 +190,12 @@
 	SETFEATURES_WC_ON	= 0x02, /* Enable write cache */
 	SETFEATURES_WC_OFF	= 0x82, /* Disable write cache */
 
+	SETFEATURES_SATA_ENABLE = 0x10, /* Enable use of SATA feature */
+	SETFEATURES_SATA_DISABLE = 0x90, /* Disable use of SATA feature */
+
+	/* SETFEATURE Sector counts for SATA features */
+	SATA_AN			= 0x05,  /* Asynchronous Notification */
+
 	/* ATAPI stuff */
 	ATAPI_PKT_DMA		= (1 << 0),
 	ATAPI_DMADIR		= (1 << 2),	/* ATAPI data dir:
@@ -296,6 +302,8 @@
 #define ata_id_queue_depth(id)	(((id)[75] & 0x1f) + 1)
 #define ata_id_removeable(id)	((id)[0] & (1 << 7))
 #define ata_id_has_dword_io(id)	((id)[50] & (1 << 0))
+#define ata_id_has_an(id)	\
+	((id[76] && (~id[76])) & ((id)[78] & (1 << 5)))
 #define ata_id_u32(id,n)	\
 	(((u32) (id)[(n) + 1] << 16) | ((u32) (id)[(n)]))
 #define ata_id_u64(id,n)	\
include/linux/libata.h +1 -0
Index: 2.6-git/include/linux/libata.h
===================================================================
@@ -141,6 +141,7 @@
 	ATA_DFLAG_CDB_INTR	= (1 << 2), /* device asserts INTRQ when ready for CDB */
 	ATA_DFLAG_NCQ		= (1 << 3), /* device supports NCQ */
 	ATA_DFLAG_FLUSH_EXT	= (1 << 4), /* do FLUSH_EXT instead of FLUSH */
+	ATA_DFLAG_AN		= (1 << 5), /* device supports Async notification */
 	ATA_DFLAG_CFG_MASK	= (1 << 8) - 1,
 
 	ATA_DFLAG_PIO		= (1 << 8), /* device limited to PIO mode */

Check to see if an ATAPI device supports Asynchronous Notification. If so, enable it.

drivers/ata/libata-core.c +54 -0

72 lines not on this page · raw message

include/linux/ata.h +8 -0
Index: 2.6-git/include/linux/ata.h
===================================================================
@@ -190,6 +190,12 @@
 	SETFEATURES_WC_ON	= 0x02, /* Enable write cache */
 	SETFEATURES_WC_OFF	= 0x82, /* Disable write cache */
 
+	SETFEATURES_SATA_ENABLE = 0x10, /* Enable use of SATA feature */
+	SETFEATURES_SATA_DISABLE = 0x90, /* Disable use of SATA feature */
+
+	/* SETFEATURE Sector counts for SATA features */
+	SATA_AN			= 0x05,  /* Asynchronous Notification */
+
 	/* ATAPI stuff */
 	ATAPI_PKT_DMA		= (1 << 0),
 	ATAPI_DMADIR		= (1 << 2),	/* ATAPI data dir:
@@ -296,6 +302,8 @@
 #define ata_id_queue_depth(id)	(((id)[75] & 0x1f) + 1)
 #define ata_id_removeable(id)	((id)[0] & (1 << 7))
 #define ata_id_has_dword_io(id)	((id)[50] & (1 << 0))
+#define ata_id_has_an(id)	\
+	((id[76] && (~id[76])) & ((id)[78] & (1 << 5)))
 #define ata_id_u32(id,n)	\
 	(((u32) (id)[(n) + 1] << 16) | ((u32) (id)[(n)]))
 #define ata_id_u64(id,n)	\
include/linux/libata.h +1 -0
Index: 2.6-git/include/linux/libata.h
===================================================================
@@ -141,6 +141,7 @@
 	ATA_DFLAG_CDB_INTR	= (1 << 2), /* device asserts INTRQ when ready for CDB */
 	ATA_DFLAG_NCQ		= (1 << 3), /* device supports NCQ */
 	ATA_DFLAG_FLUSH_EXT	= (1 << 4), /* do FLUSH_EXT instead of FLUSH */
+	ATA_DFLAG_AN		= (1 << 5), /* device supports Async notification */
 	ATA_DFLAG_CFG_MASK	= (1 << 8) - 1,
 
 	ATA_DFLAG_PIO		= (1 << 8), /* device limited to PIO mode */
#2 lore raw

PATCH 2/3 libata: expose AN support to user space via sysfs

In reply to a message not in the archive

Allow user space to determine if an ATAPI device supports async notification (AN) of media changes. This is done by adding a new sysfs file "async_notification" to genhd. If the file reads 1, then the device supports async notification. If the file reads 0, it does not.

A flag is set in the generic disk to indicate whether or not AN is supported. This flag is set by the SCSI subsystem when it registers with add_disk. The SCSI system gets information from libata on whether the device supports AN during dev_configure.

block/genhd.c +10 -0
Index: 2.6-git/block/genhd.c
===================================================================
@@ -369,6 +369,11 @@
 {
 	return sprintf(page, "%llu\n", (unsigned long long)get_capacity(disk));
 }
+static ssize_t disk_an_read(struct gendisk * disk, char *page)
+{
+	return sprintf(page, "%d\n",
+		(disk->flags & GENHD_FL_ASYNC_NOTIFICATION ? 1 : 0));
+}
 
 static ssize_t disk_stats_read(struct gendisk * disk, char *page)
 {
@@ -416,6 +421,10 @@
 	.attr = {.name = "stat", .mode = S_IRUGO },
 	.show	= disk_stats_read
 };
+static struct disk_attribute disk_attr_an = {
+	.attr = {.name = "async_notification", .mode = S_IRUGO },
+	.show	= disk_an_read
+};
 
 #ifdef CONFIG_FAIL_MAKE_REQUEST
 
@@ -452,6 +461,7 @@
 	&disk_attr_removable.attr,
 	&disk_attr_size.attr,
 	&disk_attr_stat.attr,
+	&disk_attr_an.attr,
 #ifdef CONFIG_FAIL_MAKE_REQUEST
 	&disk_attr_fail.attr,
 #endif
include/linux/genhd.h +1 -0
Index: 2.6-git/include/linux/genhd.h
===================================================================
@@ -90,6 +90,7 @@
 
 #define GENHD_FL_REMOVABLE			1
 #define GENHD_FL_DRIVERFS			2
+#define GENHD_FL_ASYNC_NOTIFICATION		4
 #define GENHD_FL_CD				8
 #define GENHD_FL_UP				16
 #define GENHD_FL_SUPPRESS_PARTITION_INFO	32
include/scsi/scsi_device.h +1 -1
Index: 2.6-git/include/scsi/scsi_device.h
===================================================================
@@ -123,7 +123,7 @@
 	unsigned select_no_atn:1;
 	unsigned fix_capacity:1;	/* READ_CAPACITY is too high by 1 */
 	unsigned retry_hwerror:1;	/* Retry HARDWARE_ERROR */
-
+	unsigned async_notification:1;	/* device supports async notification */
 	unsigned int device_blocked;	/* Device returned QUEUE_FULL. */
 
 	unsigned int max_device_blocked; /* what device_blocked counts down from  */
drivers/ata/libata-scsi.c +3 -0
Index: 2.6-git/drivers/ata/libata-scsi.c
===================================================================
@@ -855,6 +855,9 @@
 		blk_queue_max_hw_segments(q, q->max_hw_segments - 1);
 	}
 
+	if (dev->flags & ATA_DFLAG_AN)
+		sdev->async_notification = 1;
+
 	if (dev->flags & ATA_DFLAG_NCQ) {
 		int depth;
 
drivers/scsi/sr.c +2 -0
Index: 2.6-git/drivers/scsi/sr.c
===================================================================
@@ -602,6 +602,8 @@
 
 	dev_set_drvdata(dev, cd);
 	disk->flags |= GENHD_FL_REMOVABLE;
+	if (sdev->async_notification)
+		disk->flags |= GENHD_FL_ASYNC_NOTIFICATION;
 	add_disk(disk);
 
 	sdev_printk(KERN_DEBUG, sdev,

Allow user space to determine if an ATAPI device supports async notification (AN) of media changes. This is done by adding a new sysfs file "async_notification" to genhd. If the file reads 1, then the device supports async notification. If the file reads 0, it does not.

A flag is set in the generic disk to indicate whether or not AN is supported. This flag is set by the SCSI subsystem when it registers with add_disk. The SCSI system gets information from libata on whether the device supports AN during dev_configure.

block/genhd.c +10 -0
Index: 2.6-git/block/genhd.c
===================================================================
@@ -369,6 +369,11 @@
 {
 	return sprintf(page, "%llu\n", (unsigned long long)get_capacity(disk));
 }
+static ssize_t disk_an_read(struct gendisk * disk, char *page)
+{
+	return sprintf(page, "%d\n",
+		(disk->flags & GENHD_FL_ASYNC_NOTIFICATION ? 1 : 0));
+}
 
 static ssize_t disk_stats_read(struct gendisk * disk, char *page)
 {
@@ -416,6 +421,10 @@
 	.attr = {.name = "stat", .mode = S_IRUGO },
 	.show	= disk_stats_read
 };
+static struct disk_attribute disk_attr_an = {
+	.attr = {.name = "async_notification", .mode = S_IRUGO },
+	.show	= disk_an_read
+};
 
 #ifdef CONFIG_FAIL_MAKE_REQUEST
 
@@ -452,6 +461,7 @@
 	&disk_attr_removable.attr,
 	&disk_attr_size.attr,
 	&disk_attr_stat.attr,
+	&disk_attr_an.attr,
 #ifdef CONFIG_FAIL_MAKE_REQUEST
 	&disk_attr_fail.attr,
 #endif
include/linux/genhd.h +1 -0
Index: 2.6-git/include/linux/genhd.h
===================================================================
@@ -90,6 +90,7 @@
 
 #define GENHD_FL_REMOVABLE			1
 #define GENHD_FL_DRIVERFS			2
+#define GENHD_FL_ASYNC_NOTIFICATION		4
 #define GENHD_FL_CD				8
 #define GENHD_FL_UP				16
 #define GENHD_FL_SUPPRESS_PARTITION_INFO	32
include/scsi/scsi_device.h +1 -1
Index: 2.6-git/include/scsi/scsi_device.h
===================================================================
@@ -123,7 +123,7 @@
 	unsigned select_no_atn:1;
 	unsigned fix_capacity:1;	/* READ_CAPACITY is too high by 1 */
 	unsigned retry_hwerror:1;	/* Retry HARDWARE_ERROR */
-
+	unsigned async_notification:1;	/* device supports async notification */
 	unsigned int device_blocked;	/* Device returned QUEUE_FULL. */
 
 	unsigned int max_device_blocked; /* what device_blocked counts down from  */
drivers/ata/libata-scsi.c +3 -0
Index: 2.6-git/drivers/ata/libata-scsi.c
===================================================================
@@ -855,6 +855,9 @@
 		blk_queue_max_hw_segments(q, q->max_hw_segments - 1);
 	}
 
+	if (dev->flags & ATA_DFLAG_AN)
+		sdev->async_notification = 1;
+
 	if (dev->flags & ATA_DFLAG_NCQ) {
 		int depth;
 
drivers/scsi/sr.c +2 -0
Index: 2.6-git/drivers/scsi/sr.c
===================================================================
@@ -602,6 +602,8 @@
 
 	dev_set_drvdata(dev, cd);
 	disk->flags |= GENHD_FL_REMOVABLE;
+	if (sdev->async_notification)
+		disk->flags |= GENHD_FL_ASYNC_NOTIFICATION;
 	add_disk(disk);
 
 	sdev_printk(KERN_DEBUG, sdev,
#3 lore raw

PATCH 3/3 libata: handle AN interrupt

In reply to a message not in the archive

When we get an SDB FIS with the 'N' bit set, we should send an event to user space to indicate that there has been a media change. The ahci host controller will send the event via KOBJ_CHANGE uevent.

drivers/ata/ahci.c +20 -0
Index: 2.6-git/drivers/ata/ahci.c
===================================================================
@@ -1126,6 +1126,26 @@
 		return;
 	}
 
+	if (status & PORT_IRQ_SDB_FIS) {
+		/*
+		 * if this is an ATAPI device with AN turned on,
+		 * then we should interrogate the device to
+		 * determine the cause of the interrupt
+		 *
+		 * for AN - this we should check the SDB FIS
+		 * and find the I and N bits set
+		 */
+		const u32 *f = pp->rx_fis + RX_FIS_SDB;
+
+		/* check the 'N' bit in word 0 of the FIS */
+		if (f[0] & (1 << 15)) {
+			int port_addr =  ((f[0] & 0x00000f00) >> 8);
+			struct ata_device *adev = &ap->device[port_addr];
+			ata_port_printk(ap, KERN_INFO, "N bit set on SDB FIS!\n");
+			if (adev->flags & ATA_DFLAG_AN)
+				ata_async_notify(adev);
+		}
+	}
 	if (ap->sactive)
 		qc_active = readl(port_mmio + PORT_SCR_ACT);
 	else
include/linux/libata.h +2 -0
Index: 2.6-git/include/linux/libata.h
===================================================================
@@ -504,6 +504,7 @@
 	/* error history */
 	struct ata_ering	ering;
 	unsigned int		horkage;	/* List of broken features */
+	struct work_struct	async_notify;
 };
 
 /* Offset into struct ata_device.  Fields above it are maintained
@@ -827,6 +828,7 @@
 extern int ata_scsi_change_queue_depth(struct scsi_device *sdev,
 				       int queue_depth);
 extern struct ata_device *ata_dev_pair(struct ata_device *adev);
+extern void ata_async_notify(struct ata_device *atadev);
 
 /*
  * Timing helpers
drivers/ata/libata-core.c +22 -0
Index: 2.6-git/drivers/ata/libata-core.c
===================================================================
@@ -1589,6 +1589,26 @@
 	}
 }
 
+static void async_notify_thread(struct work_struct *work)
+{
+	struct ata_device *atadev =
+		container_of(work, struct ata_device, async_notify);
+
+	/*
+	 * TBD - who should send this event?  I couldn't find an
+	 * easy way to map an ata_device to a genhd device, so
+	 * decided maybe the ata host should send the event and
+	 * allow user space to figure out what happened?
+	 */
+	kobject_uevent(&atadev->ap->host->dev->kobj, KOBJ_CHANGE);
+}
+
+void ata_async_notify(struct ata_device *atadev)
+{
+	schedule_work(&atadev->async_notify);
+}
+
+
 /**
  *	ata_dev_configure - Configure the specified ATA/ATAPI device
  *	@dev: Target device to configure
@@ -1755,6 +1775,7 @@
 				goto err_out_nosup;
 			}
 			dev->flags |= ATA_DFLAG_AN;
+			INIT_WORK(&dev->async_notify, async_notify_thread);
 		}
 
 		if (ata_id_cdb_intr(dev->id)) {
@@ -6545,3 +6566,4 @@
 EXPORT_SYMBOL_GPL(ata_eh_qc_complete);
 EXPORT_SYMBOL_GPL(ata_eh_qc_retry);
 EXPORT_SYMBOL_GPL(ata_do_eh);
+EXPORT_SYMBOL_GPL(ata_async_notify);

When we get an SDB FIS with the 'N' bit set, we should send an event to user space to indicate that there has been a media change. The ahci host controller will send the event via KOBJ_CHANGE uevent.

drivers/ata/ahci.c +20 -0
Index: 2.6-git/drivers/ata/ahci.c
===================================================================
@@ -1126,6 +1126,26 @@
 		return;
 	}
 
+	if (status & PORT_IRQ_SDB_FIS) {
+		/*
+		 * if this is an ATAPI device with AN turned on,
+		 * then we should interrogate the device to
+		 * determine the cause of the interrupt
+		 *
+		 * for AN - this we should check the SDB FIS
+		 * and find the I and N bits set
+		 */
+		const u32 *f = pp->rx_fis + RX_FIS_SDB;
+
+		/* check the 'N' bit in word 0 of the FIS */
+		if (f[0] & (1 << 15)) {
+			int port_addr =  ((f[0] & 0x00000f00) >> 8);
+			struct ata_device *adev = &ap->device[port_addr];
+			ata_port_printk(ap, KERN_INFO, "N bit set on SDB FIS!\n");
+			if (adev->flags & ATA_DFLAG_AN)
+				ata_async_notify(adev);
+		}
+	}
 	if (ap->sactive)
 		qc_active = readl(port_mmio + PORT_SCR_ACT);
 	else
include/linux/libata.h +2 -0
Index: 2.6-git/include/linux/libata.h
===================================================================
@@ -504,6 +504,7 @@
 	/* error history */
 	struct ata_ering	ering;
 	unsigned int		horkage;	/* List of broken features */
+	struct work_struct	async_notify;
 };
 
 /* Offset into struct ata_device.  Fields above it are maintained
@@ -827,6 +828,7 @@
 extern int ata_scsi_change_queue_depth(struct scsi_device *sdev,
 				       int queue_depth);
 extern struct ata_device *ata_dev_pair(struct ata_device *adev);
+extern void ata_async_notify(struct ata_device *atadev);
 
 /*
  * Timing helpers
drivers/ata/libata-core.c +22 -0
Index: 2.6-git/drivers/ata/libata-core.c
===================================================================
@@ -1589,6 +1589,26 @@
 	}
 }
 
+static void async_notify_thread(struct work_struct *work)
+{
+	struct ata_device *atadev =
+		container_of(work, struct ata_device, async_notify);
+
+	/*
+	 * TBD - who should send this event?  I couldn't find an
+	 * easy way to map an ata_device to a genhd device, so
+	 * decided maybe the ata host should send the event and
+	 * allow user space to figure out what happened?
+	 */
+	kobject_uevent(&atadev->ap->host->dev->kobj, KOBJ_CHANGE);
+}
+
+void ata_async_notify(struct ata_device *atadev)
+{
+	schedule_work(&atadev->async_notify);
+}
+
+
 /**
  *	ata_dev_configure - Configure the specified ATA/ATAPI device
  *	@dev: Target device to configure
@@ -1755,6 +1775,7 @@
 				goto err_out_nosup;
 			}
 			dev->flags |= ATA_DFLAG_AN;
+			INIT_WORK(&dev->async_notify, async_notify_thread);
 		}
 
 		if (ata_id_cdb_intr(dev->id)) {
@@ -6545,3 +6566,4 @@
 EXPORT_SYMBOL_GPL(ata_eh_qc_complete);
 EXPORT_SYMBOL_GPL(ata_eh_qc_retry);
 EXPORT_SYMBOL_GPL(ata_do_eh);
+EXPORT_SYMBOL_GPL(ata_async_notify);