C-program to send Identify-Namespace command to an NVMe storage drive

In this blog, we will send an Identify command to fetch the Identify-Namespace data. The code explained here, is written for Ubuntu 16.04.

Every NVMe drive has to support an Identify command. In the case of SATA 512 bytes of data is returned upon running of Identify command – it identifies the storage drive as well as tells about it capabilities. In the case of NVMe protocol, NVMe too supports an Identify command but it returns two types of data – controller-identify and namespace-identify. The controller-identify data would give the capabilities of SSD controller and the namespace-identify data would give the capabilities of a  namespace. Identify data is 4096 bytes long.

The Namespace is a Flash Memory Partition. It gives many benefits like – we can separate a submission queue for a namespace thus, minimising the arbitration, we can also have different LBA lengths for different namespaces and similarly different other features.

The Identify-Namespace gives the capabilities of all the namespace supported by the drive. The number of namespaces supported by your drive can be ascertained from Identify-Controller data. Every drive must support at least one namespace – spanning the complete drive. All the namespaces must add up to total drive size.

Steps involved in getting Identify-controller data.
step1: Open the device file corresponding to your drive in O_RDWR mode.
step2: Allocate a buffer of 4096 bytes.
step3: Prepare the nvme_admin_cmd structure – defined in “nvme_ioctl.h”.
step 4: Call the IOCTL with NVME_IOCTL_ADMIN_CMD as a magic number.
step 5: Interpret the data in the buffer.

Step 1: Opening the device file in O_RDWR mode.
Everything is a file in Linux even your storage drives and to communicate with it, we must open it in a suitable mode. The name of the device file is passed into the code from the terminal as an argument to the executable. O_RDONLY would also do here.

int fd;
fd = open(argv[1],O_RDWR);
if(fd == 0){
printf(“could not open device file\n”);
return 1;
}else printf(“device file opened successfully\n”);

Step 2: Allocating a buffer of 4096 bytes. We can use char array or we can malloc it.

char data[IDENTIFY_LEN];

Step 3: Prepare the nvme_admin_cmd.
This structure is defined in nvme_ioctl.h and it eases up the sending of admin commands (admin commands like Identify, Set-Feature and Get-Log). There are few fields in the nvme_admin_cmd structure that we need to initialize to prepare the Identify command. Command Dword 10 is one of them it decides which identify data to be returned for identify command i.e data for controller-identify or namespace-identify. So, CWD10 decides the type of identify command to perform.

struct nvme_admin_cmd cmd = {

.opcode = 0x06, /*for identify command*/
.nsid = NAMESPACE_ID, /*providing namespace id whose data we want*/
.addr = (int)data, /*address where returned data to be stored*/
.data_len = IDENTIFY_LEN,/*length of the data – 0x1000*/
.cdw10 = 0,/*command DWord 10 decides the type of the identify – i.e. wheather to return data correspoonding to controller or data corresponding to a namespace*/

};

Step 4: Call the IOCTL.
To launch the command we need to call the IOCTL with NVME_IOCTL_ADMIN_CMD as the magic number. To the IOCTL we also need to pass the nvme_admin_cmd structure object – “cmd” here.

ret= ioctl(fd,NVME_IOCTL_ADMIN_CMD,&cmd);

if(ret==0) printf(“successful \n”);
else printf(“failed %d\n”,ret);

Below is the complete code to get the namespace-identify data and interpret it.

GITHUB Link for downloading the Code.

#include<stdio.h>
#include<stdlib.h>
#include<sys/ioctl.h>
#include<unistd.h>
#include<linux/nvme_ioctl.h>
#include<fcntl.h>
#include<linux/types.h>

#define IDENTIFY_LEN 0x1000
#define NAMESPACE_ID 1

int main(int argc, char* argv[]){
if(argc < 2){
printf(“kindly give the device file name\n”);
return 1;

}
int fd;
fd = open(argv[1],O_RDWR);
if(fd == 0){
printf(“could not open device file\n”);
return 1;
}else printf(“device file opened successfully\n”);

char data[IDENTIFY_LEN];
for(register int i=0; i<IDENTIFY_LEN;data[i++]=0);
struct nvme_admin_cmd cmd = {
.opcode = 0x06, /*for identify command*/
.nsid = NAMESPACE_ID, /*providing namespace id whose data we want*/
.addr = (int)data, /*address where returned data to be stored*/
.data_len = IDENTIFY_LEN,/*length of the data – 0x1000*/
.cdw10 = 0,/*command DWord 10 decides the type of the identify – i.e. wheather to return data correspoonding to controller or data corresponding to a namespace*/

};

int ret;
ret= ioctl(fd,NVME_IOCTL_ADMIN_CMD,&cmd);
if(ret==0) printf(“successful \n”);
else printf(“failed %d\n”,ret);

printf(“IDENTIFY DETAILS\n”);
printf(“NAMESPACE SIZE(MAX LBA): %lld\n”,*((__u64*)data));
printf(“NAMESPACE CAPACITY(ALLOWED LBA – THIN OVERPROVISIONING): %lld\n”,*((__u64*)(data+8)));
printf(“NAMESPACE UTILIZATION: %lld\n”,*((__u64*)(data+16)));
if(data[24]&0x1)
printf(“Namespace supports Thin OverProvisioning(NAMESPZE CAPACITY reported may be less than SIZE)\n”);
else printf(“Thin Overprovisioning not supported\n”);
printf(“NUMBER OF LBA FORMATS: %d\n”,(__u8)data[25]);
printf(“FORMATED LBA SIZE: %d\n”,(__u8)data[26]);
printf(“LBA FORMATE 0: METADAT SIZE: %d\n”, *((__u16*)(data+128)));
printf(“LBA FORMATE 0: LBA DATA SIZE(2^n): %d\n”, *((__u8*)(data+130)));

return 0;
}

Below is the screen-shot of an output:

Screenshot from 2017-06-19 17-15-44

 

GOOD DAY;

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s