Skip to content

Commit

Permalink
os_darwin: initial NVMe support, updating NEWS
Browse files Browse the repository at this point in the history
git-svn-id: https://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools@4436 4ea69e1a-61f1-4043-bf83-b5c94c648137
  • Loading branch information
samm2 committed Sep 20, 2017
1 parent 6a02e54 commit 8bf07d1
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 12 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ $Id$
2017-09-20 Alex Samorukov <[email protected]>

os_freebsd: use /dev/nvme/nvme.h on the recent versions
os_darwin: initial NVMe support for the darwin platform.

2017-08-08 Christian Franke <[email protected]>

Expand Down
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ http://sourceforge.net/p/smartmontools/code/HEAD/tree/trunk/smartmontools/NEWS
Date <Not released yet, please try current SVN or daily builds>
Summary: smartmontools release 6.6
-----------------------------------------------------------
- OS/2: Support for the OS2AHCI driver, updating source code,
adding autoscan support, adding self-test support
- Darwin: Initial NVMe support based on undocumented API.
- Device type '-d intelliprop,N' for IntelliProp controllers.
- smartctl: Control ATA write cache through SCT Feature Control
with '-s wcache-sct,ata|on|off[,p]' and '-g wcache-sct'.
Expand Down
112 changes: 100 additions & 12 deletions os_darwin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "int64.h"
#include "atacmds.h"
#include "scsicmds.h"
#include "nvmecmds.h"
#include "utility.h"
#include "os_darwin.h"
#include "dev_interface.h"
Expand Down Expand Up @@ -75,7 +76,8 @@ static const char smartctl_examples[] =
static struct {
io_object_t ioob;
IOCFPlugInInterface **plugin;
IOATASMARTInterface **smartIf;
IOATASMARTInterface **smartIf; // ATA devices
IONVMeSMARTInterface **smartIfNVMe;
} devices[20];

const char * dev_darwin_cpp_cvsid = "$Id$"
Expand Down Expand Up @@ -109,7 +111,6 @@ class darwin_smart_device
int get_fd() const
{ return m_fd; }


private:
int m_fd; ///< filedesc, -1 if not open.
const char * m_mode; ///< Mode string for deviceopen().
Expand All @@ -128,15 +129,24 @@ bool darwin_smart_device::is_open() const
}

// Determine whether 'dev' is a SMART-capable device.
static bool is_smart_capable (io_object_t dev) {
CFTypeRef smartCapableKey;
static bool is_smart_capable (io_object_t dev, const char * type) {
CFTypeRef smartCapableKey = NULL;
CFDictionaryRef diskChars;

// If the device has kIOPropertySMARTCapableKey, then it's capable,
// no matter what it looks like.
if (!strcmp("ATA", type)) {
smartCapableKey = IORegistryEntryCreateCFProperty
(dev, CFSTR (kIOPropertySMARTCapableKey),
kCFAllocatorDefault, 0);
}

else if (!strcmp("NVME", type)) {
smartCapableKey = IORegistryEntryCreateCFProperty
(dev, CFSTR (kIOPropertyNVMeSMARTCapableKey),
kCFAllocatorDefault, 0);
}

if (smartCapableKey)
{
CFRelease (smartCapableKey);
Expand All @@ -145,6 +155,7 @@ static bool is_smart_capable (io_object_t dev) {

// If it's an kIOATABlockStorageDeviceClass then we're successful
// only if its ATA features indicate it supports SMART.
// This will be broken for NVMe, however it is not needed
if (IOObjectConformsTo (dev, kIOATABlockStorageDeviceClass)
&& (diskChars = (CFDictionaryRef)IORegistryEntryCreateCFProperty
(dev, CFSTR (kIOPropertyDeviceCharacteristicsKey),
Expand Down Expand Up @@ -180,7 +191,7 @@ bool darwin_smart_device::open()
const char *pathname = get_dev_name();
char *type = const_cast<char*>(m_mode);

if (strcmp (type, "ATA") != 0)
if (!(strcmp("ATA", type) || strcmp("NVME", type)))
{
set_err (EINVAL);
return false;
Expand All @@ -205,7 +216,7 @@ bool darwin_smart_device::open()
// allow user to just say 'disk0'
devname = pathname;

// Find the device.
// Find the device. This part should be the same for the NVMe and ATA
if (devname)
{
CFMutableDictionaryRef matcher;
Expand All @@ -224,7 +235,7 @@ bool darwin_smart_device::open()
}

// Find a SMART-capable driver which is a parent of this device.
while (! is_smart_capable (disk))
while (! is_smart_capable (disk, type))
{
IOReturn err;
io_object_t prevdisk = disk;
Expand All @@ -246,17 +257,35 @@ bool darwin_smart_device::open()

devices[devnum].plugin = NULL;
devices[devnum].smartIf = NULL;
devices[devnum].smartIfNVMe = NULL;

CFUUIDRef pluginType = NULL;
CFUUIDRef smartInterfaceId = NULL;
void ** SMARTptr = NULL;

if (!strcmp("ATA", type)) {
pluginType = kIOATASMARTUserClientTypeID;
smartInterfaceId = kIOATASMARTInterfaceID;
SMARTptr = (void **)&devices[devnum].smartIf;
}
else if (!strcmp("NVME", type)) {
pluginType = kIONVMeSMARTUserClientTypeID;
smartInterfaceId = kIONVMeSMARTInterfaceID;
SMARTptr = (void **)&devices[devnum].smartIfNVMe;
}

// Create an interface to the ATA SMART library.
if (IOCreatePlugInInterfaceForService (disk,
kIOATASMARTUserClientTypeID,
pluginType,
kIOCFPlugInInterfaceID,
&devices[devnum].plugin,
&dummy) == kIOReturnSuccess)
(*devices[devnum].plugin)->QueryInterface
(devices[devnum].plugin,
CFUUIDGetUUIDBytes ( kIOATASMARTInterfaceID),
(void **)&devices[devnum].smartIf);
CFUUIDGetUUIDBytes ( smartInterfaceId),
SMARTptr);
else
return set_err(ENOSYS, "IOCreatePlugInInterfaceForService failed");
}


Expand All @@ -273,6 +302,8 @@ bool darwin_smart_device::close()
int fd = m_fd; m_fd = -1;
if (devices[fd].smartIf)
(*devices[fd].smartIf)->Release (devices[fd].smartIf);
if (devices[fd].smartIf)
(*devices[fd].smartIfNVMe)->Release (devices[fd].smartIfNVMe);
if (devices[fd].plugin)
IODestroyPlugInInterface (devices[fd].plugin);
IOObjectRelease (devices[fd].ioob);
Expand Down Expand Up @@ -305,7 +336,7 @@ static int make_device_names (char*** devlist, const char* name) {
// Count the devices.
result = 0;
while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
if (is_smart_capable (device))
if (is_smart_capable (device, name))
result++;
IOObjectRelease (device);
}
Expand All @@ -317,7 +348,7 @@ static int make_device_names (char*** devlist, const char* name) {
*devlist = (char**)calloc (result, sizeof (char *));
index = 0;
while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
if (is_smart_capable (device))
if (is_smart_capable (device, name))
{
io_string_t devName;
IORegistryEntryGetPath(device, kIOServicePlane, devName);
Expand Down Expand Up @@ -504,11 +535,63 @@ class darwin_smart_interface

virtual scsi_device * get_scsi_device(const char * name, const char * type);

virtual nvme_device * get_nvme_device(const char * name, const char * type,
unsigned nsid);

virtual smart_device * autodetect_smart_device(const char * name);

};

/////////////////////////////////////////////////////////////////////////////
/// NVMe support

class darwin_nvme_device
: public /*implements*/ nvme_device,
public /*extends*/ darwin_smart_device
{
public:
darwin_nvme_device(smart_interface * intf, const char * dev_name,
const char * req_type, unsigned nsid);

virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
};

darwin_nvme_device::darwin_nvme_device(smart_interface * intf, const char * dev_name,
const char * req_type, unsigned nsid)
: smart_device(intf, dev_name, "nvme", req_type),
nvme_device(nsid),
darwin_smart_device("NVME")
{
}

bool darwin_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
{
int fd = get_fd();
IONVMeSMARTInterface **ifp = devices[fd].smartIfNVMe;
IONVMeSMARTInterface *smartIfNVMe ;
IOReturn err = 0;
unsigned int page = in.cdw10 & 0xff;

if (! ifp)
return -1;
smartIfNVMe = *ifp;
// currently only GetIdentifyData and SMARTReadData are supported
switch (in.opcode) {
case smartmontools::nvme_admin_identify:
err = smartIfNVMe->GetIdentifyData(ifp, (struct nvme_id_ctrl *) in.buffer, in.nsid); // FIXME
break;
case smartmontools::nvme_admin_get_log_page:
if(page == 0x02)
err = smartIfNVMe->SMARTReadData(ifp, (struct nvme_smart_log *) in.buffer);
else /* GetLogPage() is not working yet */
return set_err(ENOSYS, "NVMe admin command:0x%02x/page:0x%02x is not supported",
in.opcode, page);
break;
default:
return set_err(ENOSYS, "NVMe admin command 0x%02x is not supported", in.opcode);
}
return true;
}
//////////////////////////////////////////////////////////////////////

std::string darwin_smart_interface::get_os_version_str()
Expand Down Expand Up @@ -537,6 +620,11 @@ scsi_device * darwin_smart_interface::get_scsi_device(const char *, const char *
return 0; // scsi devices are not supported [yet]
}

nvme_device * darwin_smart_interface::get_nvme_device(const char * name, const char * type,
unsigned nsid)
{
return new darwin_nvme_device(this, name, type, nsid);
}

smart_device * darwin_smart_interface::autodetect_smart_device(const char * name)
{
Expand Down
91 changes: 91 additions & 0 deletions os_darwin.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,95 @@
#define kIOPropertySMARTCapableKey "SMART Capable"
#endif

// NVMe definitions, non documented, experimental
#define kIOPropertyNVMeSMARTCapableKey "NVMe SMART Capable"

// Constant to init driver
#define kIONVMeSMARTUserClientTypeID CFUUIDGetConstantUUIDWithBytes(NULL, \
0xAA, 0x0F, 0xA6, 0xF9, 0xC2, 0xD6, 0x45, 0x7F, 0xB1, 0x0B, \
0x59, 0xA1, 0x32, 0x53, 0x29, 0x2F)

// Constant to use plugin interface
#define kIONVMeSMARTInterfaceID CFUUIDGetConstantUUIDWithBytes(NULL, \
0xcc, 0xd1, 0xdb, 0x19, 0xfd, 0x9a, 0x4d, 0xaf, 0xbf, 0x95, \
0x12, 0x45, 0x4b, 0x23, 0xa, 0xb6)

// interface structure, obtained using lldb, could be incomplete or wrong
typedef struct IONVMeSMARTInterface
{
IUNKNOWN_C_GUTS;

UInt16 version;
UInt16 revision;

// NVMe smart data, returns nvme_smart_log structure
IOReturn ( *SMARTReadData )( void * interface,
struct nvme_smart_log * NVMeSMARTData );

// NVMe IdentifyData, returns nvme_id_ctrl per namespace
IOReturn ( *GetIdentifyData )( void * interface,
struct nvme_id_ctrl * NVMeIdentifyControllerStruct,
unsigned int ns );

// Always getting kIOReturnDeviceError
IOReturn ( *GetFieldCounters )( void * interface,
char * FieldCounters );
// Returns 0
IOReturn ( *ScheduleBGRefresh )( void * interface);

// Always returns kIOReturnDeviceError, probably expects pointer to some
// structure as an argument
IOReturn ( *GetLogPage )( void * interface, void * data, unsigned int, unsigned int);


/* GetSystemCounters Looks like a table with an attributes. Sample result:
0x101022200: 0x01 0x00 0x08 0x00 0x00 0x00 0x00 0x00
0x101022208: 0x00 0x00 0x00 0x00 0x02 0x00 0x08 0x00
0x101022210: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x101022218: 0x03 0x00 0x08 0x00 0xf1 0x74 0x26 0x01
0x101022220: 0x00 0x00 0x00 0x00 0x04 0x00 0x08 0x00
0x101022228: 0x0a 0x91 0xb1 0x00 0x00 0x00 0x00 0x00
0x101022230: 0x05 0x00 0x08 0x00 0x24 0x9f 0xfe 0x02
0x101022238: 0x00 0x00 0x00 0x00 0x06 0x00 0x08 0x00
0x101022240: 0x9b 0x42 0x38 0x02 0x00 0x00 0x00 0x00
0x101022248: 0x07 0x00 0x08 0x00 0xdd 0x08 0x00 0x00
0x101022250: 0x00 0x00 0x00 0x00 0x08 0x00 0x08 0x00
0x101022258: 0x07 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x101022260: 0x09 0x00 0x08 0x00 0x00 0x00 0x00 0x00
0x101022268: 0x00 0x00 0x00 0x00 0x0a 0x00 0x04 0x00
.........
0x101022488: 0x74 0x00 0x08 0x00 0x00 0x00 0x00 0x00
0x101022490: 0x00 0x00 0x00 0x00 0x75 0x00 0x40 0x02
0x101022498: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
*/
IOReturn ( *GetSystemCounters )( void * interface, char *, unsigned int *);


/* GetAlgorithmCounters returns mostly 0
0x102004000: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004008: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004010: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004018: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004020: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004028: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004038: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004040: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004048: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004050: 0x00 0x00 0x00 0x00 0x80 0x00 0x00 0x00
0x102004058: 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004060: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004068: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004070: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004078: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004080: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004088: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x102004090: 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00
0x102004098: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
*/
IOReturn ( *GetAlgorithmCounters )( void * interface, char *, unsigned int *);
} IONVMeSMARTInterface;


#endif /* OS_DARWIN_H_ */

0 comments on commit 8bf07d1

Please sign in to comment.