diff options
Diffstat (limited to 'drivers/hwmon/drivetemp.c')
| -rw-r--r-- | drivers/hwmon/drivetemp.c | 72 |
1 files changed, 57 insertions, 15 deletions
diff --git a/drivers/hwmon/drivetemp.c b/drivers/hwmon/drivetemp.c index 0d4f3d97ffc6..9c5b021aab86 100644 --- a/drivers/hwmon/drivetemp.c +++ b/drivers/hwmon/drivetemp.c @@ -10,7 +10,7 @@ * hwmon: Driver for SCSI/ATA temperature sensors * by Constantin Baranov <const@mimas.ru>, submitted September 2009 * - * This drive supports reporting the temperatire of SATA drives. It can be + * This drive supports reporting the temperature of SATA drives. It can be * easily extended to report the temperature of SCSI drives. * * The primary means to read drive temperatures and temperature limits @@ -102,7 +102,6 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> -#include <linux/mutex.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_driver.h> @@ -110,7 +109,6 @@ struct drivetemp_data { struct list_head list; /* list of instantiated devices */ - struct mutex lock; /* protect data buffer accesses */ struct scsi_device *sdev; /* SCSI device */ struct device *dev; /* instantiating device */ struct device *hwdev; /* hardware monitoring device */ @@ -164,7 +162,8 @@ static int drivetemp_scsi_command(struct drivetemp_data *st, u8 lba_low, u8 lba_mid, u8 lba_high) { u8 scsi_cmd[MAX_COMMAND_SIZE]; - int data_dir; + enum req_op op; + int err; memset(scsi_cmd, 0, sizeof(scsi_cmd)); scsi_cmd[0] = ATA_16; @@ -175,7 +174,7 @@ static int drivetemp_scsi_command(struct drivetemp_data *st, * field. */ scsi_cmd[2] = 0x06; - data_dir = DMA_TO_DEVICE; + op = REQ_OP_DRV_OUT; } else { scsi_cmd[1] = (4 << 1); /* PIO Data-in */ /* @@ -183,7 +182,7 @@ static int drivetemp_scsi_command(struct drivetemp_data *st, * field. */ scsi_cmd[2] = 0x0e; - data_dir = DMA_FROM_DEVICE; + op = REQ_OP_DRV_IN; } scsi_cmd[4] = feature; scsi_cmd[6] = 1; /* 1 sector */ @@ -192,9 +191,11 @@ static int drivetemp_scsi_command(struct drivetemp_data *st, scsi_cmd[12] = lba_high; scsi_cmd[14] = ata_command; - return scsi_execute_req(st->sdev, scsi_cmd, data_dir, - st->smartdata, ATA_SECT_SIZE, NULL, HZ, 5, - NULL); + err = scsi_execute_cmd(st->sdev, scsi_cmd, op, st->smartdata, + ATA_SECT_SIZE, 10 * HZ, 5, NULL); + if (err > 0) + err = -EIO; + return err; } static int drivetemp_ata_command(struct drivetemp_data *st, u8 feature, @@ -285,6 +286,42 @@ static int drivetemp_get_scttemp(struct drivetemp_data *st, u32 attr, long *val) return err; } +static const char * const sct_avoid_models[] = { +/* + * These drives will have WRITE FPDMA QUEUED command timeouts and sometimes just + * freeze until power-cycled under heavy write loads when their temperature is + * getting polled in SCT mode. The SMART mode seems to be fine, though. + * + * While only the 3 TB model (DT01ACA3) was actually caught exhibiting the + * problem let's play safe here to avoid data corruption and ban the whole + * DT01ACAx family. + + * The models from this array are prefix-matched. + */ + "TOSHIBA DT01ACA", +}; + +static bool drivetemp_sct_avoid(struct drivetemp_data *st) +{ + struct scsi_device *sdev = st->sdev; + unsigned int ctr; + + if (!sdev->model) + return false; + + /* + * The "model" field contains just the raw SCSI INQUIRY response + * "product identification" field, which has a width of 16 bytes. + * This field is space-filled, but is NOT NULL-terminated. + */ + for (ctr = 0; ctr < ARRAY_SIZE(sct_avoid_models); ctr++) + if (!strncmp(sdev->model, sct_avoid_models[ctr], + strlen(sct_avoid_models[ctr]))) + return true; + + return false; +} + static int drivetemp_identify_sata(struct drivetemp_data *st) { struct scsi_device *sdev = st->sdev; @@ -326,6 +363,13 @@ static int drivetemp_identify_sata(struct drivetemp_data *st) /* bail out if this is not a SATA device */ if (!is_ata || !is_sata) return -ENODEV; + + if (have_sct && drivetemp_sct_avoid(st)) { + dev_notice(&sdev->sdev_gendev, + "will avoid using SCT for temperature monitoring\n"); + have_sct = false; + } + if (!have_sct) goto skip_sct; @@ -416,9 +460,7 @@ static int drivetemp_read(struct device *dev, enum hwmon_sensor_types type, case hwmon_temp_input: case hwmon_temp_lowest: case hwmon_temp_highest: - mutex_lock(&st->lock); err = st->get_temp(st, attr, val); - mutex_unlock(&st->lock); break; case hwmon_temp_lcrit: *val = st->temp_lcrit; @@ -484,7 +526,7 @@ static umode_t drivetemp_is_visible(const void *data, return 0; } -static const struct hwmon_channel_info *drivetemp_info[] = { +static const struct hwmon_channel_info * const drivetemp_info[] = { HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | @@ -508,7 +550,7 @@ static const struct hwmon_chip_info drivetemp_chip_info = { * The device argument points to sdev->sdev_dev. Its parent is * sdev->sdev_gendev, which we can use to get the scsi_device pointer. */ -static int drivetemp_add(struct device *dev, struct class_interface *intf) +static int drivetemp_add(struct device *dev) { struct scsi_device *sdev = to_scsi_device(dev->parent); struct drivetemp_data *st; @@ -520,7 +562,6 @@ static int drivetemp_add(struct device *dev, struct class_interface *intf) st->sdev = sdev; st->dev = dev; - mutex_init(&st->lock); if (drivetemp_identify(st)) { err = -ENODEV; @@ -543,7 +584,7 @@ abort: return err; } -static void drivetemp_remove(struct device *dev, struct class_interface *intf) +static void drivetemp_remove(struct device *dev) { struct drivetemp_data *st, *tmp; @@ -578,3 +619,4 @@ module_exit(drivetemp_exit); MODULE_AUTHOR("Guenter Roeck <linus@roeck-us.net>"); MODULE_DESCRIPTION("Hard drive temperature monitor"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:drivetemp"); |
