diff options
Diffstat (limited to '')
-rw-r--r-- | plugins/huawei/huawei-nvme.c | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c new file mode 100644 index 0000000..0272dea --- /dev/null +++ b/plugins/huawei/huawei-nvme.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2017-2019 Huawei Corporation or its affiliates. + * + * 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 Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Author: Zou Ming<zouming.zouming@huawei.com>, + * Yang Feng <philip.yang@huawei.com> + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <limits.h> +#include <fcntl.h> +#include <unistd.h> +#include <dirent.h> + +#include <sys/stat.h> + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" + +#include "util/suffix.h" + +#define CREATE_CMD +#include "huawei-nvme.h" + +#define HW_SSD_PCI_VENDOR_ID 0x19E5 +#define ARRAY_NAME_LEN 80 +#define NS_NAME_LEN 40 + +#define MIN_ARRAY_NAME_LEN 16 +#define MIN_NS_NAME_LEN 16 + +struct huawei_list_item { + char node[1024]; + struct nvme_id_ctrl ctrl; + unsigned int nsid; + struct nvme_id_ns ns; + unsigned int block; + char ns_name[NS_NAME_LEN]; + char array_name[ARRAY_NAME_LEN]; + bool huawei_device; +}; + +struct huawei_list_element_len { + unsigned int node; + unsigned int ns_name; + unsigned int nguid; + unsigned int ns_id; + unsigned int usage; + unsigned int array_name; +}; + +static int huawei_get_nvme_info(int fd, struct huawei_list_item *item, const char *node) +{ + int err; + int len; + struct stat nvme_stat_info; + + memset(item, 0, sizeof(*item)); + + err = nvme_identify_ctrl(fd, &item->ctrl); + if (err) + return err; + + /*identify huawei device*/ + if (strstr(item->ctrl.mn, "Huawei") == NULL && + le16_to_cpu(item->ctrl.vid) != HW_SSD_PCI_VENDOR_ID) { + item->huawei_device = false; + return 0; + } + + item->huawei_device = true; + err = nvme_get_nsid(fd, &item->nsid); + err = nvme_identify_ns(fd, item->nsid, &item->ns); + if (err) + return err; + + err = fstat(fd, &nvme_stat_info); + if (err < 0) + return err; + + strncpy(item->node, node, sizeof(item->node)); + item->node[sizeof(item->node) - 1] = '\0'; + item->block = S_ISBLK(nvme_stat_info.st_mode); + + if (item->ns.vs[0] == 0) { + len = snprintf(item->ns_name, NS_NAME_LEN, "%s", "----"); + if (len < 0) + return -EINVAL; + } else { + memcpy(item->ns_name, item->ns.vs, NS_NAME_LEN); + item->ns_name[NS_NAME_LEN - 1] = '\0'; + } + + if (item->ctrl.vs[0] == 0) { + len = snprintf(item->array_name, ARRAY_NAME_LEN, "%s", "----"); + if (len < 0) + return -EINVAL; + } else { + memcpy(item->array_name, item->ctrl.vs, ARRAY_NAME_LEN); + item->array_name[ARRAY_NAME_LEN - 1] = '\0'; + } + return 0; +} + +static void format(char *formatter, size_t fmt_sz, char *tofmt, size_t tofmtsz) +{ + fmt_sz = snprintf(formatter, fmt_sz, "%-*.*s", (int)tofmtsz, (int)tofmtsz, tofmt); + + /* trim() the obnoxious trailing white lines */ + while (fmt_sz) { + if (formatter[fmt_sz - 1] != ' ' && formatter[fmt_sz - 1] != '\0') { + formatter[fmt_sz] = '\0'; + break; + } + fmt_sz--; + } +} + +static void huawei_json_print_list_items(struct huawei_list_item *list_items, + unsigned int len) +{ + struct json_object *root; + struct json_object *devices; + struct json_object *device_attrs; + char formatter[128] = { 0 }; + int index, i = 0; + + root = json_create_object(); + devices = json_create_array(); + for (i = 0; i < len; i++) { + device_attrs = json_create_object(); + + json_object_add_value_string(device_attrs, + "DevicePath", + list_items[i].node); + + if (sscanf(list_items[i].node, "/dev/nvme%d", &index) == 1) + json_object_add_value_int(device_attrs, + "Index", + index); + + format(formatter, sizeof(formatter), + list_items[i].ns_name, + sizeof(list_items[i].ns_name)); + + json_object_add_value_string(device_attrs, + "NS Name", + formatter); + + format(formatter, sizeof(formatter), + list_items[i].array_name, + sizeof(list_items[i].array_name)); + + json_object_add_value_string(device_attrs, + "Array Name", + formatter); + + json_array_add_value_object(devices, device_attrs); + } + json_object_add_value_array(root, "Devices", devices); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); +} + +static void huawei_print_list_head(struct huawei_list_element_len element_len) +{ + char dash[128]; + int i; + + for (i = 0; i < 128; i++) + dash[i] = '-'; + dash[127] = '\0'; + + printf("%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s %-*.*s\n", + element_len.node, element_len.node, "Node", + element_len.ns_name, element_len.ns_name, "NS Name", + element_len.nguid, element_len.nguid, "Nguid", + element_len.ns_id, element_len.ns_id, "NS ID", + element_len.usage, element_len.usage, "Usage", + element_len.array_name, element_len.array_name, "Array Name"); + + printf("%-.*s %-.*s %-.*s %-.*s %-.*s %-.*s\n", + element_len.node, dash, element_len.ns_name, dash, + element_len.nguid, dash, element_len.ns_id, dash, + element_len.usage, dash, element_len.array_name, dash); +} + +static void huawei_print_list_item(struct huawei_list_item *list_item, + struct huawei_list_element_len element_len) +{ + __u8 lba_index; + + nvme_id_ns_flbas_to_lbaf_inuse(list_item->ns.flbas, &lba_index); + unsigned long long lba = 1ULL << list_item->ns.lbaf[lba_index].ds; + double nsze = le64_to_cpu(list_item->ns.nsze) * lba; + double nuse = le64_to_cpu(list_item->ns.nuse) * lba; + + const char *s_suffix = suffix_si_get(&nsze); + const char *u_suffix = suffix_si_get(&nuse); + + char usage[128]; + char nguid_buf[2 * sizeof(list_item->ns.nguid) + 1]; + char *nguid = nguid_buf; + int i; + + sprintf(usage, "%6.2f %2sB / %6.2f %2sB", nuse, u_suffix, nsze, s_suffix); + + memset(nguid, 0, sizeof(nguid_buf)); + for (i = 0; i < sizeof(list_item->ns.nguid); i++) + nguid += sprintf(nguid, "%02x", list_item->ns.nguid[i]); + + printf("%-*.*s %-*.*s %-*.*s %-*d %-*.*s %-*.*s\n", + element_len.node, element_len.node, list_item->node, + element_len.ns_name, element_len.ns_name, list_item->ns_name, + element_len.nguid, element_len.nguid, nguid_buf, + element_len.ns_id, list_item->nsid, + element_len.usage, element_len.usage, usage, + element_len.array_name, element_len.array_name, + list_item->array_name); + +} + +static unsigned int choose_len(unsigned int old_len, unsigned int cur_len, unsigned int default_len) +{ + unsigned int temp_len; + + temp_len = (cur_len > default_len) ? cur_len : default_len; + if (temp_len > old_len) + return temp_len; + return old_len; +} + +static unsigned int huawei_get_ns_len(struct huawei_list_item *list_items, unsigned int len, + unsigned int default_len) +{ + int i; + unsigned int min_len = default_len; + + for (i = 0 ; i < len ; i++) + min_len = choose_len(min_len, strlen(list_items->ns_name), default_len); + + return min_len; +} + +static int huawei_get_array_len(struct huawei_list_item *list_items, unsigned int len, + unsigned int default_len) +{ + int i; + int min_len = default_len; + + for (i = 0 ; i < len ; i++) + min_len = choose_len(min_len, strlen(list_items->array_name), default_len); + + return min_len; +} + +static void huawei_print_list_items(struct huawei_list_item *list_items, unsigned int len) +{ + unsigned int i; + struct huawei_list_element_len element_len; + + element_len.node = 16; + element_len.nguid = 2 * sizeof(list_items->ns.nguid) + 1; + element_len.ns_id = 9; + element_len.usage = 26; + element_len.ns_name = huawei_get_ns_len(list_items, len, MIN_NS_NAME_LEN); + element_len.array_name = huawei_get_array_len(list_items, len, MIN_ARRAY_NAME_LEN); + + huawei_print_list_head(element_len); + + for (i = 0 ; i < len ; i++) + huawei_print_list_item(&list_items[i], element_len); +} + +static int huawei_list(int argc, char **argv, struct command *command, + struct plugin *plugin) +{ + char path[264]; + struct dirent **devices; + struct huawei_list_item *list_items; + unsigned int i, n, ret; + unsigned int huawei_num = 0; + enum nvme_print_flags fmt; + const char *desc = "Retrieve basic information for the given huawei device"; + struct config { + char *output_format; + }; + + struct config cfg = { + .output_format = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"), + OPT_END() + }; + + ret = argconfig_parse(argc, argv, desc, opts); + if (ret) + return ret; + + ret = validate_output_format(cfg.output_format, &fmt); + if (ret < 0 || (fmt != JSON && fmt != NORMAL)) + return ret; + + n = scandir("/dev", &devices, nvme_namespace_filter, alphasort); + if (n <= 0) + return n; + + list_items = calloc(n, sizeof(*list_items)); + if (!list_items) { + fprintf(stderr, "can not allocate controller list payload\n"); + ret = ENOMEM; + goto out_free_devices; + } + + for (i = 0; i < n; i++) { + int fd; + + snprintf(path, sizeof(path), "/dev/%s", devices[i]->d_name); + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Cannot open device %s: %s\n", + path, strerror(errno)); + continue; + } + ret = huawei_get_nvme_info(fd, &list_items[huawei_num], path); + if (ret) { + close(fd); + goto out_free_list_items; + } + if (list_items[huawei_num].huawei_device == true) + huawei_num++; + close(fd); + } + + if (huawei_num > 0) { + if (fmt == JSON) + huawei_json_print_list_items(list_items, huawei_num); + else + huawei_print_list_items(list_items, huawei_num); + } +out_free_list_items: + free(list_items); +out_free_devices: + for (i = 0; i < n; i++) + free(devices[i]); + free(devices); + + return ret; +} + +static void huawei_do_id_ctrl(__u8 *vs, struct json_object *root) +{ + char array_name[ARRAY_NAME_LEN + 1] = {0}; + + memcpy(array_name, vs, ARRAY_NAME_LEN); + if (root) + json_object_add_value_string(root, "array name", strlen(array_name) > 1 ? array_name : "NULL"); + else + printf("array name : %s\n", strlen(array_name) > 1 ? array_name : "NULL"); +} + +static int huawei_id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + return __id_ctrl(argc, argv, cmd, plugin, huawei_do_id_ctrl); +} |