Commit e4f7bdc2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull libata ZAC support from Tejun Heo:
 "This contains Zone ATA Command support for Shingled Magnetic Recording
  devices.

  In addition to sending the new commands down to the device, as ZAC
  commands depend on getting a lot of responses from the device, piping
  up responses is beefed up too.  However, it doesn't involve changes to
  libata core mechanism or its interaction with upper layers, so I'm not
  expecting too many fallouts.

  Kudos to Hannes for driving SMR support"

* 'for-4.7-zac' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata: (28 commits)
  libata: support host-aware and host-managed ZAC devices
  libata: support device-managed ZAC devices
  libata: NCQ encapsulation for ZAC MANAGEMENT OUT
  libata: Implement ZBC OUT translation
  libata: implement ZBC IN translation
  libata: fixup ZAC device disabling
  libata-scsi: Generate sense code for disabled devices
  libata-trace: decode subcommands
  libata: Check log page directory before accessing pages
  libata: Add command definitions for NCQ Encapsulation for READ LOG DMA EXT
  libata: Separate out ata_dev_config_ncq_send_recv()
  libata/libsas: Define ATA_CMD_NCQ_NON_DATA
  libsas: enable FPDMA SEND/RECEIVE
  libata: do not attempt to retrieve sense code twice
  libata-scsi: Set information sense field for invalid parameter
  libata-scsi: set bit pointer for sense code information
  libata-scsi: Set field pointer in sense code
  scsi: add scsi_set_sense_field_pointer()
  libata: Implement control mode page to select sense format
  libata-scsi: generate correct ATA pass-through sense
  ...
parents 3159ee58 6d1003ae
Loading
Loading
Loading
Loading
+194 −21
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@
#include <scsi/scsi_host.h>
#include <linux/libata.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
#include <linux/cdrom.h>
#include <linux/ratelimit.h>
#include <linux/pm_runtime.h>
@@ -695,7 +696,7 @@ static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev)
 *	RETURNS:
 *	Block address read from @tf.
 */
u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev)
u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev)
{
	u64 block = 0;

@@ -720,7 +721,7 @@ u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev)
		if (!sect) {
			ata_dev_warn(dev,
				     "device reported invalid CHS sector 0\n");
			sect = 1; /* oh well */
			return U64_MAX;
		}

		block = (cyl * dev->heads + head) * dev->sectors + sect - 1;
@@ -2079,6 +2080,81 @@ static inline u8 ata_dev_knobble(struct ata_device *dev)
	return ((ap->cbl == ATA_CBL_SATA) && (!ata_id_is_sata(dev->id)));
}

static void ata_dev_config_ncq_send_recv(struct ata_device *dev)
{
	struct ata_port *ap = dev->link->ap;
	unsigned int err_mask;
	int log_index = ATA_LOG_NCQ_SEND_RECV * 2;
	u16 log_pages;

	err_mask = ata_read_log_page(dev, ATA_LOG_DIRECTORY,
				     0, ap->sector_buf, 1);
	if (err_mask) {
		ata_dev_dbg(dev,
			    "failed to get Log Directory Emask 0x%x\n",
			    err_mask);
		return;
	}
	log_pages = get_unaligned_le16(&ap->sector_buf[log_index]);
	if (!log_pages) {
		ata_dev_warn(dev,
			     "NCQ Send/Recv Log not supported\n");
		return;
	}
	err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_SEND_RECV,
				     0, ap->sector_buf, 1);
	if (err_mask) {
		ata_dev_dbg(dev,
			    "failed to get NCQ Send/Recv Log Emask 0x%x\n",
			    err_mask);
	} else {
		u8 *cmds = dev->ncq_send_recv_cmds;

		dev->flags |= ATA_DFLAG_NCQ_SEND_RECV;
		memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_SEND_RECV_SIZE);

		if (dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM) {
			ata_dev_dbg(dev, "disabling queued TRIM support\n");
			cmds[ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET] &=
				~ATA_LOG_NCQ_SEND_RECV_DSM_TRIM;
		}
	}
}

static void ata_dev_config_ncq_non_data(struct ata_device *dev)
{
	struct ata_port *ap = dev->link->ap;
	unsigned int err_mask;
	int log_index = ATA_LOG_NCQ_NON_DATA * 2;
	u16 log_pages;

	err_mask = ata_read_log_page(dev, ATA_LOG_DIRECTORY,
				     0, ap->sector_buf, 1);
	if (err_mask) {
		ata_dev_dbg(dev,
			    "failed to get Log Directory Emask 0x%x\n",
			    err_mask);
		return;
	}
	log_pages = get_unaligned_le16(&ap->sector_buf[log_index]);
	if (!log_pages) {
		ata_dev_warn(dev,
			     "NCQ Send/Recv Log not supported\n");
		return;
	}
	err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_NON_DATA,
				     0, ap->sector_buf, 1);
	if (err_mask) {
		ata_dev_dbg(dev,
			    "failed to get NCQ Non-Data Log Emask 0x%x\n",
			    err_mask);
	} else {
		u8 *cmds = dev->ncq_non_data_cmds;

		memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_NON_DATA_SIZE);
	}
}

static int ata_dev_config_ncq(struct ata_device *dev,
			       char *desc, size_t desc_sz)
{
@@ -2123,29 +2199,125 @@ static int ata_dev_config_ncq(struct ata_device *dev,
		snprintf(desc, desc_sz, "NCQ (depth %d/%d)%s", hdepth,
			ddepth, aa_desc);

	if ((ap->flags & ATA_FLAG_FPDMA_AUX) &&
	    ata_id_has_ncq_send_and_recv(dev->id)) {
		err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_SEND_RECV,
					     0, ap->sector_buf, 1);
	if ((ap->flags & ATA_FLAG_FPDMA_AUX)) {
		if (ata_id_has_ncq_send_and_recv(dev->id))
			ata_dev_config_ncq_send_recv(dev);
		if (ata_id_has_ncq_non_data(dev->id))
			ata_dev_config_ncq_non_data(dev);
	}

	return 0;
}

static void ata_dev_config_sense_reporting(struct ata_device *dev)
{
	unsigned int err_mask;

	if (!ata_id_has_sense_reporting(dev->id))
		return;

	if (ata_id_sense_reporting_enabled(dev->id))
		return;

	err_mask = ata_dev_set_feature(dev, SETFEATURE_SENSE_DATA, 0x1);
	if (err_mask) {
		ata_dev_dbg(dev,
				    "failed to get NCQ Send/Recv Log Emask 0x%x\n",
			    "failed to enable Sense Data Reporting, Emask 0x%x\n",
			    err_mask);
		} else {
			u8 *cmds = dev->ncq_send_recv_cmds;
	}
}

			dev->flags |= ATA_DFLAG_NCQ_SEND_RECV;
			memcpy(cmds, ap->sector_buf, ATA_LOG_NCQ_SEND_RECV_SIZE);
static void ata_dev_config_zac(struct ata_device *dev)
{
	struct ata_port *ap = dev->link->ap;
	unsigned int err_mask;
	u8 *identify_buf = ap->sector_buf;
	int log_index = ATA_LOG_SATA_ID_DEV_DATA * 2, i, found = 0;
	u16 log_pages;

			if (dev->horkage & ATA_HORKAGE_NO_NCQ_TRIM) {
				ata_dev_dbg(dev, "disabling queued TRIM support\n");
				cmds[ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET] &=
					~ATA_LOG_NCQ_SEND_RECV_DSM_TRIM;
	dev->zac_zones_optimal_open = U32_MAX;
	dev->zac_zones_optimal_nonseq = U32_MAX;
	dev->zac_zones_max_open = U32_MAX;

	/*
	 * Always set the 'ZAC' flag for Host-managed devices.
	 */
	if (dev->class == ATA_DEV_ZAC)
		dev->flags |= ATA_DFLAG_ZAC;
	else if (ata_id_zoned_cap(dev->id) == 0x01)
		/*
		 * Check for host-aware devices.
		 */
		dev->flags |= ATA_DFLAG_ZAC;

	if (!(dev->flags & ATA_DFLAG_ZAC))
		return;

	/*
	 * Read Log Directory to figure out if IDENTIFY DEVICE log
	 * is supported.
	 */
	err_mask = ata_read_log_page(dev, ATA_LOG_DIRECTORY,
				     0, ap->sector_buf, 1);
	if (err_mask) {
		ata_dev_info(dev,
			     "failed to get Log Directory Emask 0x%x\n",
			     err_mask);
		return;
	}
	log_pages = get_unaligned_le16(&ap->sector_buf[log_index]);
	if (log_pages == 0) {
		ata_dev_warn(dev,
			     "ATA Identify Device Log not supported\n");
		return;
	}
	/*
	 * Read IDENTIFY DEVICE data log, page 0, to figure out
	 * if page 9 is supported.
	 */
	err_mask = ata_read_log_page(dev, ATA_LOG_SATA_ID_DEV_DATA, 0,
				     identify_buf, 1);
	if (err_mask) {
		ata_dev_info(dev,
			     "failed to get Device Identify Log Emask 0x%x\n",
			     err_mask);
		return;
	}
	log_pages = identify_buf[8];
	for (i = 0; i < log_pages; i++) {
		if (identify_buf[9 + i] == ATA_LOG_ZONED_INFORMATION) {
			found++;
			break;
		}
	}
	if (!found) {
		ata_dev_warn(dev,
			     "ATA Zoned Information Log not supported\n");
		return;
	}

	return 0;
	/*
	 * Read IDENTIFY DEVICE data log, page 9 (Zoned-device information)
	 */
	err_mask = ata_read_log_page(dev, ATA_LOG_SATA_ID_DEV_DATA,
				     ATA_LOG_ZONED_INFORMATION,
				     identify_buf, 1);
	if (!err_mask) {
		u64 zoned_cap, opt_open, opt_nonseq, max_open;

		zoned_cap = get_unaligned_le64(&identify_buf[8]);
		if ((zoned_cap >> 63))
			dev->zac_zoned_cap = (zoned_cap & 1);
		opt_open = get_unaligned_le64(&identify_buf[24]);
		if ((opt_open >> 63))
			dev->zac_zones_optimal_open = (u32)opt_open;
		opt_nonseq = get_unaligned_le64(&identify_buf[32]);
		if ((opt_nonseq >> 63))
			dev->zac_zones_optimal_nonseq = (u32)opt_nonseq;
		max_open = get_unaligned_le64(&identify_buf[40]);
		if ((max_open >> 63))
			dev->zac_zones_max_open = (u32)max_open;
	}
}

/**
@@ -2370,7 +2542,8 @@ int ata_dev_configure(struct ata_device *dev)
					dev->devslp_timing[i] = sata_setting[j];
				}
		}

		ata_dev_config_sense_reporting(dev);
		ata_dev_config_zac(dev);
		dev->cdb_len = 16;
	}

+100 −13
Original line number Diff line number Diff line
@@ -1600,6 +1600,8 @@ static int ata_eh_read_log_10h(struct ata_device *dev,
	tf->hob_lbah = buf[10];
	tf->nsect = buf[12];
	tf->hob_nsect = buf[13];
	if (ata_id_has_ncq_autosense(dev->id))
		tf->auxiliary = buf[14] << 16 | buf[15] << 8 | buf[16];

	return 0;
}
@@ -1635,6 +1637,56 @@ unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key)
	return err_mask;
}

/**
 *	ata_eh_request_sense - perform REQUEST_SENSE_DATA_EXT
 *	@dev: device to perform REQUEST_SENSE_SENSE_DATA_EXT to
 *	@cmd: scsi command for which the sense code should be set
 *
 *	Perform REQUEST_SENSE_DATA_EXT after the device reported CHECK
 *	SENSE.  This function is an EH helper.
 *
 *	LOCKING:
 *	Kernel thread context (may sleep).
 */
static void ata_eh_request_sense(struct ata_queued_cmd *qc,
				 struct scsi_cmnd *cmd)
{
	struct ata_device *dev = qc->dev;
	struct ata_taskfile tf;
	unsigned int err_mask;

	if (qc->ap->pflags & ATA_PFLAG_FROZEN) {
		ata_dev_warn(dev, "sense data available but port frozen\n");
		return;
	}

	if (!cmd || qc->flags & ATA_QCFLAG_SENSE_VALID)
		return;

	if (!ata_id_sense_reporting_enabled(dev->id)) {
		ata_dev_warn(qc->dev, "sense data reporting disabled\n");
		return;
	}

	DPRINTK("ATA request sense\n");

	ata_tf_init(dev, &tf);
	tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
	tf.flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48;
	tf.command = ATA_CMD_REQ_SENSE_DATA;
	tf.protocol = ATA_PROT_NODATA;

	err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0);
	/* Ignore err_mask; ATA_ERR might be set */
	if (tf.command & ATA_SENSE) {
		ata_scsi_set_sense(dev, cmd, tf.lbah, tf.lbam, tf.lbal);
		qc->flags |= ATA_QCFLAG_SENSE_VALID;
	} else {
		ata_dev_warn(dev, "request sense failed stat %02x emask %x\n",
			     tf.command, err_mask);
	}
}

/**
 *	atapi_eh_request_sense - perform ATAPI REQUEST_SENSE
 *	@dev: device to perform REQUEST_SENSE to
@@ -1797,6 +1849,18 @@ void ata_eh_analyze_ncq_error(struct ata_link *link)
	memcpy(&qc->result_tf, &tf, sizeof(tf));
	qc->result_tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_LBA | ATA_TFLAG_LBA48;
	qc->err_mask |= AC_ERR_DEV | AC_ERR_NCQ;
	if ((qc->result_tf.command & ATA_SENSE) || qc->result_tf.auxiliary) {
		char sense_key, asc, ascq;

		sense_key = (qc->result_tf.auxiliary >> 16) & 0xff;
		asc = (qc->result_tf.auxiliary >> 8) & 0xff;
		ascq = qc->result_tf.auxiliary & 0xff;
		ata_scsi_set_sense(dev, qc->scsicmd, sense_key, asc, ascq);
		ata_scsi_set_sense_information(dev, qc->scsicmd,
					       &qc->result_tf);
		qc->flags |= ATA_QCFLAG_SENSE_VALID;
	}

	ehc->i.err_mask &= ~AC_ERR_DEV;
}

@@ -1826,14 +1890,23 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc,
		return ATA_EH_RESET;
	}

	if (stat & (ATA_ERR | ATA_DF))
	if (stat & (ATA_ERR | ATA_DF)) {
		qc->err_mask |= AC_ERR_DEV;
	else
		/*
		 * Sense data reporting does not work if the
		 * device fault bit is set.
		 */
		if (stat & ATA_DF)
			stat &= ~ATA_SENSE;
	} else {
		return 0;
	}

	switch (qc->dev->class) {
	case ATA_DEV_ATA:
	case ATA_DEV_ZAC:
		if (stat & ATA_SENSE)
			ata_eh_request_sense(qc, qc->scsicmd);
		if (err & ATA_ICRC)
			qc->err_mask |= AC_ERR_ATA_BUS;
		if (err & (ATA_UNC | ATA_AMNF))
@@ -1847,20 +1920,31 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc,
			tmp = atapi_eh_request_sense(qc->dev,
						qc->scsicmd->sense_buffer,
						qc->result_tf.feature >> 4);
			if (!tmp) {
				/* ATA_QCFLAG_SENSE_VALID is used to
				 * tell atapi_qc_complete() that sense
				 * data is already valid.
				 *
				 * TODO: interpret sense data and set
				 * appropriate err_mask.
				 */
			if (!tmp)
				qc->flags |= ATA_QCFLAG_SENSE_VALID;
			} else
			else
				qc->err_mask |= tmp;
		}
	}

	if (qc->flags & ATA_QCFLAG_SENSE_VALID) {
		int ret = scsi_check_sense(qc->scsicmd);
		/*
		 * SUCCESS here means that the sense code could
		 * evaluated and should be passed to the upper layers
		 * for correct evaluation.
		 * FAILED means the sense code could not interpreted
		 * and the device would need to be reset.
		 * NEEDS_RETRY and ADD_TO_MLQUEUE means that the
		 * command would need to be retried.
		 */
		if (ret == NEEDS_RETRY || ret == ADD_TO_MLQUEUE) {
			qc->flags |= ATA_QCFLAG_RETRY;
			qc->err_mask |= AC_ERR_OTHER;
		} else if (ret != SUCCESS) {
			qc->err_mask |= AC_ERR_HSM;
		}
	}
	if (qc->err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT | AC_ERR_ATA_BUS))
		action |= ATA_EH_RESET;

@@ -2398,6 +2482,8 @@ const char *ata_get_cmd_descript(u8 command)
		{ ATA_CMD_CFA_WRITE_MULT_NE,	"CFA WRITE MULTIPLE WITHOUT ERASE" },
		{ ATA_CMD_REQ_SENSE_DATA,	"REQUEST SENSE DATA EXT" },
		{ ATA_CMD_SANITIZE_DEVICE,	"SANITIZE DEVICE" },
		{ ATA_CMD_ZAC_MGMT_IN,		"ZAC MANAGEMENT IN" },
		{ ATA_CMD_ZAC_MGMT_OUT,		"ZAC MANAGEMENT OUT" },
		{ ATA_CMD_READ_LONG,		"READ LONG (with retries)" },
		{ ATA_CMD_READ_LONG_ONCE,	"READ LONG (without retries)" },
		{ ATA_CMD_WRITE_LONG,		"WRITE LONG (with retries)" },
@@ -2569,14 +2655,15 @@ static void ata_eh_link_report(struct ata_link *link)

#ifdef CONFIG_ATA_VERBOSE_ERROR
		if (res->command & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ |
				    ATA_ERR)) {
				    ATA_SENSE | ATA_ERR)) {
			if (res->command & ATA_BUSY)
				ata_dev_err(qc->dev, "status: { Busy }\n");
			else
				ata_dev_err(qc->dev, "status: { %s%s%s%s}\n",
				ata_dev_err(qc->dev, "status: { %s%s%s%s%s}\n",
				  res->command & ATA_DRDY ? "DRDY " : "",
				  res->command & ATA_DF ? "DF " : "",
				  res->command & ATA_DRQ ? "DRQ " : "",
				  res->command & ATA_SENSE ? "SENSE " : "",
				  res->command & ATA_ERR ? "ERR " : "");
		}

Loading