Message ID | 20191209052119.32072-2-linux@roeck-us.net (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Summary: hwmon driver for temperature sensors on SATA drives | expand |
Hi, On 12/8/19 9:21 PM, Guenter Roeck wrote: > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig > index 13a6b4afb4b3..4c63eb7ba96a 100644 > --- a/drivers/hwmon/Kconfig > +++ b/drivers/hwmon/Kconfig > @@ -1346,6 +1346,16 @@ config SENSORS_RASPBERRYPI_HWMON > This driver can also be built as a module. If so, the module > will be called raspberrypi-hwmon. > > +config SENSORS_SATATEMP > + tristate "SATA hard disk drives with temperature sensors" > + depends on SCSI && ATA > + help > + If you say yes you get support for the temperature sensor on > + SATA hard disk drives. > + > + This driver can also be built as a module. If so, the module > + will be called smarttemp. Makefile seems to say satatemp. > + > config SENSORS_SHT15 > tristate "Sensiron humidity and temperature sensors. SHT15 and compat." > depends on GPIOLIB || COMPILE_TEST > diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile > index 40c036ea45e6..fe55b8f76af9 100644 > --- a/drivers/hwmon/Makefile > +++ b/drivers/hwmon/Makefile > @@ -148,6 +148,7 @@ obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o > obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o > obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o > obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o > +obj-$(CONFIG_SENSORS_SATATEMP) += satatemp.o > obj-$(CONFIG_SENSORS_SHT15) += sht15.o > obj-$(CONFIG_SENSORS_SHT21) += sht21.o > obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o
On 12/8/19 9:28 PM, Randy Dunlap wrote: > Hi, > > On 12/8/19 9:21 PM, Guenter Roeck wrote: >> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig >> index 13a6b4afb4b3..4c63eb7ba96a 100644 >> --- a/drivers/hwmon/Kconfig >> +++ b/drivers/hwmon/Kconfig >> @@ -1346,6 +1346,16 @@ config SENSORS_RASPBERRYPI_HWMON >> This driver can also be built as a module. If so, the module >> will be called raspberrypi-hwmon. >> >> +config SENSORS_SATATEMP >> + tristate "SATA hard disk drives with temperature sensors" >> + depends on SCSI && ATA >> + help >> + If you say yes you get support for the temperature sensor on >> + SATA hard disk drives. >> + >> + This driver can also be built as a module. If so, the module >> + will be called smarttemp. > > Makefile seems to say satatemp. > Oops. Thanks for the note. Will fix. Guenter >> + >> config SENSORS_SHT15 >> tristate "Sensiron humidity and temperature sensors. SHT15 and compat." >> depends on GPIOLIB || COMPILE_TEST >> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile >> index 40c036ea45e6..fe55b8f76af9 100644 >> --- a/drivers/hwmon/Makefile >> +++ b/drivers/hwmon/Makefile >> @@ -148,6 +148,7 @@ obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o >> obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o >> obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o >> obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o >> +obj-$(CONFIG_SENSORS_SATATEMP) += satatemp.o >> obj-$(CONFIG_SENSORS_SHT15) += sht15.o >> obj-$(CONFIG_SENSORS_SHT21) += sht21.o >> obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o > >
On 12/8/19 9:21 PM, Guenter Roeck wrote: > +static int satatemp_scsi_command(struct satatemp_data *st, > + u8 ata_command, u8 feature, > + u8 lba_low, u8 lba_mid, u8 lba_high) > +{ > + static u8 scsi_cmd[MAX_COMMAND_SIZE]; > + int data_dir; Declaring scsi_cmd[] static makes an otherwise thread-safe function thread-unsafe. Has it been considered to allocate scsi_cmd[] on the stack? > + /* > + * Inquiry data sanity checks (per SAT-5): > + * - peripheral qualifier must be 0 > + * - peripheral device type must be 0x0 (Direct access block device) > + * - SCSI Vendor ID is "ATA " > + */ > + if (sdev->inquiry[0] || > + strncmp(&sdev->inquiry[8], "ATA ", 8)) > + return -ENODEV; It's possible that we will need a quirk mechanism to disable temperature monitoring for certain ATA devices. Has it been considered to make scsi_add_lun() set a flag that indicates whether or not temperatures should be monitored and to check that flag from inside this function? I'm asking this because an identical strncmp() check exists in scsi_add_lun(). > +static int satatemp_read(struct device *dev, enum hwmon_sensor_types type, > + u32 attr, int channel, long *val) > +{ > + struct satatemp_data *st = dev_get_drvdata(dev); Which device does 'dev' represent? What guarantees that the drvdata won't be used for another purpose, e.g. by the SCSI core? > +/* > + * 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 satatemp_add(struct device *dev, struct class_interface *intf) > +{ > + struct scsi_device *sdev = to_scsi_device(dev->parent); > + struct satatemp_data *st; > + int err; > + > + st = kzalloc(sizeof(*st), GFP_KERNEL); > + if (!st) > + return -ENOMEM; > + > + st->sdev = sdev; > + st->dev = dev; > + mutex_init(&st->lock); > + > + if (satatemp_identify(st)) { > + err = -ENODEV; > + goto abort; > + } > + > + st->hwdev = hwmon_device_register_with_info(dev->parent, "satatemp", > + st, &satatemp_chip_info, > + NULL); > + if (IS_ERR(st->hwdev)) { > + err = PTR_ERR(st->hwdev); > + goto abort; > + } > + > + list_add(&st->list, &satatemp_devlist); > + return 0; > + > +abort: > + kfree(st); > + return err; > +} How much does synchronously submitting SCSI commands from inside the device probing call back slow down SCSI device discovery? What is the impact of this code on systems with a large number of ATA devices? Thanks, Bart.
On Mon, Dec 09, 2019 at 09:08:13AM -0800, Bart Van Assche wrote: > On 12/8/19 9:21 PM, Guenter Roeck wrote: > > +static int satatemp_scsi_command(struct satatemp_data *st, > > + u8 ata_command, u8 feature, > > + u8 lba_low, u8 lba_mid, u8 lba_high) > > +{ > > + static u8 scsi_cmd[MAX_COMMAND_SIZE]; > > + int data_dir; > > Declaring scsi_cmd[] static makes an otherwise thread-safe function > thread-unsafe. Has it been considered to allocate scsi_cmd[] on the stack? > No idea why I declared that variable 'static'. I removed it. > > + /* > > + * Inquiry data sanity checks (per SAT-5): > > + * - peripheral qualifier must be 0 > > + * - peripheral device type must be 0x0 (Direct access block device) > > + * - SCSI Vendor ID is "ATA " > > + */ > > + if (sdev->inquiry[0] || > > + strncmp(&sdev->inquiry[8], "ATA ", 8)) > > + return -ENODEV; > > It's possible that we will need a quirk mechanism to disable temperature > monitoring for certain ATA devices. Has it been considered to make > scsi_add_lun() set a flag that indicates whether or not temperatures > should be monitored and to check that flag from inside this function? > I'm asking this because an identical strncmp() check exists in > scsi_add_lun(). > I am aware that we may at some point need quirks for some SATA devices. From my perspective, the place for such quirks would be this driver, possibly using the ATA ID string in the inquiry data structure and, if needed, the firmware revision as identifier. > > +static int satatemp_read(struct device *dev, enum hwmon_sensor_types type, > > + u32 attr, int channel, long *val) > > +{ > > + struct satatemp_data *st = dev_get_drvdata(dev); > > Which device does 'dev' represent? What guarantees that the drvdata > won't be used for another purpose, e.g. by the SCSI core? > 'dev' is the hardware monitoring device. The driver data is set in hwmon_device_register_with_info(); it is the third argument of that function. It won't be used outside the context of this driver. > > +/* > > + * 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 satatemp_add(struct device *dev, struct class_interface *intf) > > +{ > > + struct scsi_device *sdev = to_scsi_device(dev->parent); > > + struct satatemp_data *st; > > + int err; > > + > > + st = kzalloc(sizeof(*st), GFP_KERNEL); > > + if (!st) > > + return -ENOMEM; > > + > > + st->sdev = sdev; > > + st->dev = dev; > > + mutex_init(&st->lock); > > + > > + if (satatemp_identify(st)) { > > + err = -ENODEV; > > + goto abort; > > + } > > + > > + st->hwdev = hwmon_device_register_with_info(dev->parent, "satatemp", > > + st, &satatemp_chip_info, > > + NULL); > > + if (IS_ERR(st->hwdev)) { > > + err = PTR_ERR(st->hwdev); > > + goto abort; > > + } > > + > > + list_add(&st->list, &satatemp_devlist); > > + return 0; > > + > > +abort: > > + kfree(st); > > + return err; > > +} > > How much does synchronously submitting SCSI commands from inside the > device probing call back slow down SCSI device discovery? What is the > impact of this code on systems with a large number of ATA devices? > Interesting question. In general, any SCSI commands would only be executed for SATA drives since the very first check in satatemp_identify() uses sdev->inquiriy and aborts if the drive in question is not an ATA drive. When connected to SATA drives, I measured the execution time of satatemp_identify() to be between ~900 uS and 1,700 uS on a system with Ryzen 3900 CPU. In more detail: - Time to read VPD page: ~10-20 uS - Time to execute SMART_READ_LOG/SCT_STATUS_REQ_ADDR: ~140-150 uS - Time to execute SMART_WRITE_LOG/SCT_STATUS_REQ_ADDR: ~600-1,500 uS - Time to execute SMART_READ_LOG/SCT_READ_LOG_ADDR: ~100-130 uS Does that answer your question ? Please note that I think that this is irrelevant in this context. The driver is only instantiated if loaded explicitly, so whoever uses it will be in a position to decide if the benefit of using it will outweigh its cost. If instantiation time ever becomes a real problem, for example if someone with a large number of SATA drives in a system wants to use the driver and is concerned about instantiation time, we can make the second part of its registration (ie everything after identifying SATA drives) asynchronous. That would, however, add a substantial amount of complexity to the driver, and we should only do it if it is really warranted. Thanks, Guenter
On 12/9/19 2:20 PM, Guenter Roeck wrote: > On Mon, Dec 09, 2019 at 09:08:13AM -0800, Bart Van Assche wrote: >> How much does synchronously submitting SCSI commands from inside the >> device probing call back slow down SCSI device discovery? What is the >> impact of this code on systems with a large number of ATA devices? > > Interesting question. In general, any SCSI commands would only be executed > for SATA drives since the very first check in satatemp_identify() uses > sdev->inquiriy and aborts if the drive in question is not an ATA drive. > When connected to SATA drives, I measured the execution time of > satatemp_identify() to be between ~900 uS and 1,700 uS on a system with > Ryzen 3900 CPU. > > In more detail: > - Time to read VPD page: ~10-20 uS > - Time to execute SMART_READ_LOG/SCT_STATUS_REQ_ADDR: ~140-150 uS > - Time to execute SMART_WRITE_LOG/SCT_STATUS_REQ_ADDR: ~600-1,500 uS > - Time to execute SMART_READ_LOG/SCT_READ_LOG_ADDR: ~100-130 uS > > Does that answer your question ? > > Please note that I think that this is irrelevant in this context. > The driver is only instantiated if loaded explicitly, so whoever uses it > will be in a position to decide if the benefit of using it will outweigh > its cost. > > If instantiation time ever becomes a real problem, for example if someone > with a large number of SATA drives in a system wants to use the driver > and is concerned about instantiation time, we can make the second part > of its registration (ie everything after identifying SATA drives) > asynchronous. That would, however, add a substantial amount of complexity > to the driver, and we should only do it if it is really warranted. Hi Guenter, Thank you for having answered my question in great detail. I think this overhead is low enough to be acceptable. Bart.
Hi Guenther, needless to say I am a big fan of this patch, so: Reviewed-by: Linus Walleij <linus.walleij@linaro.org> It's a nice addition with the SCT command, I never figured that part out. Also nice how you register the scsi class interface I never saw that before, it makes it a very neat plug-in. The comments are more discussion points on how to (maybe) take it further after this. On Mon, Dec 9, 2019 at 6:21 AM Guenter Roeck <linux@roeck-us.net> wrote: > If the drive supports SCT transport and reports temperature limits, > those are reported as well. If I understand the patch correctly it will prefer to use SCT transport to read the temperature, and only fall back to the SMART attributes if this is not working, so I guess the commit message should state the heuristics used here. > +++ b/Documentation/hwmon/satatemp.rst Excellent doc. > + * If the SCT Command Transport feature set is not available, drive temperatures > + * may be readable through SMART attributes. Since SMART attributes are not well > + * defined, this method is only used as fallback mechanism. So this maybe cut/paste to commit message as well so people understand the commit fully. > + for (i = 0; i < ATA_MAX_SMART_ATTRS; i++) { > + u8 *attr = buf + i * 12; > + int id = attr[2]; > + > + if (!id) > + continue; > + > + if (id == SMART_TEMP_PROP_190) { > + temp_raw = attr[7]; > + have_temp = true; > + } > + if (id == SMART_TEMP_PROP_194) { > + temp_raw = attr[7]; > + have_temp = true; > + break; > + } > + } > + > + if (have_temp) { > + *temp = temp_raw * 1000; > + return 0; > + } This looks like it will work fine, I had some heuristics to determine the vendor-specific max/min temperatures in property 194 in my patch, but I can certainly add that back in later. > +static const struct hwmon_channel_info *satatemp_info[] = { > + HWMON_CHANNEL_INFO(chip, > + HWMON_C_REGISTER_TZ), I suppose this means I will also have a temperature zone as I want :D When I read the comments from the previous thread I got the impression the SCSI people wanted me to use something like the SCT transport and the hook in the SMART thing in the libata back-end specifically for [S]ATA in response to the SCT read log command. In drivers/ata/libata-scsi.c I suppose. I guess one thing doesn't exclude the other though. We can attempt to move the code for [S]ATA over to libata at some point and respond to the SCT read log command from within the library in that case. I don't understand if that means the SCT read log also works on some SCSI drives, or if it is just a slot-in thing for ATA translation that has no meaning on SCSI drives. But that can be resolved by people who want to use this for SCSI drives and not by us. Yours, Linus Walleij
Linus, > It's a nice addition with the SCT command, I never figured that part > out. Also nice how you register the scsi class interface I never saw > that before, it makes it a very neat plug-in. Yep, I agree that the patch looks pretty good in general. There are just a few wrinkles in the detection heuristics I would like to tweak. More on that later. Yesterday I added support for the SCSI temperature log page and am working through some kinks wrt. making this work for USB as well. > When I read the comments from the previous thread I got the impression > the SCSI people wanted me to use something like the SCT transport and > the hook in the SMART thing in the libata back-end specifically for > [S]ATA in response to the SCT read log command. Our recommendation was for libata-scsi.c to export the SCSI temperature log page, just like we do for all the other ATA parameters. However, in tinkering with this the last couple of days, I find myself torn on the subject. For two reasons. First of all, there is no 1:1 sensor mapping unless you implement the slightly more complex environmental log page. Which isn't a big deal, except out of the hundred or so SCSI devices I have here there isn't a single one that supports it it. So in practice this interface would probably only exist for the purpose of the libata SATL. The other reason the libata approach is slightly less attractive is that we need all the same SMART parsing for USB as well. So while it is cleaner to hide everything ATA in libata, the reality of USB-ATA bridges gets in the way. That is why I previously suggested having a libsmart or similar with those common bits. Anyway, based on what I've worked on today, I'm not sure that libata is necessarily the way to go. Sorry about giving bad advice! We've successfully implemented translations for everything else in libata over the years without too much trouble. And it's not really that the translation is bad. It's more the need to support it for USB as well that makes things clunky. > I don't understand if that means the SCT read log also works > on some SCSI drives, or if it is just a slot-in thing for > ATA translation that has no meaning on SCSI drives. It's an ATA command. One concern I have is wrt. to sensor naming. Maybe my /usr/bin/sensors command is too old. But it's pretty hopeless to get sensor readings for 100 drives without being able to tell which sensor is for which device. Haven't looked into that yet. The links exist in /sys/class/hwmon that would allow vendor/model/serial to be queried. Oh, and another issue. While technically legal according to the spec, I am not sure it's a good idea to export a sensor per scsi_device. I have moved things to scsi_target instead to avoid having bazillions of sensors show up. Multi-actuator drives are already shipping. If I recall correctly, though, I seem to recall that you had some sort of multi-LUN external disk box that warranted you working on this in the first place. Is that correct? Can you refresh my memory?
On 12/12/19 3:21 PM, Martin K. Petersen wrote: > > Linus, > >> It's a nice addition with the SCT command, I never figured that part >> out. Also nice how you register the scsi class interface I never saw >> that before, it makes it a very neat plug-in. > > Yep, I agree that the patch looks pretty good in general. There are just > a few wrinkles in the detection heuristics I would like to tweak. More > on that later. > > Yesterday I added support for the SCSI temperature log page and am > working through some kinks wrt. making this work for USB as well. > >> When I read the comments from the previous thread I got the impression >> the SCSI people wanted me to use something like the SCT transport and >> the hook in the SMART thing in the libata back-end specifically for >> [S]ATA in response to the SCT read log command. > > Our recommendation was for libata-scsi.c to export the SCSI temperature > log page, just like we do for all the other ATA parameters. > > However, in tinkering with this the last couple of days, I find myself > torn on the subject. For two reasons. First of all, there is no 1:1 > sensor mapping unless you implement the slightly more complex > environmental log page. Which isn't a big deal, except out of the > hundred or so SCSI devices I have here there isn't a single one that > supports it it. So in practice this interface would probably only exist > for the purpose of the libata SATL. > > The other reason the libata approach is slightly less attractive is that > we need all the same SMART parsing for USB as well. So while it is > cleaner to hide everything ATA in libata, the reality of USB-ATA bridges > gets in the way. That is why I previously suggested having a libsmart or > similar with those common bits. > > Anyway, based on what I've worked on today, I'm not sure that libata is > necessarily the way to go. Sorry about giving bad advice! We've > successfully implemented translations for everything else in libata over > the years without too much trouble. And it's not really that the > translation is bad. It's more the need to support it for USB as well > that makes things clunky. > >> I don't understand if that means the SCT read log also works >> on some SCSI drives, or if it is just a slot-in thing for >> ATA translation that has no meaning on SCSI drives. > > It's an ATA command. > > One concern I have is wrt. to sensor naming. Maybe my /usr/bin/sensors > command is too old. But it's pretty hopeless to get sensor readings for You'll need the command (and libsensors) from the lm-sensors package version 3.5 or later for it to recognize SCSI/ATA drives. > 100 drives without being able to tell which sensor is for which > device. Haven't looked into that yet. The links exist in > /sys/class/hwmon that would allow vendor/model/serial to be queried. > There is a device/ subdirectory which points to that information. Is that what you are looking for ? "sensors" displays something like satatemp-scsi-5-0, which matches sd 5:0:0:0: > Oh, and another issue. While technically legal according to the spec, I > am not sure it's a good idea to export a sensor per scsi_device. I have > moved things to scsi_target instead to avoid having bazillions of > sensors show up. Multi-actuator drives are already shipping. > Not sure I understand what you mean with 'bazillions of sensors' and 'sensor per scsi_device'. Can you elaborate ? I see one sensor per drive, which is what I would expect. Thanks, Guenter > If I recall correctly, though, I seem to recall that you had some sort > of multi-LUN external disk box that warranted you working on this in the > first place. Is that correct? Can you refresh my memory? >
Guenter, > Not sure I understand what you mean with 'bazillions of sensors' and > 'sensor per scsi_device'. Can you elaborate ? I see one sensor per > drive, which is what I would expect. Yes, but for storage arrays, hanging off of struct scsi_device means you would get a sensor for each volume you create. Even though you presumably only have one physical "box" to monitor (ignoring for a moment that the drives inside the box may have their own sensors that may or may not be visible to the host). Also, multi-actuator disk drives are shipping. They present themselves to the host as a target with multiple LUNs. Once again you'll probably have one temperature sensor for the physical drive but many virtual disks being presented to the OS. So you'd end up with for instance 4 sensors in hwmon even though there physically only is one. It's a tough call since there may be hardware configurations where distinct per-LUN temperature is valid (some quirky JBODs represent disk drives as different LUNs instead of different targets, for instance). How expensive will it be to have - say - 100 hwmon sensors instantiated for a drive tray?
On 12/16/19 6:47 PM, Martin K. Petersen wrote: > > Guenter, > >> Not sure I understand what you mean with 'bazillions of sensors' and >> 'sensor per scsi_device'. Can you elaborate ? I see one sensor per >> drive, which is what I would expect. > > Yes, but for storage arrays, hanging off of struct scsi_device means you > would get a sensor for each volume you create. Even though you > presumably only have one physical "box" to monitor (ignoring for a > moment that the drives inside the box may have their own sensors that > may or may not be visible to the host). > > Also, multi-actuator disk drives are shipping. They present themselves > to the host as a target with multiple LUNs. Once again you'll probably > have one temperature sensor for the physical drive but many virtual > disks being presented to the OS. So you'd end up with for instance 4 > sensors in hwmon even though there physically only is one. > > It's a tough call since there may be hardware configurations where > distinct per-LUN temperature is valid (some quirky JBODs represent disk > drives as different LUNs instead of different targets, for instance). > > How expensive will it be to have - say - 100 hwmon sensors instantiated > for a drive tray? > If that drive tray has 100 physical drives, that is what I would expect to see. The most expensive part is the device entry, and there are already several of those for each scsi device. I have seen systems with hundreds of hwmon devices (backbone switches tend to be quite generous with voltage, current, power, and temperature sensors), so I am not particularly concerned in that regard. If there are 100 physical drives, you would actually want to see the temperature of each drive separately, as one of them might be overheating due to some internal failure. If the storage array is represented to the system as single huge physical drive, which is then split into logical entities not related to physical drives, I guess that would represent a problem for system management overall. Maybe such boxes have separate thermal monitoring ? Either case, we have the question if it is possible to distinguish the pseudo-physical drive from the virtual drives (or volumes). I would not mind to tie the hardware monitoring device to something else than the scsi device if the scsi device does not always have a physical representation. Is there a way to determine if a scsi device is virtual or real ? Obviously it does not make sense to report the same temperature multiple times, and we would want only a single temperature reported for each physical drive. At the same time, I absolutely want to avoid a situation where a single hardware monitoring device would report the temperature of multiple drives. The concern here is crossing OIR boundaries. A single hardware monitoring device should never cross an OIR boundary. Thanks, Guenter
Guenter, > If there are 100 physical drives, you would actually want to see the > temperature of each drive separately, as one of them might be > overheating due to some internal failure. Yep. However, for "big boxes" you'll typically get that information from SAF-TE or SES enclosure services and not from the drive itself. SES allows you to monitor power supplies, drive bays, hot swap events, thermals, etc. We have a SES driver in SCSI that exposes all these things in sysfs. It is not currently tied into hwmon. > If the storage array is represented to the system as single huge > physical drive, which is then split into logical entities not related > to physical drives, I guess that would represent a problem for system > management overall. Yep. That's why there's dedicated plumbing in smartmontools to handle various RAID controller interfaces for accessing physical drive information. It's typically highly vendor-specific. > I would not mind to tie the hardware monitoring device to something > else than the scsi device if the scsi device does not always have a > physical representation. Is there a way to determine if a scsi device > is virtual or real ? Not really. Target is usually a pretty good approximation, although some arrays introduce virtual targets because of limited LUN (scsi_device) numbering capabilities. However, arrays generally don't support per-LUN temperature because it makes no sense. I'm trying to gauge how much a pain potentially redundant sensors would be for userland monitoring tooling vs. how many oddball devices we'd not be able to support if we were to use scsi_target as parent (or restrict the sensor binding to LUN 0).
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 230ad59b462b..ecf1832dd013 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -133,6 +133,7 @@ Hardware Monitoring Kernel Drivers pxe1610 pwm-fan raspberrypi-hwmon + satatemp sch5627 sch5636 scpi-hwmon diff --git a/Documentation/hwmon/satatemp.rst b/Documentation/hwmon/satatemp.rst new file mode 100644 index 000000000000..59b105f3c79a --- /dev/null +++ b/Documentation/hwmon/satatemp.rst @@ -0,0 +1,48 @@ +Kernel driver satatemp +====================== + + +References +---------- + +ANS T13/1699-D +Information technology - AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS) + +ANS Project T10/BSR INCITS 513 +Information technology - SCSI Primary Commands - 4 (SPC-4) + +ANS Project INCITS 557 +Information technology - SCSI / ATA Translation - 5 (SAT-5) + + +Description +----------- + +This driver supports reporting the temperature of SATA drives. +If supported, it uses the SCT Command Transport feature to read +the current drive temperature and, if available, temperature limits +as well as historic minimum and maximum temperatures. If SCT Command +Transport is not supported, the driver uses SMART attributes to read +the drive temperature. + + +Sysfs entries +------------- + +Only the temp1_input attribute is always available. Other attributes are +available only if reported by the drive. All temperatures are reported in +milli-degrees Celsius. + +======================= ===================================================== +temp1_input Current drive temperature +temp1_lcrit Minimum temperature limit. Operating the device below + this temperature may cause physical damage to the + device. +temp1_min Minimum recommended continuous operating limit +temp1_max Maximum recommended continuous operating temperature +temp1_crit Maximum temperature limit. Operating the device above + this temperature may cause physical damage to the + device. +temp1_lowest Minimum temperature seen this power cycle +temp1_highest Maximum temperature seen this power cycle +======================= ===================================================== diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 13a6b4afb4b3..4c63eb7ba96a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1346,6 +1346,16 @@ config SENSORS_RASPBERRYPI_HWMON This driver can also be built as a module. If so, the module will be called raspberrypi-hwmon. +config SENSORS_SATATEMP + tristate "SATA hard disk drives with temperature sensors" + depends on SCSI && ATA + help + If you say yes you get support for the temperature sensor on + SATA hard disk drives. + + This driver can also be built as a module. If so, the module + will be called smarttemp. + config SENSORS_SHT15 tristate "Sensiron humidity and temperature sensors. SHT15 and compat." depends on GPIOLIB || COMPILE_TEST diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 40c036ea45e6..fe55b8f76af9 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -148,6 +148,7 @@ obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o +obj-$(CONFIG_SENSORS_SATATEMP) += satatemp.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o diff --git a/drivers/hwmon/satatemp.c b/drivers/hwmon/satatemp.c new file mode 100644 index 000000000000..4a6bdcc86988 --- /dev/null +++ b/drivers/hwmon/satatemp.c @@ -0,0 +1,575 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hwmon client for SATA hard disk drives with temperature sensors + * Copyright (C) 2019 Zodiac Inflight Innovations + * + * With input from: + * Hwmon client for S.M.A.R.T. hard disk drives with temperature sensors. + * (C) 2018 Linus Walleij + * + * hwmon: Driver for SCSI/ATA temperature sensors + * by Constantin Baranov <const@mimas.ru>, submitted September 2009 + * + * The primary means to read hard drive temperatures and temperature limits + * is the SCT Command Transport feature set as specified in ATA8-ACS. + * It can be used to read the current drive temperature, temperature limits, + * and historic minimum and maximum temperatures. The SCT Command Transport + * feature set is documented in "AT Attachment 8 - ATA/ATAPI Command Set + * (ATA8-ACS)". + * + * If the SCT Command Transport feature set is not available, drive temperatures + * may be readable through SMART attributes. Since SMART attributes are not well + * defined, this method is only used as fallback mechanism. + * + * There are three SMART attributes which may report drive temperatures. + * Those are defined as follows (from + * http://www.cropel.com/library/smart-attribute-list.aspx). + * + * 190 Temperature Temperature, monitored by a sensor somewhere inside + * the drive. Raw value typicaly holds the actual + * temperature (hexadecimal) in its rightmost two digits. + * + * 194 Temperature Temperature, monitored by a sensor somewhere inside + * the drive. Raw value typicaly holds the actual + * temperature (hexadecimal) in its rightmost two digits. + * + * 231 Temperature Temperature, monitored by a sensor somewhere inside + * the drive. Raw value typicaly holds the actual + * temperature (hexadecimal) in its rightmost two digits. + * + * Wikipedia defines attributes a bit differently. + * + * 190 Temperature Value is equal to (100-temp. °C), allowing manufacturer + * Difference or to set a minimum threshold which corresponds to a + * Airflow maximum temperature. This also follows the convention of + * Temperature 100 being a best-case value and lower values being + * undesirable. However, some older drives may instead + * report raw Temperature (identical to 0xC2) or + * Temperature minus 50 here. + * 194 Temperature or Indicates the device temperature, if the appropriate + * Temperature sensor is fitted. Lowest byte of the raw value contains + * Celsius the exact temperature value (Celsius degrees). + * 231 Life Left Indicates the approximate SSD life left, in terms of + * (SSDs) or program/erase cycles or available reserved blocks. + * Temperature A normalized value of 100 represents a new drive, with + * a threshold value at 10 indicating a need for + * replacement. A value of 0 may mean that the drive is + * operating in read-only mode to allow data recovery. + * Previously (pre-2010) occasionally used for Drive + * Temperature (more typically reported at 0xC2). + * + * Common denominator is that the first raw byte reports the temperature + * in degrees C on almost all drives. Some drives may report a fractional + * temperature in the second raw byte. + * + * Known exceptions (from libatasmart): + * - SAMSUNG SV0412H and SAMSUNG SV1204H) report the temperature in 10th + * degrees C in the first two raw bytes. + * - A few Maxtor drives report an unknown or bad value in attribute 194. + * - Certain Apple SSD drives report an unknown value in attribute 190. + * Only certain firmware versions are affected. + * + * Those exceptions affect older ATA drives and are currently ignored. + * Also, the second raw byte (possibly reporting the fractional temperature) + * is currently ignored. + * + * Many drives also report temperature limits in additional SMART data raw + * bytes. The format of those is not well defined and varies widely. + * The driver does not currently attempt to report those limits. + * + * According to data in smartmontools, attribute 231 is rarely used to report + * drive temperatures. At the same time, several drives report SSD life left + * in attribute 231, but do not support temperature sensors. For this reason, + * attribute 231 is currently ignored. + * + * Following above definitions, temperatures are reported as follows. + * If SCT Command Transport is supported, it is used to read the + * temperature and, if available, temperature limits. + * - Otherwise, if SMART attribute 194 is supported, it is used to read + * the temperature. + * - Otherwise, if SMART attribute 190 is supported, it is used to read + * the temperature. + */ + +#include <linux/ata.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/hwmon.h> +#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> +#include <scsi/scsi_proto.h> + +struct satatemp_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 */ + u8 smartdata[ATA_SECT_SIZE]; /* local buffer */ + int (*get_temp)(struct satatemp_data *st, u32 attr, long *val); + bool have_temp_lowest; /* lowest temp in SCT status */ + bool have_temp_highest; /* highest temp in SCT status */ + bool have_temp_min; /* have min temp */ + bool have_temp_max; /* have max temp */ + bool have_temp_lcrit; /* have lower critical limit */ + bool have_temp_crit; /* have critical limit */ + int temp_min; /* min temp */ + int temp_max; /* max temp */ + int temp_lcrit; /* lower critical limit */ + int temp_crit; /* critical limit */ +}; + +static LIST_HEAD(satatemp_devlist); + +#define ATA_MAX_SMART_ATTRS 30 +#define SMART_TEMP_PROP_190 190 +#define SMART_TEMP_PROP_194 194 + +#define SCT_STATUS_REQ_ADDR 0xe0 +#define SCT_STATUS_VERSION_LOW 0 /* log byte offsets */ +#define SCT_STATUS_VERSION_HIGH 1 +#define SCT_STATUS_TEMP 200 +#define SCT_STATUS_TEMP_LOWEST 201 +#define SCT_STATUS_TEMP_HIGHEST 202 +#define SCT_READ_LOG_ADDR 0xe1 +#define SMART_READ_LOG 0xd5 +#define SMART_WRITE_LOG 0xd6 + +#define INVALID_TEMP 0x80 + +#define temp_is_valid(temp) ((temp) != INVALID_TEMP) +#define temp_from_sct(temp) (((s8)(temp)) * 1000) + +static inline bool ata_id_smart_supported(u16 *id) +{ + return id[ATA_ID_COMMAND_SET_1] & BIT(0); +} + +static inline bool ata_id_smart_enabled(u16 *id) +{ + return id[ATA_ID_CFS_ENABLE_1] & BIT(0); +} + +static int satatemp_scsi_command(struct satatemp_data *st, + u8 ata_command, u8 feature, + u8 lba_low, u8 lba_mid, u8 lba_high) +{ + static u8 scsi_cmd[MAX_COMMAND_SIZE]; + int data_dir; + + memset(scsi_cmd, 0, sizeof(scsi_cmd)); + scsi_cmd[0] = ATA_16; + if (ata_command == ATA_CMD_SMART && feature == SMART_WRITE_LOG) { + scsi_cmd[1] = (5 << 1); /* PIO Data-out */ + /* + * No off.line or cc, write to dev, block count in sector count + * field. + */ + scsi_cmd[2] = 0x06; + data_dir = DMA_TO_DEVICE; + } else { + scsi_cmd[1] = (4 << 1); /* PIO Data-in */ + /* + * No off.line or cc, read from dev, block count in sector count + * field. + */ + scsi_cmd[2] = 0x0e; + data_dir = DMA_FROM_DEVICE; + } + scsi_cmd[4] = feature; + scsi_cmd[6] = 1; /* 1 sector */ + scsi_cmd[8] = lba_low; + scsi_cmd[10] = lba_mid; + 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); +} + +static int satatemp_ata_command(struct satatemp_data *st, u8 feature, u8 select) +{ + return satatemp_scsi_command(st, ATA_CMD_SMART, feature, select, + ATA_SMART_LBAM_PASS, ATA_SMART_LBAH_PASS); +} + +static int satatemp_get_smarttemp(struct satatemp_data *st, u32 attr, + long *temp) +{ + u8 *buf = st->smartdata; + bool have_temp = false; + u8 temp_raw; + u8 csum; + int err; + int i; + + err = satatemp_ata_command(st, ATA_SMART_READ_VALUES, 0); + if (err) + return err; + + /* Checksum the read value table */ + csum = 0; + for (i = 0; i < ATA_SECT_SIZE; i++) + csum += buf[i]; + if (csum) { + dev_dbg(&st->sdev->sdev_gendev, + "checksum error reading SMART values\n"); + return -EIO; + } + + for (i = 0; i < ATA_MAX_SMART_ATTRS; i++) { + u8 *attr = buf + i * 12; + int id = attr[2]; + + if (!id) + continue; + + if (id == SMART_TEMP_PROP_190) { + temp_raw = attr[7]; + have_temp = true; + } + if (id == SMART_TEMP_PROP_194) { + temp_raw = attr[7]; + have_temp = true; + break; + } + } + + if (have_temp) { + *temp = temp_raw * 1000; + return 0; + } + + return -ENXIO; +} + +static int satatemp_get_scttemp(struct satatemp_data *st, u32 attr, long *val) +{ + u8 *buf = st->smartdata; + int err; + + err = satatemp_ata_command(st, SMART_READ_LOG, SCT_STATUS_REQ_ADDR); + if (err) + return err; + switch (attr) { + case hwmon_temp_input: + *val = temp_from_sct(buf[SCT_STATUS_TEMP]); + break; + case hwmon_temp_lowest: + *val = temp_from_sct(buf[SCT_STATUS_TEMP_LOWEST]); + break; + case hwmon_temp_highest: + *val = temp_from_sct(buf[SCT_STATUS_TEMP_HIGHEST]); + break; + default: + err = -EINVAL; + break; + } + return err; +} + +static int satatemp_identify(struct satatemp_data *st) +{ + struct scsi_device *sdev = st->sdev; + u8 *buf = st->smartdata; + bool is_ata, is_sata; + bool have_sct_data_table; + bool have_sct_temp; + bool have_smart; + bool have_sct; + u16 *ata_id; + u16 version; + long temp; + u8 *vpd; + int err; + + /* bail out immediately if there is no inquiry data */ + if (!sdev->inquiry || sdev->inquiry_len < 16) + return -ENODEV; + + /* + * Inquiry data sanity checks (per SAT-5): + * - peripheral qualifier must be 0 + * - peripheral device type must be 0x0 (Direct access block device) + * - SCSI Vendor ID is "ATA " + */ + if (sdev->inquiry[0] || + strncmp(&sdev->inquiry[8], "ATA ", 8)) + return -ENODEV; + + vpd = kzalloc(1024, GFP_KERNEL); + if (!vpd) + return -ENOMEM; + + err = scsi_get_vpd_page(sdev, 0x89, vpd, 1024); + if (err) { + kfree(vpd); + return err; + } + + /* + * More sanity checks. + * For VPD offsets and values see ANS Project INCITS 557, + * "Information technology - SCSI / ATA Translation - 5 (SAT-5)". + */ + if (vpd[1] != 0x89 || vpd[2] != 0x02 || vpd[3] != 0x38 || + vpd[36] != 0x34 || vpd[56] != ATA_CMD_ID_ATA) { + kfree(vpd); + return -ENODEV; + } + ata_id = (u16 *)&vpd[60]; + is_ata = ata_id_is_ata(ata_id); + is_sata = ata_id_is_sata(ata_id); + have_sct = ata_id_sct_supported(ata_id); + have_sct_data_table = ata_id_sct_data_tables(ata_id); + have_smart = ata_id_smart_supported(ata_id) && + ata_id_smart_enabled(ata_id); + + kfree(vpd); + + /* bail out if this is not a SATA device */ + if (!is_ata || !is_sata) + return -ENODEV; + if (!have_sct) + goto skip_sct; + + err = satatemp_ata_command(st, SMART_READ_LOG, SCT_STATUS_REQ_ADDR); + if (err) + goto skip_sct; + + version = (buf[SCT_STATUS_VERSION_HIGH] << 8) | + buf[SCT_STATUS_VERSION_LOW]; + if (version != 2 && version != 3) + goto skip_sct; + + have_sct_temp = temp_is_valid(buf[SCT_STATUS_TEMP]); + if (!have_sct_temp) + goto skip_sct; + + st->have_temp_lowest = temp_is_valid(buf[SCT_STATUS_TEMP_LOWEST]); + st->have_temp_highest = temp_is_valid(buf[SCT_STATUS_TEMP_HIGHEST]); + + if (!have_sct_data_table) + goto skip_sct; + + /* Request and read temperature history table */ + memset(buf, '\0', sizeof(st->smartdata)); + buf[0] = 5; /* data table command */ + buf[2] = 1; /* read table */ + buf[4] = 2; /* temperature history table */ + + err = satatemp_ata_command(st, SMART_WRITE_LOG, SCT_STATUS_REQ_ADDR); + if (err) + goto skip_sct_data; + + err = satatemp_ata_command(st, SMART_READ_LOG, SCT_READ_LOG_ADDR); + if (err) + goto skip_sct_data; + + /* + * Temperature limits per AT Attachment 8 - + * ATA/ATAPI Command Set (ATA8-ACS) + */ + st->have_temp_max = temp_is_valid(buf[6]); + st->have_temp_crit = temp_is_valid(buf[7]); + st->have_temp_min = temp_is_valid(buf[8]); + st->have_temp_lcrit = temp_is_valid(buf[9]); + + st->temp_max = temp_from_sct(buf[6]); + st->temp_crit = temp_from_sct(buf[7]); + st->temp_min = temp_from_sct(buf[8]); + st->temp_lcrit = temp_from_sct(buf[9]); + +skip_sct_data: + if (have_sct_temp) { + st->get_temp = satatemp_get_scttemp; + return 0; + } +skip_sct: + if (!have_smart) + return -ENODEV; + st->get_temp = satatemp_get_smarttemp; + return satatemp_get_smarttemp(st, hwmon_temp_input, &temp); +} + +static int satatemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct satatemp_data *st = dev_get_drvdata(dev); + int err = 0; + + if (type != hwmon_temp) + return -EINVAL; + + switch (attr) { + 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; + break; + case hwmon_temp_min: + *val = st->temp_min; + break; + case hwmon_temp_max: + *val = st->temp_max; + break; + case hwmon_temp_crit: + *val = st->temp_crit; + break; + default: + err = -EINVAL; + break; + } + return err; +} + +static umode_t satatemp_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct satatemp_data *st = data; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_lowest: + if (st->have_temp_lowest) + return 0444; + break; + case hwmon_temp_highest: + if (st->have_temp_highest) + return 0444; + break; + case hwmon_temp_min: + if (st->have_temp_min) + return 0444; + break; + case hwmon_temp_max: + if (st->have_temp_max) + return 0444; + break; + case hwmon_temp_lcrit: + if (st->have_temp_lcrit) + return 0444; + break; + case hwmon_temp_crit: + if (st->have_temp_crit) + return 0444; + break; + default: + break; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info *satatemp_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | + HWMON_T_LOWEST | HWMON_T_HIGHEST | + HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_LCRIT | HWMON_T_CRIT), + NULL +}; + +static const struct hwmon_ops satatemp_ops = { + .is_visible = satatemp_is_visible, + .read = satatemp_read, +}; + +static const struct hwmon_chip_info satatemp_chip_info = { + .ops = &satatemp_ops, + .info = satatemp_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 satatemp_add(struct device *dev, struct class_interface *intf) +{ + struct scsi_device *sdev = to_scsi_device(dev->parent); + struct satatemp_data *st; + int err; + + st = kzalloc(sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->sdev = sdev; + st->dev = dev; + mutex_init(&st->lock); + + if (satatemp_identify(st)) { + err = -ENODEV; + goto abort; + } + + st->hwdev = hwmon_device_register_with_info(dev->parent, "satatemp", + st, &satatemp_chip_info, + NULL); + if (IS_ERR(st->hwdev)) { + err = PTR_ERR(st->hwdev); + goto abort; + } + + list_add(&st->list, &satatemp_devlist); + return 0; + +abort: + kfree(st); + return err; +} + +static void satatemp_remove(struct device *dev, struct class_interface *intf) +{ + struct satatemp_data *st, *tmp; + + list_for_each_entry_safe(st, tmp, &satatemp_devlist, list) { + if (st->dev == dev) { + list_del(&st->list); + hwmon_device_unregister(st->hwdev); + kfree(st); + break; + } + } +} + +static struct class_interface satatemp_interface = { + .add_dev = satatemp_add, + .remove_dev = satatemp_remove, +}; + +static int __init satatemp_init(void) +{ + return scsi_register_interface(&satatemp_interface); +} + +static void __exit satatemp_exit(void) +{ + scsi_unregister_interface(&satatemp_interface); +} + +module_init(satatemp_init); +module_exit(satatemp_exit); + +MODULE_AUTHOR("Guenter Roeck <linus@roeck-us.net>"); +MODULE_DESCRIPTION("ATA temperature monitor"); +MODULE_LICENSE("GPL");
Reading the hard drive temperature has been supported for years by userspace tools such as smarttools or hddtemp. The downside of such tools is that they need to run with super-user privilege, that the temperatures are not reported by standard tools such as 'sensors' or 'libsensors', and that drive temperatures are not available for use in the kernel's thermal subsystem. This driver solves this problem by adding support for reading the temperature of SATA drives from the kernel using the hwmon API and by adding a temperature zone for each drive. With this driver, the hard disk temperature can be read using the unprivileged 'sensors' application: $ sensors satatemp-scsi-1-0 satatemp-scsi-1-0 Adapter: SCSI adapter temp1: +23.0°C or directly from sysfs: $ grep . /sys/class/hwmon/hwmon9/{name,temp1_input} /sys/class/hwmon/hwmon9/name:satatemp /sys/class/hwmon/hwmon9/temp1_input:23000 If the drive supports SCT transport and reports temperature limits, those are reported as well. satatemp-scsi-0-0 Adapter: SCSI adapter temp1: +27.0°C (low = +0.0°C, high = +60.0°C) (crit low = -41.0°C, crit = +85.0°C) (lowest = +23.0°C, highest = +34.0°C) Cc: Chris Healy <cphealy@gmail.com> Cc: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Guenter Roeck <linux@roeck-us.net> --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/satatemp.rst | 48 +++ drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/satatemp.c | 575 +++++++++++++++++++++++++++++++ 5 files changed, 635 insertions(+) create mode 100644 Documentation/hwmon/satatemp.rst create mode 100644 drivers/hwmon/satatemp.c