libata: Media rotation rate and form factor heuristics

This patch provides new heuristics for parsing both the form factor and
media rotation rate ATA IDENFITY words.

The reported ATA version must be 7 or greater and the device must return
values defined as valid in the standard. Only then are the
characteristics reported to SCSI via the VPD B1 page.

This seems like a reasonable compromise to me considering that we have
been shipping several kernel releases that key off the rotation rate bit
without any version checking whatsoever. With no complaints so far.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>

authored by Martin K. Petersen and committed by Jeff Garzik 4bca3286 61d79a8e

+34 -5
+6 -5
drivers/ata/libata-scsi.c
··· 2142 2142 2143 2143 static unsigned int ata_scsiop_inq_b1(struct ata_scsi_args *args, u8 *rbuf) 2144 2144 { 2145 + int form_factor = ata_id_form_factor(args->id); 2146 + int media_rotation_rate = ata_id_rotation_rate(args->id); 2147 + 2145 2148 rbuf[1] = 0xb1; 2146 2149 rbuf[3] = 0x3c; 2147 - if (ata_id_major_version(args->id) > 7) { 2148 - rbuf[4] = args->id[217] >> 8; 2149 - rbuf[5] = args->id[217]; 2150 - rbuf[7] = args->id[168] & 0xf; 2151 - } 2150 + rbuf[4] = media_rotation_rate >> 8; 2151 + rbuf[5] = media_rotation_rate; 2152 + rbuf[7] = form_factor; 2152 2153 2153 2154 return 0; 2154 2155 }
+28
include/linux/ata.h
··· 730 730 return 0; 731 731 } 732 732 733 + static inline int ata_id_form_factor(const u16 *id) 734 + { 735 + u16 val = id[168]; 736 + 737 + if (ata_id_major_version(id) < 7 || val == 0 || val == 0xffff) 738 + return 0; 739 + 740 + val &= 0xf; 741 + 742 + if (val > 5) 743 + return 0; 744 + 745 + return val; 746 + } 747 + 748 + static inline int ata_id_rotation_rate(const u16 *id) 749 + { 750 + u16 val = id[217]; 751 + 752 + if (ata_id_major_version(id) < 7 || val == 0 || val == 0xffff) 753 + return 0; 754 + 755 + if (val > 1 && val < 0x401) 756 + return 0; 757 + 758 + return val; 759 + } 760 + 733 761 static inline int ata_id_has_trim(const u16 *id) 734 762 { 735 763 if (ata_id_major_version(id) >= 7 &&