#include #include #include #include #include // isxdigit #include #include #include unsigned char hex2digit(char c) { if((c >= '0') && (c <= '9')) { return c - '0'; } else if ((c >= 'a') && (c <= 'f')) return c - 'a' + 10; else if ((c >= 'A') && (c <= 'F')) return c - 'F' + 10; else { printf("invalid hex digit: %d\n", (int)c); return 0; } } void hexString2uchar(char *s, int *val) { int low_byte, high_byte; if (strlen(s) == 1) { high_byte = hex2digit(s[0]); *val = high_byte; } else if (strlen(s) == 2) { low_byte = hex2digit(s[1]); high_byte = hex2digit(s[0]); *val = ((high_byte<<4) + low_byte); } else //if (strlen(s) > 2) { printf("hex to uchar failed\n"); } } typedef struct { unsigned char bus; // This is static info and provided by BIOS unsigned char dev:5; unsigned char fun:3; } PCI_BDF; #define SUPPORTED_NS_COUNT 1 typedef struct { PCI_BDF pci_bdf; // BDF is assigned by BIOS int nvme_dev_id; // This is id of "/dev/nvme%d", added when present int ns_cnt; char ns_dev[SUPPORTED_NS_COUNT][30]; // char firmware_ver[]; } NVME_DRIVE_SLOT_MAPPING; #define INVALID_NVME_DEV_ID (-1) NVME_DRIVE_SLOT_MAPPING nvme_slot_mapping[24] = { // PLX1 {{0x73, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 1 {{0x74, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 2 {{0x6a, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 3 {{0x6b, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 4 {{0x6c, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 5 {{0x6d, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 6 {{0x68, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 7 {{0x69, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 8 {{0x6e, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 9 {{0x6f, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 10 {{0x70, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 11 {{0x71, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 12 // PLX0 {{0x1e, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 13 {{0x1f, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 14 {{0x1b, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 15 {{0x1c, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 16 {{0x19, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 17 {{0x1a, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 18 {{0x21, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 19 {{0x22, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 20 {{0x24, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 21 {{0x25, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 22 {{0x26, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 23 {{0x27, 0x00, 0x00}, INVALID_NVME_DEV_ID, 0}, // slot 24 }; typedef struct { char devpath[32+1]; char serial_number[20+1]; char model_number[40+1]; int nsid; unsigned long long lba_count; // NSZE int lba_size; // LBADS } SCANED_NVME_DEV; int nvme_ssd_count; int scan_nvme_ssd(void) { DIR *dir; struct dirent *ptr; DIR *dir2; struct dirent *ptr2; char base[40]; char link[32]; const char *nvme_sys_path = "/sys/class/nvme"; //const char *nvme_sys_path_block = "/sys/class/nvme"; char nvme_sys_path_block[100]; int pci_bus, pci_dev, pci_fun; int nvme_dev_id; // stringstream ss; int i; #if 0 const char *cmd1 = "nvme list | awk 'NR>2{print $1}' | wc -l"; const char *cmd2 = "nvme list"; // const char *temp_filename = "/tmp/nvme_list_temp.txt"; char cmd_buf[64]; char cmd_result[16384]; // 16k buffer FILE *fp = NULL; char *p_str; char *p_str_end; int nvmedev_cnt; SCANED_NVME_DEV *nvme_dev_info; memset(cmd_buf, 0, sizeof(cmd_buf)); snprintf(cmd_buf, sizeof(cmd_buf), cmd1); fp = popen(cmd_buf, "r"); if (fp == NULL) { printf("ShowAllData fail\n" ); return PAL_DRIVE_FAILED; } memset(cmd_result, 0, sizeof(cmd_result fgets(cmd_result, sizeof(cmd_result)-1, fp); close(fp); nvmedev_cnt = atoi(cmd_result); nvme_dev_info = calloc(nvmedev_cnt, sizeof(SCANED_NVME_DEV)); if (nvme_dev_info == NULL) { printf("ShowAllData fail\n" ); return PAL_DRIVE_FAILED; } p_str = strchr(cmd_result, '\n'); // skip line 1 p_str ++; p_str = strchr(p_str, '\n'); // skip line 2 p_str++; while (p_str < cmd_buf+strlen(cmd_buf)) { p_str_end = strchr(p_str, '\n'); } #endif dir = opendir(nvme_sys_path); if (dir == NULL) { return -1; } nvme_ssd_count = 0; for (i = 0; i < sizeof(nvme_slot_mapping)/sizeof(NVME_DRIVE_SLOT_MAPPING); i++) { nvme_slot_mapping[i].nvme_dev_id = INVALID_NVME_DEV_ID; nvme_slot_mapping[i].ns_cnt = 0; nvme_slot_mapping[i].ns_dev[0][0] = 0; //printf("nvme_slot_mapping[%d].pci_bdf.bus = %d\n", i, nvme_slot_mapping[i].pci_bdf.bus); //printf("nvme_slot_mapping[%d].pci_bdf.dev = %d\n", i, nvme_slot_mapping[i].pci_bdf.dev); //printf("nvme_slot_mapping[%d].pci_bdf.fun = %d\n", i, nvme_slot_mapping[i].pci_bdf.fun); } while ((ptr=readdir(dir)) != NULL) { //if (ptr->d_type == 8) //file if ((ptr->d_type == 10 || //link file ptr->d_type == 4) && /*dir*/ (strncmp(ptr->d_name, "nvme", 4) == 0) /* d_name = "nvmeX" */ ) { for (i=4; id_name); i++) { if (!isdigit(ptr->d_name[i])) { break; } } if (i != strlen(ptr->d_name)) continue; nvme_dev_id = atoi(&ptr->d_name[4]); memset(base, 0, sizeof(base)); strcpy(base, nvme_sys_path); strcat(base, "/"); strcat(base, ptr->d_name); strcat(base, "/"); strcat(base, "device"); // base is "/sys/class/nvme/nvmeX/device" readlink(base, link, sizeof(link)); // link is "../../../0000:24:00.0" //printf("base = %s, link = %s\n", base, link); if (strncmp(link, "../../../", 9) != 0 || !isxdigit(link[9]) || !isxdigit(link[10]) || !isxdigit(link[11]) || !isxdigit(link[12]) || link[13] != ':' || !isxdigit(link[14]) || !isxdigit(link[15]) || link[16] != ':' || !isxdigit(link[17]) || !isxdigit(link[18]) || link[19] != '.' || !isxdigit(link[20])) { printf("get link result wrong\n"); continue; } link[13] = 0; link[16] = 0; link[19] = 0; /* ss.str(""); ss.clear(); ss << hex << (&link[14]); ss >> pci_bus; ss.str(""); ss.clear(); ss << hex << (&link[17]); ss >> pci_dev; ss.str(""); ss.clear(); ss << hex << (&link[20]); ss >> pci_fun; */ hexString2uchar(&link[14], &pci_bus); hexString2uchar(&link[17], &pci_dev); hexString2uchar(&link[20], &pci_fun); //printf("bdf = %x:%x.%x\n", pci_bus, pci_dev, pci_fun); for (i = 0; i < sizeof(nvme_slot_mapping)/sizeof(NVME_DRIVE_SLOT_MAPPING); i++) { if (nvme_slot_mapping[i].pci_bdf.bus == (unsigned char)pci_bus && nvme_slot_mapping[i].pci_bdf.dev == (unsigned char)pci_dev && nvme_slot_mapping[i].pci_bdf.fun == (unsigned char)pci_fun) { char expected_ns_prefix[100]; int expected_ns_prefix_len; nvme_slot_mapping[i].nvme_dev_id = nvme_dev_id; //printf("nvme_slot_mapping[%d].nvme_dev_id = %d\n", i, nvme_slot_mapping[i].nvme_dev_id); // Search for block devices (namespaces) snprintf(nvme_sys_path_block, sizeof(nvme_sys_path_block), "/sys/class/nvme/nvme%d/", nvme_dev_id); snprintf(expected_ns_prefix, sizeof(expected_ns_prefix), "nvme%dn", nvme_dev_id); expected_ns_prefix_len = strlen(expected_ns_prefix); dir2 = opendir(nvme_sys_path_block); while ((ptr2 = readdir(dir2)) != NULL) { //printf("ptr2->d_name = %s\n", ptr2->d_name); if (ptr2->d_type == 4) /*link*/ { int m; for (m = 0; m < expected_ns_prefix_len; m++) { if (ptr2->d_name[m] != expected_ns_prefix[m]) { break; } } if (m < expected_ns_prefix_len) { // doesn't match nvme%dn%d continue; } if (nvme_slot_mapping[i].ns_cnt >= SUPPORTED_NS_COUNT) { printf("Warning: NVMe SSD on slot %d (%s) has more than %d namespaces?\n", i, ptr2->d_name, SUPPORTED_NS_COUNT); continue; } strcpy(nvme_slot_mapping[i].ns_dev[nvme_slot_mapping[i].ns_cnt], ptr2->d_name); nvme_slot_mapping[i].ns_cnt++; nvme_ssd_count ++; } } // check the namespace break; } } } } closedir(dir); return 0; } int createNvmeNodes( void ) { int i; char cmd_buf[64]; // char cmd_result[64]; // FILE *fp = NULL; // int major, minor; // char *p_str; int remove_node; #if 0 // Remove old nodes. for (i=0; i 0) { //for (j=0; j 0) // we only use 1 namespace { #if 0 snprintf(cmd_buf, sizeof(cmd_buf), "cat /sys/block/%s/dev", nvme_slot_mapping[i].ns_dev[0]); //printf("cmd1: %s\n", cmd_buf); fp = popen(cmd_buf, "r"); fgets(cmd_result, sizeof(cmd_result)-1, fp); pclose(fp); p_str = strchr(cmd_result, ':'); *p_str = 0; major = atoi(cmd_result); minor = atoi(p_str+1); snprintf(cmd_buf, sizeof(cmd_buf), "/bin/mknod /dev/NVMeSlot%d b %d %d", i, major, minor); //printf("mknod cmd: %s\n", cmd_buf); system(cmd_buf); #else snprintf(cmd_buf, sizeof(cmd_buf), "ln -s /dev/%s /dev/NVMeSlot%d", nvme_slot_mapping[i].ns_dev[0], i); system(cmd_buf); #endif remove_node = 0; } } if (remove_node != 0) { printf("Remove node /dev/NVMeSlot%d\n", i); snprintf(cmd_buf, sizeof(cmd_buf), "if [ -L /dev/NVMeSlot%d ] ; then rm /dev/NVMeSlot%d ; fi", i, i); system(cmd_buf); } } return 0; } int isRunning(void) { int ret = 0; char sCurrPid[16] = {0}; sprintf(sCurrPid, "%d\n", getpid()); FILE *fstream=NULL; char buff[1024] = {0}; if(NULL==(fstream=popen("ps -aux | grep nvmeSsdMgr | grep -v grep | grep -v \"systemctl start\" | awk '{print $2}'", "r"))) { //fprintf(stderr,"execute command failed: %s", strerror(errno)); fprintf(stderr,"execute command failed.\n"); return -1; } while(NULL!=fgets(buff, sizeof(buff), fstream)) { if (strlen(buff) > 0) { if (strcmp(buff, sCurrPid) !=0) { //printf("%s, %s\n", buff, sCurrPid); ret = 1; break; } } } pclose(fstream); return ret; } int exit_flag = 0; // Do not exit void exit_handler(int sig) { //printf("I got a signal %d\nI'm quitting.\n", sig); syslog(LOG_INFO, "Exit by signal %d\n", sig); exit_flag = 4; // Exit - signaled } int main( int argc, char **argv ) { #if 0 if (scan_nvme_ssd() != 0) return -1; return createNvmeNodes(); #else struct sigaction act; if (isRunning() == 1) { printf("Already Running\n"); return 0; } if (argc == 2 && strcmp(argv[1], "--no-daemon") == 0) { exit_flag = 1; // exit - not daemon } else { if (-1 == daemon(0, 0)) { printf("create daemon failed\n"); exit_flag = 2; // exit - create daemon failed } else { openlog("nvmeSsdMgr", LOG_PID, LOG_USER); syslog(LOG_INFO, "daemon start.\n"); act.sa_handler = exit_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; if(sigaction(SIGQUIT, &act, NULL)) { syslog(LOG_INFO, "sigaction error.\n"); exit_flag = 3; // exit - create signal failed } } } // kill -3 $PID do { if (scan_nvme_ssd() == 0) createNvmeNodes(); if (exit_flag == 0) sleep(3); } while (exit_flag == 0); if (exit_flag == 3 || exit_flag == 4) { syslog(LOG_INFO, "exit_flag = %d\n", exit_flag); closelog(); } return 0; #endif }