瀏覽代碼

libata: clear dev->ering in smarter way

dev->ering used to be cleared together with the rest of ata_device in
ata_dev_init() which is called whenever a probing event occurs.
dev->ering is about to be used to track probing failures so it needs
to remain persistent over multiple porbing events.  This patch
achieves this by doing the following.

* Instead of CLEAR_OFFSET, define CLEAR_BEGIN and CLEAR_END and only
  clear between BEGIN and END.  ering is moved after END.  The split
  of persistent area is to allow hotter items remain at the head.

* ering is explicitly cleared on ata_dev_disable() and when device
  attach succeeds.  So, ering is persistent throug a device's life
  time (unless explicitly cleared of course) and also through periods
  inbetween disablement of an attached device and successful detection
  of the next one.

Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Tejun Heo 16 年之前
父節點
當前提交
99cf610aa4
共有 3 個文件被更改,包括 19 次插入10 次删除
  1. 2 2
      drivers/ata/libata-core.c
  2. 7 0
      drivers/ata/libata-eh.c
  3. 10 8
      include/linux/libata.h

+ 2 - 2
drivers/ata/libata-core.c

@@ -5404,8 +5404,8 @@ void ata_dev_init(struct ata_device *dev)
 	dev->horkage = 0;
 	dev->horkage = 0;
 	spin_unlock_irqrestore(ap->lock, flags);
 	spin_unlock_irqrestore(ap->lock, flags);
 
 
-	memset((void *)dev + ATA_DEVICE_CLEAR_OFFSET, 0,
-	       sizeof(*dev) - ATA_DEVICE_CLEAR_OFFSET);
+	memset((void *)dev + ATA_DEVICE_CLEAR_BEGIN, 0,
+	       ATA_DEVICE_CLEAR_END - ATA_DEVICE_CLEAR_BEGIN);
 	dev->pio_mask = UINT_MAX;
 	dev->pio_mask = UINT_MAX;
 	dev->mwdma_mask = UINT_MAX;
 	dev->mwdma_mask = UINT_MAX;
 	dev->udma_mask = UINT_MAX;
 	dev->udma_mask = UINT_MAX;

+ 7 - 0
drivers/ata/libata-eh.c

@@ -1194,6 +1194,11 @@ void ata_dev_disable(struct ata_device *dev)
 	ata_acpi_on_disable(dev);
 	ata_acpi_on_disable(dev);
 	ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO0 | ATA_DNXFER_QUIET);
 	ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO0 | ATA_DNXFER_QUIET);
 	dev->class++;
 	dev->class++;
+
+	/* From now till the next successful probe, ering is used to
+	 * track probe failures.  Clear accumulated device error info.
+	 */
+	ata_ering_clear(&dev->ering);
 }
 }
 
 
 /**
 /**
@@ -2765,6 +2770,8 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
 						     readid_flags, dev->id);
 						     readid_flags, dev->id);
 			switch (rc) {
 			switch (rc) {
 			case 0:
 			case 0:
+				/* clear error info accumulated during probe */
+				ata_ering_clear(&dev->ering);
 				new_mask |= 1 << dev->devno;
 				new_mask |= 1 << dev->devno;
 				break;
 				break;
 			case -ENOENT:
 			case -ENOENT:

+ 10 - 8
include/linux/libata.h

@@ -580,7 +580,7 @@ struct ata_device {
 	acpi_handle		acpi_handle;
 	acpi_handle		acpi_handle;
 	union acpi_object	*gtf_cache;
 	union acpi_object	*gtf_cache;
 #endif
 #endif
-	/* n_sector is used as CLEAR_OFFSET, read comment above CLEAR_OFFSET */
+	/* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */
 	u64			n_sectors;	/* size of device, if ATA */
 	u64			n_sectors;	/* size of device, if ATA */
 	unsigned int		class;		/* ATA_DEV_xxx */
 	unsigned int		class;		/* ATA_DEV_xxx */
 	unsigned long		unpark_deadline;
 	unsigned long		unpark_deadline;
@@ -605,20 +605,22 @@ struct ata_device {
 	u16			heads;		/* Number of heads */
 	u16			heads;		/* Number of heads */
 	u16			sectors;	/* Number of sectors per track */
 	u16			sectors;	/* Number of sectors per track */
 
 
-	/* error history */
-	int			spdn_cnt;
-	struct ata_ering	ering;
-
 	union {
 	union {
 		u16		id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */
 		u16		id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */
 		u32		gscr[SATA_PMP_GSCR_DWORDS]; /* PMP GSCR block */
 		u32		gscr[SATA_PMP_GSCR_DWORDS]; /* PMP GSCR block */
 	};
 	};
+
+	/* error history */
+	int			spdn_cnt;
+	/* ering is CLEAR_END, read comment above CLEAR_END */
+	struct ata_ering	ering;
 };
 };
 
 
-/* Offset into struct ata_device.  Fields above it are maintained
- * acress device init.  Fields below are zeroed.
+/* Fields between ATA_DEVICE_CLEAR_BEGIN and ATA_DEVICE_CLEAR_END are
+ * cleared to zero on ata_dev_init().
  */
  */
-#define ATA_DEVICE_CLEAR_OFFSET		offsetof(struct ata_device, n_sectors)
+#define ATA_DEVICE_CLEAR_BEGIN		offsetof(struct ata_device, n_sectors)
+#define ATA_DEVICE_CLEAR_END		offsetof(struct ata_device, ering)
 
 
 struct ata_eh_info {
 struct ata_eh_info {
 	struct ata_device	*dev;		/* offending device */
 	struct ata_device	*dev;		/* offending device */