From f801b72815b6bab22499beecf02ddc3f6e0e8e29 Mon Sep 17 00:00:00 2001 From: likewise <likewise@4ea69e1a-61f1-4043-bf83-b5c94c648137> Date: Fri, 13 Aug 2004 00:04:39 +0000 Subject: [PATCH] Added SMART support for Marvell's SATA controllers. See Request ID 1008374. git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/branches/RELEASE_5_1_14_MARVELL_SATA_BRANCH@1853 4ea69e1a-61f1-4043-bf83-b5c94c648137 --- sm5/atacmds.c | 160 ++++++++++++++++++++++- sm5/extern.h | 3 +- sm5/scsicmds.h | 6 +- sm5/scsiprint.c | 56 ++++++-- sm5/smartctl.c | 13 +- sm5/smartctl.h | 5 +- sm5/smartd.8 | 16 ++- sm5/smartd.c | 338 +++++++++++++++++++++++++++++++++++++++++++++--- sm5/smartd.h | 8 +- 9 files changed, 563 insertions(+), 42 deletions(-) diff --git a/sm5/atacmds.c b/sm5/atacmds.c index f449cdc06..4f83b51af 100644 --- a/sm5/atacmds.c +++ b/sm5/atacmds.c @@ -29,10 +29,11 @@ #include <stdlib.h> #include <ctype.h> #include "atacmds.h" +#include "scsicmds.h" #include "utility.h" #include "extern.h" -const char *atacmds_c_cvsid="$Id: atacmds.c,v 1.100 2003/06/12 12:18:53 ballen4705 Exp $" ATACMDS_H_CVSID EXTERN_H_CVSID UTILITY_H_CVSID; +const char *atacmds_c_cvsid="$Id: atacmds.c,v 1.100.2.1 2004/08/13 00:04:39 likewise Exp $" ATACMDS_H_CVSID EXTERN_H_CVSID UTILITY_H_CVSID; // for passing global control variables extern smartmonctrl *con; @@ -446,7 +447,7 @@ char *create_vendor_attribute_arg_list(void){ int os_specific_handler(int device, smart_command_set command, int select, char *data){ unsigned char buff[STRANGE_BUFFER_LENGTH]; - int retval, copydata=0; + int retval = 0, copydata = 0; // See struct hd_drive_cmd_hdr in hdreg.h // buff[0]: ATA COMMAND CODE REGISTER @@ -562,6 +563,151 @@ int os_specific_handler(int device, smart_command_set command, int select, char return 0; } +// PURPOSE +// This is an interface routine meant to isolate the OS dependent +// parts of the code, and to provide a debugging interface. Each +// different port and OS needs to provide it's own interface. This +// is the linux one. +// DESCRIPTION +// The function sends SCSI vendor-specific command 0xC (6 bytes long)followed +// by the data buffer representing ATA registers: +// buff[0]: ATA COMMAND CODE REGISTER +// buff[1]: ATA SECTOR NUMBER REGISTER +// buff[2]: ATA FEATURES REGISTER +// buff[3]: ATA SECTOR COUNT REGISTER +// (See struct hd_drive_cmd_hdr in hdreg.h) +// +// ARGUMENTS +// device: is the file descriptor provided by open() +// command: defines the different operations. +// select: additional input data if needed (which log, which type of +// self-test). +// data: location to write output data, if needed (512 bytes). +// Note: not all commands use all arguments. +// RETURN VALUES +// -1 if the command failed +// 0 if the command succeeded, +// STATUS_CHECK routine: +// -1 if the command failed +// 0 if the command succeeded and disk SMART status is "OK" +// 1 if the command succeeded and disk SMART status is "FAILING" + +int mvsata_os_specific_handler(int device, + smart_command_set command, + int select, + char *data) +{ + typedef struct + { + int inlen; + int outlen; + char cmd[540]; + } mvsata_scsi_cmd; + + int copydata = 0; + mvsata_scsi_cmd smart_command; + unsigned char *buff = &smart_command.cmd[6]; + // See struct hd_drive_cmd_hdr in hdreg.h + // buff[0]: ATA COMMAND CODE REGISTER + // buff[1]: ATA SECTOR NUMBER REGISTER + // buff[2]: ATA FEATURES REGISTER + // buff[3]: ATA SECTOR COUNT REGISTER + + // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes) + memset(&smart_command, 0, sizeof(smart_command)); + smart_command.inlen = 540; + smart_command.outlen = 540; + smart_command.cmd[0] = 0xC; //Vendor-specific code + smart_command.cmd[4] = 6; //command length + + buff[0] = WIN_SMART; + switch (command){ + case READ_VALUES: + buff[2]=SMART_READ_VALUES; + copydata=buff[3]=1; + break; + case READ_THRESHOLDS: + buff[2]=SMART_READ_THRESHOLDS; + copydata=buff[1]=buff[3]=1; + break; + case READ_LOG: + buff[2]=SMART_READ_LOG_SECTOR; + buff[1]=select; + copydata=buff[3]=1; + break; + case IDENTIFY: + buff[0]=WIN_IDENTIFY; + copydata=buff[3]=1; + break; + case PIDENTIFY: + buff[0]=WIN_PIDENTIFY; + copydata=buff[3]=1; + break; + case ENABLE: + buff[2]=SMART_ENABLE; + buff[1]=1; + break; + case DISABLE: + buff[2]=SMART_DISABLE; + buff[1]=1; + break; + case STATUS: + case STATUS_CHECK: + // this command only says if SMART is working. It could be + // replaced with STATUS_CHECK below. + buff[2] = SMART_STATUS; + break; + case AUTO_OFFLINE: + buff[2]=SMART_AUTO_OFFLINE; + buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case AUTOSAVE: + buff[2]=SMART_AUTOSAVE; + buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! + break; + case IMMEDIATE_OFFLINE: + buff[2]=SMART_IMMEDIATE_OFFLINE; + buff[1]=select; + break; + default: + pout("Unrecognized command %d in mvsata_os_specific_handler()\n", command); + exit(1); + break; + } + // There are two different types of ioctls(). The HDIO_DRIVE_TASK + // one is this: + // We are now doing the HDIO_DRIVE_CMD type ioctl. + if (ioctl(device, SCSI_IOCTL_SEND_COMMAND, (void *)&smart_command)) + return -1; + //Data returned is starting from 0 offset + if (command == STATUS || command == STATUS_CHECK) + { + // Cyl low and Cyl high unchanged means "Good SMART status" + if (buff[4] == 0x4F && buff[5] == 0xC2) + return 0; + // These values mean "Bad SMART status" + if (buff[4] == 0xF4 && buff[5] == 0x2C) + return 1; + // We haven't gotten output that makes sense; print out some debugging info + syserror("Error SMART Status command failed"); + pout("Please get assistance from %s\n",PROJECTHOME); + pout("Register values returned from SMART Status command are:\n"); + pout("CMD =0x%02x\n",(int)buff[0]); + pout("FR =0x%02x\n",(int)buff[1]); + pout("NS =0x%02x\n",(int)buff[2]); + pout("SC =0x%02x\n",(int)buff[3]); + pout("CL =0x%02x\n",(int)buff[4]); + pout("CH =0x%02x\n",(int)buff[5]); + pout("SEL=0x%02x\n",(int)buff[6]); + return -1; + } + + if (copydata) + memcpy(data, buff, 512); + return 0; +} + + static char *commandstrings[]={ [ENABLE]= "SMART ENABLE", [DISABLE]= "SMART DISABLE", @@ -621,7 +767,15 @@ int smartcommandhandler(int device, smart_command_set command, int select, char } // now execute the command - retval=os_specific_handler(device, command, select, data); + /*Check for MVSATA controller*/ + if (con->ismvsata) + { + retval = mvsata_os_specific_handler(device, command, select, data); + } + else + { + retval = os_specific_handler(device, command, select, data); + } // If reporting is enabled, say what output was produced by the command if (con->reportataioctl){ diff --git a/sm5/extern.h b/sm5/extern.h index 74c7cb89a..7006bdfc6 100644 --- a/sm5/extern.h +++ b/sm5/extern.h @@ -27,7 +27,7 @@ #ifndef EXTERN_H_CVSID -#define EXTERN_H_CVSID "$Id: extern.h,v 1.24 2003/04/19 09:53:41 pjwilliams Exp $\n" +#define EXTERN_H_CVSID "$Id: extern.h,v 1.24.2.1 2004/08/13 00:04:38 likewise Exp $\n" #endif // Possible values for fixfirmwarebug @@ -77,6 +77,7 @@ typedef struct smartmonctrl_s { // a non-default meaning. See the ataPrintSmartAttribName and // and parse_attribute_def functions. unsigned char attributedefs[256]; + unsigned char ismvsata; } smartmonctrl; #endif diff --git a/sm5/scsicmds.h b/sm5/scsicmds.h index 67912abf6..f389256c0 100644 --- a/sm5/scsicmds.h +++ b/sm5/scsicmds.h @@ -30,7 +30,7 @@ #define SCSICMDS_H_ #ifndef SCSICMDS_H_CVSID -#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.29 2003/06/01 12:36:12 dpgilbert Exp $\n" +#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.29.2.1 2004/08/13 00:04:38 likewise Exp $\n" #endif #include <stdio.h> @@ -76,6 +76,10 @@ #define SEND_DIAGNOSTIC 0x1d #endif +#ifndef SCSI_IOCTL_SEND_COMMAND +#define SCSI_IOCTL_SEND_COMMAND 1 +#endif + typedef unsigned char UINT8; typedef char INT8; typedef unsigned int UINT32; diff --git a/sm5/scsiprint.c b/sm5/scsiprint.c index f93b4a180..ec532c0f8 100644 --- a/sm5/scsiprint.c +++ b/sm5/scsiprint.c @@ -40,7 +40,7 @@ #define GBUF_SIZE 65535 -const char* scsiprint_c_cvsid="$Id: scsiprint.c,v 1.48 2003/06/01 12:38:22 dpgilbert Exp $" +const char* scsiprint_c_cvsid="$Id: scsiprint.c,v 1.48.2.1 2004/08/13 00:04:39 likewise Exp $" EXTERN_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID; // control block which points to external global control variables @@ -455,9 +455,11 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all) int err, len; int is_tape = 0; int peri_dt = 0; - - memset(gBuf, 0, 36); - if ((err = scsiStdInquiry(device, gBuf, 36))) { + /* Reset global MVSATA flag */ + con->ismvsata = 0; + + memset(gBuf, 0, 64); + if ((err = scsiStdInquiry(device, gBuf, 64))) { QUIETON(con); pout("Standard Inquiry failed [%s]\n", scsiErrString(err)); QUIETOFF(con); @@ -467,9 +469,21 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all) peri_dt = gBuf[0] & 0x1f; if (peripheral_type) *peripheral_type = peri_dt; - if (! all) - return 0; + if (!all) + { + /*Check vendor-specific section for presence of MVSATA controller*/ + if (len >= 42) + { + if (!strcmp(&gBuf[36], "MVSATA")) + { + con->ismvsata = 1; + return 1; + } + } + return 0; + } + if (len >= 36) { memset(manufacturer, 0, sizeof(manufacturer)); strncpy(manufacturer, &gBuf[8], 8); @@ -480,6 +494,17 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all) memset(revision, 0, sizeof(revision)); strncpy(revision, &gBuf[32], 4); pout("Device: %s %s Version: %s\n", manufacturer, product, revision); + + /*Check vendor-specific section for presence of MVSATA controller*/ + if (len >= 42) + { + if (!strcmp(&gBuf[36], "MVSATA")) + { + con->ismvsata = 1; + return 1; + } + } + if (0 == scsiInquiryVpd(device, 0x80, gBuf, 64)) { /* should use VPD page 0x83 and fall back to this page (0x80) * if 0x83 not supported. NAA requires a lot of decoding code */ @@ -641,6 +666,7 @@ static void failuretest(int type, int returnvalue) /* Main entry point used by smartctl command. Return 0 for success */ +/* Return FAILMVSATA for MVSATA controller */ int scsiPrintMain(const char *dev_name, int fd) { int checkedSupportedLogPages = 0; @@ -648,9 +674,21 @@ int scsiPrintMain(const char *dev_name, int fd) int returnval=0; int res; - if (scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo)) { - pout("Smartctl: SCSI device INQUIRY Failed\n\n"); - failuretest(MANDATORY_CMD, returnval |= FAILID); + returnval = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo); + + + if (returnval) + { + //Check for MVSATA controller + if (con->ismvsata) + { + return FAILMVSATA; + } + else + { + pout("Smartctl: SCSI device INQUIRY Failed\n\n"); + failuretest(MANDATORY_CMD, returnval |= FAILID); + } } if (con->smartenable) { diff --git a/sm5/smartctl.c b/sm5/smartctl.c index 8fbdf0a1f..452e2204e 100644 --- a/sm5/smartctl.c +++ b/sm5/smartctl.c @@ -44,7 +44,7 @@ #include "utility.h" extern const char *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid; -const char* smartctl_c_cvsid="$Id: smartctl.c,v 1.76 2003/05/01 08:51:46 dpgilbert Exp $" +const char* smartctl_c_cvsid="$Id: smartctl.c,v 1.76.2.1 2004/08/13 00:04:38 likewise Exp $" ATACMDS_H_CVSID ATAPRINT_H_CVSID EXTERN_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID; // This is a block containing all the "control variables". We declare @@ -701,9 +701,18 @@ int main (int argc, char **argv){ // now call appropriate ATA or SCSI routine if (tryata) + { retval = ataPrintMain(fd); + } else if (tryscsi) - retval = scsiPrintMain(device, fd); + { + con->ismvsata = 0; + retval = scsiPrintMain(device, fd); + if (con->ismvsata) + { + retval = ataPrintMain(fd); + } + } else { pout("Smartctl: specify if this is an ATA or SCSI device with the -d option.\n"); Usage(); diff --git a/sm5/smartctl.h b/sm5/smartctl.h index 1ef52f48a..742871514 100644 --- a/sm5/smartctl.h +++ b/sm5/smartctl.h @@ -26,7 +26,7 @@ #define __SMARTCTL_H_ #ifndef SMARTCTL_H_CVSID -#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.17 2003/03/06 07:27:17 ballen4705 Exp $\n" +#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.17.2.1 2004/08/13 00:04:38 likewise Exp $\n" #endif /* Boolean Values */ @@ -61,6 +61,9 @@ // Device had Errors in the self-test log #define FAILLOG (0x01<<7) +// Device is MVSATA +#define FAILMVSATA (0x01<<8) + // Classes of SMART commands. Here 'mandatory' means "Required by the // ATA/ATAPI-5 Specification if the device implements the S.M.A.R.T. // command set." The 'mandatory' S.M.A.R.T. commands are: (1) diff --git a/sm5/smartd.8 b/sm5/smartd.8 index 2c245cbef..3b7d64a4e 100644 --- a/sm5/smartd.8 +++ b/sm5/smartd.8 @@ -1,6 +1,6 @@ \# Copyright (C) 2002-3 Bruce Allen <smartmontools-support@lists.sourceforge.net> \# -\# $Id: smartd.8,v 1.98 2003/06/12 15:36:38 ballen4705 Exp $ +\# $Id: smartd.8,v 1.98.2.1 2004/08/13 00:04:38 likewise Exp $ \# \# This program is free software; you can redistribute it and/or modify it \# under the terms of the GNU General Public License as published by the Free @@ -16,7 +16,7 @@ \# Research Center), Jack Baskin School of Engineering, University of \# California, Santa Cruz. http://ssrc.soe.ucsc.edu/ \# -.TH SMARTD 8 "$Date: 2003/06/12 15:36:38 $" "smartmontools-5.1" +.TH SMARTD 8 "$Date: 2004/08/13 00:04:38 $" "smartmontools-5.1" .SH NAME smartd \- SMART Disk Monitoring Daemon .SH SYNOPSIS @@ -424,7 +424,8 @@ Specifies the type of the device. This Directive may be used multiple times for one device, but the arguments \fIata\fP and \fIscsi\fP are mutually-exclusive and if they are both used then .B smartd -will use the one that appears last. +will use the one that appears last. If the device specified as \fImvsata\fP it will +be treated as device connected to Marvell SATA controller. If neither \fIata\fP nor \fIscsi\fP are used then .B smartd @@ -447,6 +448,11 @@ from issuing SCSI commands to an ATA device. .B smartd from issuing ATA commands to a SCSI device. +.I mvsata +\- the device type is Marvell SATA controller. In this case +.B smartd +will issue ATA commands for the device using the vendor-specific command interface. + .I removable \- the device or its media is removable. This indicates to .B smartd @@ -662,7 +668,7 @@ is set to the device path (examples: /dev/hda, /dev/sdb). .nf .fi .B SMARTD_DEVICETYPE -is set to the device type (possible values: ata, scsi). +is set to the device type (possible values: ata, scsi, mvsata). .nf .fi .B SMARTD_FAILTYPE @@ -1257,4 +1263,4 @@ smartmontools home page at \fBhttp://smartmontools.sourceforge.net/\fP . .SH CVS ID OF THIS PAGE: -$Id: smartd.8,v 1.98 2003/06/12 15:36:38 ballen4705 Exp $ +$Id: smartd.8,v 1.98.2.1 2004/08/13 00:04:38 likewise Exp $ diff --git a/sm5/smartd.c b/sm5/smartd.c index 15b50571c..3fc8feb91 100644 --- a/sm5/smartd.c +++ b/sm5/smartd.c @@ -50,7 +50,7 @@ #include "utility.h" extern const char *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid; -const char *smartd_c_cvsid="$Id: smartd.c,v 1.170 2003/06/13 18:01:17 ballen4705 Exp $" +const char *smartd_c_cvsid="$Id: smartd.c,v 1.170.2.1 2004/08/13 00:04:38 likewise Exp $" ATACMDS_H_CVSID ATAPRINT_H_CVSID EXTERN_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID UTILITY_H_CVSID; // Forward declaration @@ -234,7 +234,10 @@ void printandmail(cfgfile *cfg, int which, int priority, char *fmt, ...){ // for user scripts setenv("SMARTD_MAILER", executable, 1); setenv("SMARTD_DEVICE", cfg->name, 1); - setenv("SMARTD_DEVICETYPE", cfg->tryata?"ata":"scsi", 1); + if (cfg->trymvsata) + setenv("SMARTD_DEVICETYPE", "mvsata", 1); + else + setenv("SMARTD_DEVICETYPE", cfg->tryata?"ata":"scsi", 1); setenv("SMARTD_MESSAGE", message, 1); setenv("SMARTD_SUBJECT", subject, 1); dateandtimezoneepoch(dates, mail->firstsent); @@ -435,7 +438,7 @@ void printhead(){ // prints help info for configuration file Directives void Directives() { printout(LOG_INFO,"Configuration file (/etc/smartd.conf) Directives (after device name):\n"); - printout(LOG_INFO," -d TYPE Set the device type: ata, scsi, removable\n"); + printout(LOG_INFO," -d TYPE Set the device type: ata, scsi, mvsata, removable\n"); printout(LOG_INFO," -T TYPE Set the tolerance to one of: normal, permissive\n"); printout(LOG_INFO," -o VAL Enable/disable automatic offline tests (on/off)\n"); printout(LOG_INFO," -S VAL Enable/disable attribute autosave (on/off)\n"); @@ -563,7 +566,8 @@ int atadevicescan2(atadevices_t *devices, cfgfile *cfg){ // device open failed return 1; printout(LOG_INFO,"Device: %s, opened\n", device); - + //set the global MVSATA flag for further ioctl() usage + con->ismvsata = cfg->trymvsata; // Get drive identity structure if (ataReadHDIdentity (fd,&drive)){ // Unable to read Identity structure @@ -759,6 +763,7 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg) int k, fd, err; char *device = cfg->name; struct scsi_iec_mode_page iec; + int peri_dt = 0, len = 0; UINT8 tBuf[64]; // should we try to register this as a SCSI device? @@ -774,7 +779,38 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg) #endif } printout(LOG_INFO,"Device: %s, opened\n", device); - + + //Check whether SCSI controller is MVSATA + err = scsiStdInquiry(fd, tBuf, sizeof(tBuf)); + if (err) + { + if (1 == err) + printout(LOG_ERR, "Device: %s, failed Inquiry [err=%d]\n", + device, err); + close(fd); + + } + len = tBuf[4] + 5; + peri_dt = tBuf[0] & 0x1f; + if (peri_dt == 0) /*TYPE_DISK*/ + { + if (len >= 42) + { + if (!strcmp(&tBuf[36], "MVSATA")) + { + /*MVSATA controller detected*/ + cfg->tryata = 1; + cfg->tryscsi = 0; + cfg->trymvsata = 1; + cfg->scsidevicenum = numscsidevices; + cfg->atadevicenum = -1; + closedevice(fd, device); + return 3; + } + } + } + //MVSATA + // check that it's ready for commands. IE stores its stuff on the media. if ((err = scsiTestUnitReady(fd))) { if (1 == err) @@ -853,6 +889,7 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg) // record number of device, type of device, increment device count cfg->tryata = 0; cfg->tryscsi = 1; + cfg->trymvsata = 0; cfg->scsidevicenum = numscsidevices; cfg->atadevicenum = -1; ++numscsidevices; @@ -862,6 +899,218 @@ static int scsidevicescan(scsidevices_t *devices, cfgfile *cfg) return 0; } +int mvsatadevicescan2(scsidevices_t *devices, cfgfile *cfg) +{ + int fd; + struct hd_driveid drive; + char *device=cfg->name; + + // should we try to register this as an ATA device? + if (!(cfg->trymvsata)) + return 1; + + // open the device + if ((fd=opendevice(device, O_RDONLY | O_NONBLOCK))<0) + // device open failed + return 1; + + /*set the global MVSATA flag for further ioctl()*/ + con->ismvsata = cfg->trymvsata; + if (cfg->trymvsata) + { + /*Reset ignored values*/ + devices->SmartPageSupported = 0; + devices->Temperature = 0; + devices->TempPageSupported = 0; + } + printout(LOG_INFO,"Device: %s, opened\n", device); + // Get drive identity structure + if (ataReadHDIdentity (fd,&drive)){ + // Unable to read Identity structure + printout(LOG_INFO,"Device: %s, unable to read Device Identity Structure\n",device); + close(fd); + return 2; + } + + // pass user setings on to low-level ATA commands + con->fixfirmwarebug = cfg->fixfirmwarebug; + + // Show if device in database, and use preset vendor attribute + // options unless user has requested otherwise. + if (!cfg->ignorepresets){ + + // do whatever applypresets decides to do + if (applypresets(&drive, cfg->attributedefs, con)<0) + printout(LOG_INFO, "Device: %s, not found in smartd database.\n", device); + else + printout(LOG_INFO, "Device: %s, found in smartd database.\n", device); + + // then save the correct state of the flag (applypresets may have changed it) + cfg->fixfirmwarebug = con->fixfirmwarebug; + } + else + printout(LOG_INFO, "Device: %s, smartd database not searched (Directive: -P ignore).\n", device); + + // If requested, show which presets would be used for this drive + if (cfg->showpresets) { + int savedebugmode=debugmode; + printout(LOG_INFO, "Device %s: presets are:\n", device); + if (!debugmode) + debugmode=2; + showpresets(&drive); + debugmode=savedebugmode; + } + + if (!cfg->permissive && !ataSmartSupport(&drive)){ + // SMART not supported + printout(LOG_INFO,"Device: %s, appears to lack SMART, use '-T permissive' Directive to try anyway.\n",device); + close(fd); + return 2; + } + + if (ataEnableSmart(fd)){ + // Enable SMART command has failed + printout(LOG_INFO,"Device: %s, could not enable SMART capability\n",device); + close(fd); + return 2; + } + + // disable device attribute autosave... + if (cfg->autosave==1){ + if (ataDisableAutoSave(fd)) + printout(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",device); + else + printout(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",device); + } + + // or enable device attribute autosave + if (cfg->autosave==2){ + if (ataEnableAutoSave(fd)) + printout(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",device); + else + printout(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",device); + } + + // capability check: SMART status + if (cfg->smartcheck && ataSmartStatus2(fd)==-1){ + printout(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",device); + cfg->smartcheck=0; + } + + // capability check: Read smart values and thresholds + if (cfg->usagefailed || cfg->prefail || cfg->usage || cfg->autoofflinetest) { + devices->smartval=(struct ata_smart_values *)calloc(1,sizeof(struct ata_smart_values)); + devices->smartthres=(struct ata_smart_thresholds *)calloc(1,sizeof(struct ata_smart_thresholds)); + + if (!devices->smartval || !devices->smartthres){ + printout(LOG_CRIT,"Not enough memory to obtain SMART data\n"); + exit(EXIT_NOMEM); + } + + if (ataReadSmartValues(fd,devices->smartval) || + ataReadSmartThresholds (fd,devices->smartthres)){ + printout(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",device); + free(devices->smartval); + free(devices->smartthres); + + // make it easy to recognize that we've deallocated + devices->smartval=NULL; + devices->smartthres=NULL; + cfg->usagefailed=cfg->prefail=cfg->usage=0; + } + } + + // enable/disable automatic on-line testing + if (cfg->autoofflinetest){ + // is this an enable or disable request? + char *what=(cfg->autoofflinetest==1)?"disable":"enable"; + if (!devices->smartval) + printout(LOG_INFO,"Device: %s, could not %s SMART Automatic Offline Testing.\n",device, what); + else { + // if command appears unsupported, issue a warning... + if (!isSupportAutomaticTimer(devices->smartval)) + printout(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",device); + // ... but then try anyway + if ((cfg->autoofflinetest==1)?ataDisableAutoOffline(fd):ataEnableAutoOffline(fd)) + printout(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", device, what); + else + printout(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", device, what); + } + } + + // capability check: self-test-log + if (cfg->selftest){ + int val; + + // see if device supports Self-test logging. Note that the + // following line is not a typo: Device supports self-test log if + // and only if it also supports error log. + if (!isSmartErrorLogCapable(devices->smartval)){ + printout(LOG_INFO, "Device: %s, does not support SMART Self-test Log.\n", device); + cfg->selftest=0; + cfg->selflogcount=0; + } + else { + // get number of Self-test errors logged + val=selftesterrorcount(fd, device); + if (val>=0) + cfg->selflogcount=val; + else + cfg->selftest=0; + } + } + + // capability check: ATA error log + if (cfg->errorlog){ + int val; + + // see if device supports error logging + if (!isSmartErrorLogCapable(devices->smartval)){ + printout(LOG_INFO, "Device: %s, does not support SMART Error Log.\n", device); + cfg->errorlog=0; + cfg->ataerrorcount=0; + } + else { + // get number of ATA errors logged + val=ataerrorcount(fd, device); + if (val>=0) + cfg->ataerrorcount=val; + else + cfg->errorlog=0; + } + } + + // If no tests available or selected, return + if (!(cfg->errorlog || cfg->selftest || cfg->smartcheck || + cfg->usagefailed || cfg->prefail || cfg->usage)) { + close(fd); + return 3; + } + + // Do we still have entries available? + if (numatadevices>=MAXATADEVICES){ + printout(LOG_CRIT,"smartd has found more than MAXATADEVICES=%d ATA devices.\n" + "Recompile code from " PROJECTHOME " with larger MAXATADEVICES\n",(int)numatadevices); + exit(EXIT_CCONST); + } + + // register device + printout(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",device); + + // we were called from a routine that has global storage for the name. Keep pointer. + devices->devicename=device; + devices->cfg=cfg; + cfg->tryscsi=0; + cfg->tryata=1; + cfg->trymvsata=1; + cfg->atadevicenum = -1; + cfg->scsidevicenum = numscsidevices; + numscsidevices++; + // close file descriptor + closedevice(fd, device); + return 0; +} + // We compare old and new values of the n'th attribute. Note that n // is NOT the attribute ID number.. If (Normalized & Raw) equal, // then return 0, else nonzero. @@ -965,7 +1214,8 @@ int ataCheckDevice(atadevices_t *drive){ printandmail(cfg, 9, LOG_CRIT, "Device: %s, unable to open device", name); return 1; } - + //set MVSATA global flag for further ioctl() + con->ismvsata = cfg->trymvsata; // check smart status if (cfg->smartcheck){ int status=ataSmartStatus2(fd); @@ -1122,6 +1372,16 @@ int ataCheckDevice(atadevices_t *drive){ return 0; } +/* Calls ataCheckDevice instead of scsi */ +int mvsataCheckDevice(scsidevices_t *drive) +{ + atadevices_t atadrive; + atadrive.devicename = drive->devicename; + atadrive.cfg = drive->cfg; + atadrive.smartthres = drive->smartthres; + atadrive.smartval = drive->smartval; + return ataCheckDevice(&atadrive); +} int scsiCheckDevice(scsidevices_t *drive) { @@ -1191,7 +1451,12 @@ void CheckDevices(atadevices_t *atadevices, scsidevices_t *scsidevices){ ataCheckDevice(atadevices+i); for (i=0; i<numscsidevices; i++) - scsiCheckDevice(scsidevices+i); + { + if (scsidevices[i].cfg->trymvsata) + mvsataCheckDevice(scsidevices+i); + else + scsiCheckDevice(scsidevices+i); + } // This option is primarily for distribution developers who want // an automated procedure for seeing if smartd works correctly. @@ -1372,12 +1637,21 @@ int parsetoken(char *token,cfgfile *cfg){ } else if (!strcmp(arg, "ata")) { cfg->tryata = 1; cfg->tryscsi = 0; + cfg->trymvsata = 0; } else if (!strcmp(arg, "scsi")) { cfg->tryscsi = 1; cfg->tryata = 0; + cfg->trymvsata = 0; } else if (!strcmp(arg, "removable")) { cfg->removable = 1; - } else { + } + //set the MVSATA flag if device type is "mvsata" + else if (!strcmp(arg, "mvsata")) { + cfg->tryscsi = 0; + cfg->tryata = 1; + cfg->trymvsata = 1; + } + else { badarg = 1; } break; @@ -1638,7 +1912,8 @@ int parseconfigline(int entry, int lineno,char *line){ cfg->lineno=lineno; cfg->tryscsi=1; cfg->tryata=1; - + //reset the MVSATA flag: will be detected automatically if present + cfg->trymvsata=0; // Try and recognize if a IDE or SCSI device. These can be // overwritten by configuration file directives. if (GUESS_DEVTYPE_ATA == guess_linux_device_type(name)) @@ -2087,7 +2362,8 @@ int makeconfigentries(int num, char *name, int isata, int start, int scandirecti // select if it's a SCSI or ATA device cfg->tryata=isata; cfg->tryscsi=!isata; - + //reset trymvsata flag + cfg->trymvsata = 0; // put in the device name cfg->name=strdup(name); if (!cfg->name) { @@ -2118,6 +2394,7 @@ int main (int argc, char **argv){ atadevices_t atadevices[MAXATADEVICES], *atadevicesptr=atadevices; scsidevices_t scsidevices[MAXSCSIDEVICES], *scsidevicesptr=scsidevices; int i, entries, scandirective=0, scanning=0; + int res = 0; smartmonctrl control; // initialize global communications variables @@ -2160,8 +2437,7 @@ int main (int argc, char **argv){ // initialize total number of entries to seach for entries=0; doata=config->tryata; - doscsi=config->tryscsi; - + doscsi=config->tryscsi || config->trymvsata; // make list of ATA devices to search for if (doata) entries+=makeconfigentries(MAXATADEVICES, "/dev/hda", 1, entries, scandirective); @@ -2175,17 +2451,41 @@ int main (int argc, char **argv){ int notregistered=1; // register ATA devices - if (config[i].tryata){ - if (atadevicescan2(atadevicesptr+numatadevices, config+i)) - cantregister(config[i].name, "ATA", config[i].lineno, scandirective); + if (config[i].trymvsata) + { + /*scan device connected to MVSATA*/ + if (mvsatadevicescan2(scsidevicesptr+numscsidevices, config+i)) + cantregister(config[i].name, "MVSATA", config[i].lineno, scandirective); else - notregistered=0; + notregistered=0; + } + else + { + if (config[i].tryata) + { + if (atadevicescan2(atadevicesptr+numatadevices, config+i)) + cantregister(config[i].name, "ATA", config[i].lineno, scandirective); + else + notregistered=0; + } } // then register SCSI devices - if (config[i].tryscsi){ - if (scsidevicescan(scsidevicesptr+numscsidevices, config+i)) - cantregister(config[i].name, "SCSI", config[i].lineno, scandirective); + if (config[i].tryscsi) + { + config[i].trymvsata = 0; + res = scsidevicescan(scsidevicesptr+numscsidevices, config+i); + if (res) + { + //if MVSATA controller detected rescan with ATA options + if (config[i].trymvsata) + { + i--; + continue; + } + else + cantregister(config[i].name, "SCSI", config[i].lineno, scandirective); + } else notregistered=0; } diff --git a/sm5/smartd.h b/sm5/smartd.h index 15f76ac25..ad0169a3e 100644 --- a/sm5/smartd.h +++ b/sm5/smartd.h @@ -23,7 +23,7 @@ */ #ifndef SMARTD_H_CVSID -#define SMARTD_H_CVSID "$Id: smartd.h,v 1.36 2003/05/12 18:34:40 pjwilliams Exp $\n" +#define SMARTD_H_CVSID "$Id: smartd.h,v 1.36.2.1 2004/08/13 00:04:38 likewise Exp $\n" #endif // Configuration file @@ -97,6 +97,7 @@ typedef struct configfile_s { // which type of device was detected. char tryata; char tryscsi; + char trymvsata; char *name; // which tests have been enabled? char smartcheck; @@ -158,9 +159,14 @@ typedef struct atadevices_s { // used to store a list of SCSI devices to monitor. Devicename points // to a malloced name string. typedef struct scsidevices_s { + /* ignored for MVSATA adapter */ unsigned char SmartPageSupported; unsigned char TempPageSupported; unsigned char Temperature; + /* MVSATA support: ata parameters */ + struct ata_smart_values *smartval; + struct ata_smart_thresholds *smartthres; + /* end MVSATA support */ char *devicename; cfgfile *cfg; } scsidevices_t; -- GitLab