From a56e8bf096c04ea0c76ca026e07402f05ce58ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kinga=20Ta=C5=84ska?= Date: Tue, 14 Jun 2022 12:11:45 +0200 Subject: [PATCH 1/2] Ledctl - slots management (#94) * ledctl: add commands to support empty slots blinking * ledctl: --get-slot implementation for vmd * ledctl: --list-slots implementation for vmd * ledctl: --set-slot implementation for vmd * ledctl: --get-slot implementation for npem * ledctl: --list-slots implementation for npem * ledctl: --set-slot implementation for npem Add new interface which allows to read or modify led state for devices by controller's slots. It allows to manage empty slots in cases where: - drive is not connected. - drive is connected, but non-standard driver is used (uio, vfio). Slot identifier will used to manage led state. Following commands are added: --get-slot --list-slots --set-slot Support for VMD and NPEM is added. Signed-off-by: Kinga Tanska --- doc/ledctl.pod | 36 ++++++ src/Makefile.am | 2 +- src/block.c | 12 ++ src/block.h | 15 ++- src/ledctl.c | 329 ++++++++++++++++++++++++++++++++++++++++++++++-- src/npem.c | 145 ++++++++++++++++++--- src/npem.h | 28 +++++ src/pci_slot.c | 102 ++++++++++++++- src/pci_slot.h | 35 +++++- src/slot.h | 63 ++++++++++ src/sysfs.c | 1 - src/sysfs.h | 4 +- src/utils.c | 49 +++++++- src/utils.h | 15 ++- src/vmdssd.c | 63 +++++----- src/vmdssd.h | 11 +- 16 files changed, 833 insertions(+), 77 deletions(-) create mode 100644 src/slot.h diff --git a/doc/ledctl.pod b/doc/ledctl.pod index 774d04b2b0e2..a0da95e4c734 100644 --- a/doc/ledctl.pod +++ b/doc/ledctl.pod @@ -324,6 +324,42 @@ Displays version of ledctl and information about the license and exits. Prints information (system path and type) of all controllers detected by ledmon and exits. +=item B<-P> or B<--list-slots> B<--controller>=I + +Prints all slots for the controller. Slot definition depends on the controller +and is unique across all controllers of the same type. + +Definitions for supported controllers are described below: + +=over + +=item + +VMD - PCI Express Hot Plug Controller Driver slot number + +=item + +NPEM - PCI Express Downstream Port address + +=back + +Command returns a list of all slots for the controller with current state and +attached device name (if any). I is type of controller +(vmd, NPEM) that should be scanned here. + +=item B<-G> or B<--get-slot> B<--controller>=I B<--device>=I + +Displays slot details of given device. I is devnode of selected drive. + +=item B<-G> or B<--get-slot> B<--controller>=I B<--slot>=I + +Displays details of given slot. I is unique slot identifier. + +=item B<-S> or B<--set-slot> B<--controller>=I B<--slot>=I B<--state>=I + +Changes led state for given slot. I is type of the controller. +I is unique slot identifier. I is led pattern. + =item B<-x> or B<--listed-only> With this option ledctl will change state only on devices listed in CLI. The diff --git a/src/Makefile.am b/src/Makefile.am index f7786206a510..34311a2699a7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,7 @@ COMMON_SRCS = ahci.c block.c cntrl.c config_file.c enclosure.c list.c \ vmdssd.h ipmi.h amd.h amd_ipmi.h npem.h LEDMON_SRCS = ledmon.c pidfile.c $(COMMON_SRCS) -LEDCTL_SRCS = ledctl.c $(COMMON_SRCS) +LEDCTL_SRCS = ledctl.c slot.h $(COMMON_SRCS) sbin_PROGRAMS = ledmon ledctl diff --git a/src/block.c b/src/block.c index c61c577cb2c8..3d1815cb11a3 100644 --- a/src/block.c +++ b/src/block.c @@ -215,6 +215,18 @@ struct _host_type *block_get_host(struct cntrl_device *cntrl, int host_id) return hosts; } +struct block_device *get_block_device_from_sysfs_path(char *sub_path) +{ + struct block_device *device; + + list_for_each(sysfs_get_block_devices(), device) { + if (strstr(device->sysfs_path, sub_path)) + return device; + } + + return NULL; +} + /* * Allocates a new block device structure. See block.h for details. */ diff --git a/src/block.h b/src/block.h index 820f2d72325e..00b9fd73a19d 100644 --- a/src/block.h +++ b/src/block.h @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2018 Intel Corporation. + * Copyright (C) 2009-2022 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -25,6 +25,7 @@ #include "time.h" #include "list.h" #include "raid.h" +#include "status.h" struct block_device; @@ -227,4 +228,16 @@ struct _host_type *block_get_host(struct cntrl_device *cntrl, int host_id); int block_compare(const struct block_device *bd_old, const struct block_device *bd_new); +/** + * @brief Finds block device which name contains sub-path. + * + * This function scans block devices and checks their sysfs path + * to find any which contains PCI address specified for device in path. + * + * @param[in] sub_path Sub path. + * + * @return first block device containing sub-path if any, otherwise NULL. + */ +struct block_device *get_block_device_from_sysfs_path(char *sub_path); + #endif /* _BLOCK_H_INCLUDED_ */ diff --git a/src/ledctl.c b/src/ledctl.c index caedc1a4834d..c20986230739 100644 --- a/src/ledctl.c +++ b/src/ledctl.c @@ -42,12 +42,13 @@ #include "cntrl.h" #include "config.h" #include "config_file.h" -#include "ibpi.h" +#include "slot.h" #include "list.h" +#include "npem.h" +#include "pci_slot.h" #include "scsi.h" #include "status.h" #include "sysfs.h" -#include "utils.h" /** * @brief An IBPI state structure. @@ -69,6 +70,73 @@ struct ibpi_state { */ static struct list ibpi_list; +/** + * @brief Pointer to a get slot function. + * + * The pointer to a function which will print slot details. + * + * @param[in] device Name of the device. + * @param[in] slot Unique identifier of the slot. + * @param[in] res Pointer to slot response. + * + * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. + */ +typedef status_t (*get_slot_t) (char *device, char *slot, struct slot_response *res); + +/** + * @brief Pointer to a set slot function. + * + * The pointer to a function which will set slot details. + * + * @param[in] slot Unique identifier of the slot. + * @param[in] state IBPI state based on slot request. + * + * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. + */ +typedef status_t (*set_slot_t) (char *slot, enum ibpi_pattern state); + +/** + * @brief slot request parametres + * + * This structure contains all possible parameters for slot related commands. + */ +struct slot_request { + /** + * Option given in the request. + */ + int chosen_opt; + + /** + * Name of the device. + */ + char device[PATH_MAX]; + + /** + * Unique slot identifier. + */ + char slot[PATH_MAX]; + + /** + * Type of the controller. + */ + enum cntrl_type cntrl; + + /** + * IBPI state. + */ + enum ibpi_pattern state; + + /** + * Pointer to the get slot function. + */ + get_slot_t get_slot_fn; + + /** + * Pointer to the set slot function. + */ + set_slot_t set_slot_fn; +}; + /** * @brief IBPI pattern names. * @@ -76,7 +144,7 @@ static struct list ibpi_list; * this entries to translate enumeration type values into the string. */ const char *ibpi_str[] = { - [IBPI_PATTERN_UNKNOWN] = "", + [IBPI_PATTERN_UNKNOWN] = "UNKNOWN", [IBPI_PATTERN_NORMAL] = "NORMAL", [IBPI_PATTERN_ONESHOT_NORMAL] = "", [IBPI_PATTERN_DEGRADED] = "ICA", @@ -112,6 +180,13 @@ static int possible_params[] = { OPT_VERSION, OPT_LIST_CTRL, OPT_LISTED_ONLY, + OPT_LIST_SLOTS, + OPT_GET_SLOT, + OPT_SET_SLOT, + OPT_CONTROLLER, + OPT_DEVICE, + OPT_SLOT, + OPT_STATE, OPT_ALL, OPT_DEBUG, OPT_ERROR, @@ -126,6 +201,43 @@ static const int possible_params_size = sizeof(possible_params) static int listed_only; +enum cntrl_type get_cntrl_type(const char *cntrl) +{ + if (strcasecmp(cntrl, "vmd") == 0) + return CNTRL_TYPE_VMD; + else if (strcasecmp(cntrl, "npem") == 0) + return CNTRL_TYPE_NPEM; + return CNTRL_TYPE_UNKNOWN; +} + +/** + * @brief Determines a slot functions based on controller. + * + * This function determines slot functions based on + * controller type. + * + * @param[in] ctrl_type Controller type. + * @param[in] slot_req Pointer to the slot request. + * + * @return This function does not return a value. + */ +static void _get_slot_ctrl_fn(enum cntrl_type ctrl_type, struct slot_request *slot_req) +{ + switch (ctrl_type) { + case CNTRL_TYPE_VMD: + slot_req->get_slot_fn = pci_get_slot; + slot_req->set_slot_fn = pci_set_slot; + break; + case CNTRL_TYPE_NPEM: + slot_req->get_slot_fn = npem_get_slot; + slot_req->set_slot_fn = npem_set_slot; + break; + default: + log_debug("Slot functions could not be set because the controller type %s does not " + "support slots managing.", ctrl_type); + } +} + static void ibpi_state_fini(struct ibpi_state *p) { list_clear(&p->block_list); @@ -186,16 +298,25 @@ static void _ledctl_help(void) progname); printf("Mandatory arguments for long options are mandatory for short options, too.\n\n"); print_opt("--listed-only", "-x", - "Ledctl will change state only for given devices."); + "Ledctl will change state only for given devices."); print_opt("--list-controllers", "-L", - "Displays list of controllers detected by ledmon."); + "Displays list of controllers detected by ledmon."); + print_opt("--list-slots --controller CONTROLLER", "-P -c CONTROLLER", + "List slots under the controller, their led states, slot numbers and " + "devnodes connected."); + print_opt("--get-slot --controller CONTROLLER --device DEVNODE / --slot SLOT", + "-G -c CONTROLLER -d DEVNODE / -p SLOT", + "Prints slot information, its led state, slot number and devnode."); + print_opt("--set-slot --controller CONTROLLER --slot SLOT --state STATE", + "-S -c CONTROLLER -p SLOT -s STATE", "Sets given state for chosen slot " + "under the controller."); print_opt("--log=PATH", "-l PATH", - "Use local log file instead /var/log/ledctl.log."); + "Use local log file instead /var/log/ledctl.log."); print_opt("--help", "-h", "Displays this help text."); print_opt("--version", "-v", - "Displays version and license information."); + "Displays version and license information."); print_opt("--log-level=VALUE", "-l VALUE", - "Allows user to set ledctl verbose level in logs."); + "Allows user to set ledctl verbose level in logs."); printf("\nPatterns:\n" "\tCommon patterns are:\n" "\t\tlocate, locate_off, normal, off, degraded, rebuild,\n" "" @@ -555,6 +676,156 @@ static status_t _cmdline_parse_non_root(int argc, char *argv[]) return status; } +/** + * @brief Inits slot request structure with initial values. + * + * @param[in] slot_req structure with slot request + * + * @return This function does not return a value. + */ +static void slot_request_init(struct slot_request *slot_req) +{ + memset(slot_req, 0, sizeof(struct slot_request)); + + slot_req->chosen_opt = OPT_NULL_ELEMENT; + slot_req->state = IBPI_PATTERN_UNKNOWN; +} + +/** + * @brief Inits slot response structure with initial values. + * + * @param[in] slot_res structure with slot response + * + * @return This function does not return a value. + */ +static void slot_response_init(struct slot_response *slot_res) +{ + memset(slot_res, 0, sizeof(struct slot_response)); + + slot_res->state = IBPI_PATTERN_UNKNOWN; +} + +/** + * @brief Verifies slot request parameters. + * + * @param[in] slot_req structure with slot request + * + * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. + */ +static status_t slot_verify_request(struct slot_request *slot_req) +{ + if (slot_req->cntrl == CNTRL_TYPE_UNKNOWN) { + log_error("Invalid controller in the request."); + return STATUS_INVALID_CONTROLLER; + } + if (slot_req->chosen_opt == OPT_SET_SLOT && slot_req->state == IBPI_PATTERN_UNKNOWN) { + log_error("Invalid IBPI state in the request."); + return STATUS_INVALID_STATE; + } + if (!slot_req->get_slot_fn && !slot_req->set_slot_fn) { + log_error("The controller type %s doesn't support slot functionality.", + slot_req->cntrl); + return STATUS_INVALID_CONTROLLER; + } + if (slot_req->device[0] && slot_req->slot[0]) { + log_error("Device and slot parameters are exclusive."); + return STATUS_DATA_ERROR; + } + + return STATUS_SUCCESS; +} + +static status_t get_state_for_slot(char *slot, struct slot_request *slot_req) +{ + struct slot_response slot_res; + status_t status = STATUS_SUCCESS; + + slot_response_init(&slot_res); + status = slot_req->get_slot_fn(NULL, slot, &slot_res); + if (status == STATUS_SUCCESS) + print_slot_state(&slot_res); + + return status; +} + +/** + * @brief List slots connected to given controller + * + * This function scans all available slots connected to given controller + * and prints their led states and names of the connected devices (if exist). + * + * @param[in] slot_req Structure with slot request. + * + * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. + */ +static status_t list_slots(struct slot_request *slot_req) +{ + status_t status = STATUS_SUCCESS; + + switch (slot_req->cntrl) { + case CNTRL_TYPE_VMD: + { + struct pci_slot *slot; + + list_for_each(sysfs_get_pci_slots(), slot) + status = get_state_for_slot(slot->sysfs_path, slot_req); + return status; + } + case CNTRL_TYPE_NPEM: + { + struct cntrl_device *ctrl_dev; + + list_for_each(sysfs_get_cntrl_devices(), ctrl_dev) { + if (ctrl_dev->cntrl_type != CNTRL_TYPE_NPEM) + continue; + status = get_state_for_slot(ctrl_dev->sysfs_path, slot_req); + } + return status; + } + default: + return STATUS_NOT_SUPPORTED; + } +} + +/** + * @brief Executes proper slot mode function. + * + * @param[in] slot_req Structure with slot request. + * + * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. + */ +status_t slot_execute(struct slot_request *slot_req) +{ + struct slot_response slot_res; + status_t status = STATUS_SUCCESS; + + slot_response_init(&slot_res); + + switch (slot_req->chosen_opt) { + case OPT_LIST_SLOTS: + return list_slots(slot_req); + case OPT_SET_SLOT: + status = slot_req->get_slot_fn(slot_req->device, slot_req->slot, &slot_res); + if (slot_res.state == slot_req->state) { + log_warning("Led state: %s is already set for the slot.", + ibpi2str(slot_req->state)); + return STATUS_SUCCESS; + } + if (status != STATUS_SUCCESS) + return status; + status = slot_req->set_slot_fn(slot_res.slot, slot_req->state); + if (status != STATUS_SUCCESS) + return status; + case OPT_GET_SLOT: + status = slot_req->get_slot_fn(slot_req->device, slot_req->slot, &slot_res); + if (status == STATUS_SUCCESS) + print_slot_state(&slot_res); + return status; + default: + return STATUS_NOT_SUPPORTED; + } +} + /** * @brief Command line parser - options. * @@ -567,7 +838,7 @@ static status_t _cmdline_parse_non_root(int argc, char *argv[]) * * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. */ -static status_t _cmdline_parse(int argc, char *argv[]) +static status_t _cmdline_parse(int argc, char *argv[], struct slot_request *req) { int opt, opt_index = -1; status_t status = STATUS_SUCCESS; @@ -614,6 +885,34 @@ static status_t _cmdline_parse(int argc, char *argv[]) sysfs_reset(); exit(EXIT_SUCCESS); } + case 'G': + req->chosen_opt = OPT_GET_SLOT; + break; + case 'P': + req->chosen_opt = OPT_LIST_SLOTS; + break; + case 'S': + req->chosen_opt = OPT_SET_SLOT; + break; + case 'c': + req->cntrl = get_cntrl_type(optarg); + _get_slot_ctrl_fn(req->cntrl, req); + break; + case 's': + { + struct ibpi_state *state = _ibpi_state_get(optarg); + + if (state) + req->state = state->ibpi; + free(state); + break; + } + case 'd': + strncpy(req->device, optarg, PATH_MAX - 1); + break; + case 'p': + strncpy(req->slot, optarg, PATH_MAX - 1); + break; case ':': case '?': default: @@ -712,6 +1011,7 @@ static status_t _init_ledctl_conf(void) int main(int argc, char *argv[]) { status_t status; + struct slot_request slot_req; setup_options(&longopt, &shortopt, possible_params, possible_params_size); @@ -732,7 +1032,9 @@ int main(int argc, char *argv[]) return status; if (on_exit(_ledctl_fini, progname)) exit(STATUS_ONEXIT_ERROR); - if (_cmdline_parse(argc, argv)) + slot_request_init(&slot_req); + status = _cmdline_parse(argc, argv, &slot_req); + if (status != STATUS_SUCCESS) exit(STATUS_CMDLINE_ERROR); free(shortopt); free(longopt); @@ -746,6 +1048,13 @@ int main(int argc, char *argv[]) list_init(&ibpi_list, (item_free_t)ibpi_state_fini); sysfs_init(); sysfs_scan(); + if (slot_req.chosen_opt != OPT_NULL_ELEMENT) { + status = slot_verify_request(&slot_req); + if (status == STATUS_SUCCESS) + return slot_execute(&slot_req); + else + exit(status); + } status = _cmdline_ibpi_parse(argc, argv); if (status != STATUS_SUCCESS) { log_debug("main(): _ibpi_parse() failed (status=%s).", diff --git a/src/npem.c b/src/npem.c index 4f836ee8571d..4b08966bd13a 100644 --- a/src/npem.c +++ b/src/npem.c @@ -25,7 +25,9 @@ #include "config.h" #include "cntrl.h" +#include "list.h" #include "npem.h" +#include "sysfs.h" #include "utils.h" #define PCI_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Management */ @@ -57,19 +59,32 @@ #define PCI_NPEM_STATUS_CC 0x01 /* NPEM Command Completed */ -const int ibpi_to_npem_capability[] = { - [IBPI_PATTERN_NORMAL] = PCI_NPEM_OK_CAP, - [IBPI_PATTERN_ONESHOT_NORMAL] = PCI_NPEM_OK_CAP, - [IBPI_PATTERN_DEGRADED] = PCI_NPEM_CRA_CAP, - [IBPI_PATTERN_HOTSPARE] = PCI_NPEM_HOT_SPARE_CAP, - [IBPI_PATTERN_REBUILD] = PCI_NPEM_REBUILD_CAP, - [IBPI_PATTERN_FAILED_ARRAY] = PCI_NPEM_FA_CAP, - [IBPI_PATTERN_PFA] = PCI_NPEM_PFA_CAP, - [IBPI_PATTERN_FAILED_DRIVE] = PCI_NPEM_FAIL_CAP, - [IBPI_PATTERN_LOCATE] = PCI_NPEM_LOCATE_CAP, - [IBPI_PATTERN_LOCATE_OFF] = PCI_NPEM_OK_CAP, +const struct ibpi_value ibpi_to_npem_capability[] = { + {IBPI_PATTERN_NORMAL, PCI_NPEM_OK_CAP}, + {IBPI_PATTERN_ONESHOT_NORMAL, PCI_NPEM_OK_CAP}, + {IBPI_PATTERN_DEGRADED, PCI_NPEM_CRA_CAP}, + {IBPI_PATTERN_HOTSPARE, PCI_NPEM_HOT_SPARE_CAP}, + {IBPI_PATTERN_REBUILD, PCI_NPEM_REBUILD_CAP}, + {IBPI_PATTERN_FAILED_ARRAY, PCI_NPEM_FA_CAP}, + {IBPI_PATTERN_PFA, PCI_NPEM_PFA_CAP}, + {IBPI_PATTERN_FAILED_DRIVE, PCI_NPEM_FAIL_CAP}, + {IBPI_PATTERN_LOCATE, PCI_NPEM_LOCATE_CAP}, + {IBPI_PATTERN_LOCATE_OFF, PCI_NPEM_OK_CAP}, + {IBPI_PATTERN_UNKNOWN} }; +static enum ibpi_pattern npem_capability_to_ibpi(const u32 reg) +{ + const struct ibpi_value *tmp = ibpi_to_npem_capability; + + while (tmp->ibpi != IBPI_PATTERN_UNKNOWN) { + if (reg & tmp->value) + break; + tmp++; + } + return tmp->ibpi; +} + static struct pci_access *get_pci_access() { struct pci_access *pacc; @@ -131,8 +146,10 @@ int is_npem_capable(const char *path) struct pci_access *pacc = get_pci_access(); struct pci_dev *pdev; - if (!pacc) + if (!pacc) { + log_error("NPEM: Unable to initialize pci access for %s\n", path); return 0; + } pdev = get_pci_dev(pacc, path); @@ -215,7 +232,9 @@ int npem_write(struct block_device *device, enum ibpi_pattern ibpi) } reg = read_npem_register(pdev, PCI_NPEM_CAP_REG); - if ((reg & ibpi_to_npem_capability[ibpi]) == 0) { + u32 cap = (u32)get_value_for_ibpi(ibpi, ibpi_to_npem_capability); + + if ((reg & cap) == 0) { log_debug("NPEM: Controller %s doesn't support %s pattern\n", npem_cntrl->sysfs_path, ibpi_str[ibpi]); ibpi = IBPI_PATTERN_NORMAL; @@ -223,7 +242,8 @@ int npem_write(struct block_device *device, enum ibpi_pattern ibpi) reg = read_npem_register(pdev, PCI_NPEM_CTRL_REG); val = (reg & PCI_NPEM_RESERVED); - val = (val | PCI_NPEM_CAP | ibpi_to_npem_capability[ibpi]); + cap = (u32)get_value_for_ibpi(ibpi, ibpi_to_npem_capability); + val = (val | PCI_NPEM_CAP | cap); write_npem_register(pdev, PCI_NPEM_CTRL_REG, val); if (npem_wait_command(pdev)) { @@ -244,3 +264,100 @@ char *npem_get_path(const char *cntrl_path) { return str_dup(cntrl_path); } + +status_t npem_get_slot(char *device, char *slot_path, struct slot_response *slot_res) +{ + struct pci_dev *pdev = NULL; + struct block_device *block_device = NULL; + struct pci_access *pacc = get_pci_access(); + status_t status = STATUS_SUCCESS; + char *path = NULL; + u32 reg; + + if (!pacc) { + log_error("NPEM: Unable to initialize pci access for %s\n", path); + return STATUS_NULL_POINTER; + } + + if (device && device[0] != '\0') { + block_device = get_block_device_from_sysfs_path(basename(device)); + if (block_device) + path = block_device->cntrl->sysfs_path; + } else if (slot_path && slot_path[0] != '\0') { + struct cntrl_device *ctrl_dev; + + list_for_each(sysfs_get_cntrl_devices(), ctrl_dev) { + if (ctrl_dev->cntrl_type != CNTRL_TYPE_NPEM) + continue; + if (strcmp(basename(ctrl_dev->sysfs_path), basename(slot_path)) != 0) + continue; + path = ctrl_dev->sysfs_path; + block_device = get_block_device_from_sysfs_path(path); + break; + } + } + + if (path) { + pdev = get_pci_dev(pacc, path); + } else { + log_debug("NPEM: unable to get sysfs path for the controller."); + pci_cleanup(pacc); + return STATUS_INVALID_PATH; + } + + if (!pdev) { + log_error("NPEM: Unable to get pci device for %s\n", path); + pci_cleanup(pacc); + return STATUS_NULL_POINTER; + } + + reg = read_npem_register(pdev, PCI_NPEM_CTRL_REG); + slot_res->state = npem_capability_to_ibpi(reg); + snprintf(slot_res->slot, PATH_MAX, "%s", path); + + if (block_device) + snprintf(slot_res->device, PATH_MAX, "/dev/%s", basename(block_device->sysfs_path)); + else + snprintf(slot_res->device, PATH_MAX, "(empty)"); + + pci_free_dev(pdev); + pci_cleanup(pacc); + return status; +} + +status_t npem_set_slot(char *slot_path, enum ibpi_pattern state) +{ + struct pci_dev *pdev = NULL; + struct pci_access *pacc = get_pci_access(); + status_t status = STATUS_SUCCESS; + u32 val; + u32 reg; + u32 cap; + + if (!pacc) { + log_error("NPEM: Unable to initialize pci access for %s\n", slot_path); + return STATUS_NULL_POINTER; + } + + pdev = get_pci_dev(pacc, slot_path); + if (!pdev) { + log_error("NPEM: Unable to get pci device for %s\n", slot_path); + pci_cleanup(pacc); + return STATUS_NULL_POINTER; + } + + reg = read_npem_register(pdev, PCI_NPEM_CTRL_REG); + val = (reg & PCI_NPEM_RESERVED); + cap = (u32)get_value_for_ibpi(state, ibpi_to_npem_capability); + val = (val | PCI_NPEM_CAP | cap); + + write_npem_register(pdev, PCI_NPEM_CTRL_REG, val); + if (npem_wait_command(pdev)) { + log_error("NPEM: Write timeout for %s\n", slot_path); + status = STATUS_FILE_WRITE_ERROR; + } + + pci_free_dev(pdev); + pci_cleanup(pacc); + return status; +} diff --git a/src/npem.h b/src/npem.h index 5fa3f11d0ba3..8f4dd7b96151 100644 --- a/src/npem.h +++ b/src/npem.h @@ -21,9 +21,37 @@ #define NPEM_H_INCLUDED_ #include "block.h" #include "ibpi.h" +#include "slot.h" +#include "status.h" int is_npem_capable(const char *path); int npem_write(struct block_device *device, enum ibpi_pattern ibpi); char *npem_get_path(const char *cntrl_path); +/** + * @brief Gets led state for slot. + * + * This function finds slot connected to given identifier + * and fills slot response related to the slot. + * + * @param[in] device Requested device name. + * @param[in] slot_num Requested identifier of the slot. + * @param[in] slot_res Pointer to the slot response. + * + * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. + */ +status_t npem_get_slot(char *device, char *slot_num, struct slot_response *slot_res); + +/** + * @brief Sets led state for slot. + * + * This function finds slot connected to given number or device name + * and set given led state. + * + * @param[in] slot_num Requested number of the slot. + * @param[in] state IBPI state based on slot request. + * + * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. + */ +status_t npem_set_slot(char *slot_num, enum ibpi_pattern state); #endif // NPEM_H_INCLUDED_ diff --git a/src/pci_slot.c b/src/pci_slot.c index b15e1947c043..1e03dbabe91d 100644 --- a/src/pci_slot.c +++ b/src/pci_slot.c @@ -29,7 +29,9 @@ #include "config.h" #include "pci_slot.h" +#include "sysfs.h" #include "utils.h" +#include "vmdssd.h" /* * Allocates memory for PCI hotplug slot structure and initializes fields of @@ -44,12 +46,6 @@ struct pci_slot *pci_slot_init(const char *path) return NULL; result->sysfs_path = str_dup(path); result->address = get_text(path, "address"); - result->attention = get_int(path, -1, "attention"); - - if (result->attention == -1) { - pci_slot_fini(result); - return NULL; - } return result; } @@ -66,3 +62,97 @@ void pci_slot_fini(struct pci_slot *slot) free(slot); } } + +/** + * @brief Finds PCI slot by number of the slot. + * + * @param[in] slot_number Number of the slot + * + * @return Struct with pci slot if successful, otherwise the function returns NULL pointer. + */ +static struct pci_slot *find_pci_slot_by_number(char *slot_number) +{ + struct pci_slot *slot = NULL; + char *temp; + + if (slot_number == NULL) + return NULL; + + list_for_each(sysfs_get_pci_slots(), slot) { + temp = basename(slot->sysfs_path); + if (temp && strncmp(temp, slot_number, PATH_MAX) == 0) + return slot; + } + return NULL; +} + +/** + * @brief Sets the slot response. + * + * @param[in] slot Struct with PCI slot parameters. + * + * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. + */ +static status_t set_slot_response(struct pci_slot *slot, struct slot_response *slot_res) +{ + struct block_device *bl_device; + status_t status = STATUS_SUCCESS; + int attention = get_int(slot->sysfs_path, -1, "attention"); + + if (attention == -1) + return STATUS_INVALID_STATE; + + slot_res->state = get_ibpi_for_value(attention, ibpi_to_attention); + snprintf(slot_res->slot, PATH_MAX, "%s", basename(slot->sysfs_path)); + + bl_device = get_block_device_from_sysfs_path(slot->address); + if (bl_device) + snprintf(slot_res->device, PATH_MAX, "/dev/%s", basename(bl_device->sysfs_path)); + else + snprintf(slot_res->device, PATH_MAX, "(empty)"); + + return status; +} + +status_t pci_get_slot(char *device, char *slot_path, struct slot_response *slot_res) +{ + struct pci_slot *slot = NULL; + struct block_device *block_device = NULL; + + if (device && device[0] != '\0') { + char *sub_path = basename(device); + if (sub_path == NULL) { + log_error("Device name %s is invalid.", device); + return STATUS_DATA_ERROR; + } + + block_device = get_block_device_from_sysfs_path(sub_path + 1); + if (block_device == NULL) { + log_error("Device %s not found.", device); + return STATUS_DATA_ERROR; + } + slot = vmdssd_find_pci_slot(block_device->sysfs_path); + } else if (slot_path && slot_path[0] != '\0') { + slot = find_pci_slot_by_number(basename(slot_path)); + } + + if (slot == NULL) { + log_error("Specified slot was not found."); + return STATUS_DATA_ERROR; + } + + return set_slot_response(slot, slot_res); +} + +status_t pci_set_slot(char *slot_path, enum ibpi_pattern state) +{ + struct pci_slot *slot = NULL; + + slot = find_pci_slot_by_number(basename(slot_path)); + if (slot == NULL) { + log_error("Slot %s not found.", slot_path); + return STATUS_NULL_POINTER; + } + + return vmdssd_write_attention_buf(slot, state); +} diff --git a/src/pci_slot.h b/src/pci_slot.h index b971de3d89b7..9a181ce6057e 100644 --- a/src/pci_slot.h +++ b/src/pci_slot.h @@ -20,6 +20,10 @@ #ifndef PCI_SLOT_H_INCLUDED_ #define PCI_SLOT_H_INCLUDED_ +#include "ibpi.h" +#include "slot.h" +#include "status.h" + /** * @brief PCI hotplug slot structure. * @@ -35,11 +39,6 @@ struct pci_slot { * PCI hotplug slot address. */ char *address; - - /** - * State of the Amber LED of the PCI slot. - */ - int attention; }; /** @@ -69,4 +68,30 @@ struct pci_slot *pci_slot_init(const char *path); */ void pci_slot_fini(struct pci_slot *slot); +/** + * @brief Gets led state for slot. + * + * This function finds slot connected to given identifier + * and fills slot response related to the slot. + * + * @param[in] device Requested device name. + * @param[in] slot_num Requested identifier of the slot. + * @param[in] slot_res Pointer to the slot response. + * + * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. + */ +status_t pci_get_slot(char *device, char *slot_num, struct slot_response *slot_res); + +/** + * @brief Sets led state for slot. + * + * This function finds slot connected to given number or device name + * and set given led state. + * + * @param[in] slot_num Requested number of the slot. + * @param[in] state IBPI state based on slot request. + * + * @return STATUS_SUCCESS if successful, otherwise a valid status_t status code. + */ +status_t pci_set_slot(char *slot_num, enum ibpi_pattern state); #endif // PCI_SLOT_H_INCLUDED_ diff --git a/src/slot.h b/src/slot.h new file mode 100644 index 000000000000..82b2e9405b89 --- /dev/null +++ b/src/slot.h @@ -0,0 +1,63 @@ +/* + * Intel(R) Enclosure LED Utilities + * Copyright (C) 2022-2022 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef SLOT_H_ +#define SLOT_H_ + +#include + +#include "ibpi.h" +#include "utils.h" + +/** + * @brief slot response parameters + * + * This structure contains slot properties. + */ +struct slot_response { + /** + * Name of the device. + */ + char device[PATH_MAX]; + + /** + * Unique slot identifier. + */ + char slot[PATH_MAX]; + + /** + * IBPI state. + */ + enum ibpi_pattern state; +}; + +/** + * @brief Print address, slot identifier and led state. + * + * @param[in] res Structure with slot response. + * + * @return This function does not return a value. + */ +static inline void print_slot_state(struct slot_response *res) +{ + printf("slot: %-15s led state: %-15s device: %-15s\n", + basename(res->slot), ibpi2str(res->state), res->device); +} + +#endif // SLOT_H_INCLUDED_ diff --git a/src/sysfs.c b/src/sysfs.c index 87e83391f328..f1f9caad05a3 100644 --- a/src/sysfs.c +++ b/src/sysfs.c @@ -48,7 +48,6 @@ */ #define SYSFS_CLASS_BLOCK "/sys/block" #define SYSFS_CLASS_ENCLOSURE "/sys/class/enclosure" -#define SYSFS_PCI_DEVICES "/sys/bus/pci/devices" #define SYSFS_PCI_SLOTS "/sys/bus/pci/slots" /** diff --git a/src/sysfs.h b/src/sysfs.h index efebea840cee..ea0109f6aa33 100644 --- a/src/sysfs.h +++ b/src/sysfs.h @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2018 Intel Corporation. + * Copyright (C) 2009-2022 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -22,6 +22,8 @@ #include "status.h" +#define SYSFS_PCI_DEVICES "/sys/bus/pci/devices" + /** * @brief Initializes sysfs module. * diff --git a/src/utils.c b/src/utils.c index 7f52194f5d1c..085cdb1d5c2f 100644 --- a/src/utils.c +++ b/src/utils.c @@ -505,7 +505,7 @@ int get_log_fd(void) void print_opt(const char *long_opt, const char *short_opt, const char *desc) { - printf("%-20s%-10s%s\n", long_opt, short_opt, desc); + printf("%-70s%-40s%s\n", long_opt, short_opt, desc); } /** @@ -581,6 +581,13 @@ struct option longopt_all[] = { [OPT_LIST_CTRL] = {"list-controllers", no_argument, NULL, 'L'}, [OPT_LISTED_ONLY] = {"listed-only", no_argument, NULL, 'x'}, [OPT_FOREGROUND] = {"foreground", no_argument, NULL, '\0'}, + [OPT_LIST_SLOTS] = {"list-slots", no_argument, NULL, 'P'}, + [OPT_GET_SLOT] = {"get-slot", no_argument, NULL, 'G'}, + [OPT_SET_SLOT] = {"set-slot", no_argument, NULL, 'S'}, + [OPT_CONTROLLER] = {"controller", required_argument, NULL, 'c'}, + [OPT_DEVICE] = {"device", required_argument, NULL, 'd'}, + [OPT_SLOT] = {"slot", required_argument, NULL, 'p'}, + [OPT_STATE] = {"state", required_argument, NULL, 's'}, [OPT_NULL_ELEMENT] = {NULL, no_argument, NULL, '\0'} }; @@ -699,3 +706,43 @@ const char *ibpi2str(enum ibpi_pattern ibpi) return ret; } + +/** + * @brief Returns value based on IBPI state + * + * @param[in] value Value for led state. + * @param[in] ibpi_values Array with defined IBPI states and values. + * + * @return Integer value which represents given IBPI state. + */ +int get_value_for_ibpi(enum ibpi_pattern ibpi, const struct ibpi_value ibpi_values[]) +{ + const struct ibpi_value *tmp = ibpi_values; + + while (tmp->ibpi != IBPI_PATTERN_UNKNOWN) { + if (tmp->ibpi == ibpi) + break; + tmp++; + } + return tmp->value; +} + +/** + * @brief Returns IBPI pattern based on value + * + * @param[in] value Value for led state. + * @param[in] ibpi_values Array with defined IBPI states and values. + * + * @return Enum with IBPI value, which represents given value. + */ +enum ibpi_pattern get_ibpi_for_value(const int value, const struct ibpi_value ibpi_values[]) +{ + const struct ibpi_value *tmp = ibpi_values; + + while (tmp->ibpi != IBPI_PATTERN_UNKNOWN) { + if (tmp->value == value) + break; + tmp++; + } + return tmp->ibpi; +} diff --git a/src/utils.h b/src/utils.h index 3f5a77fc6f74..5ef3020e6dce 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (C) 2009-2019 Intel Corporation. + * Copyright (C) 2009-2022 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -59,6 +59,10 @@ struct log_level_info { int priority; }; +struct ibpi_value { + int ibpi, value; +}; + /** */ #define PREFIX_DEBUG " DEBUG: " @@ -418,6 +422,13 @@ enum opt { OPT_LIST_CTRL, OPT_LISTED_ONLY, OPT_FOREGROUND, + OPT_LIST_SLOTS, + OPT_GET_SLOT, + OPT_SET_SLOT, + OPT_CONTROLLER, + OPT_DEVICE, + OPT_SLOT, + OPT_STATE, OPT_NULL_ELEMENT }; @@ -428,5 +439,7 @@ int get_option_id(const char *optarg); status_t set_verbose_level(int log_level); const char *ibpi2str(enum ibpi_pattern ibpi); +int get_value_for_ibpi(enum ibpi_pattern ibpi, const struct ibpi_value ibpi_values[]); +enum ibpi_pattern get_ibpi_for_value(const int value, const struct ibpi_value ibpi_values[]); #endif /* _UTILS_H_INCLUDED_ */ diff --git a/src/vmdssd.c b/src/vmdssd.c index 3c6d24bdc604..015d3489c276 100644 --- a/src/vmdssd.c +++ b/src/vmdssd.c @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (c) 2016-2019, Intel Corporation + * Copyright (c) 2016-2022, Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -37,6 +37,13 @@ #define ATTENTION_REBUILD 0x5 /* (0101) Attention On, Power On */ #define ATTENTION_FAILURE 0xD /* (1101) Attention On, Power Off */ +struct ibpi_value ibpi_to_attention[] = { + {IBPI_PATTERN_LOCATE, ATTENTION_LOCATE}, + {IBPI_PATTERN_FAILED_DRIVE, ATTENTION_FAILURE}, + {IBPI_PATTERN_REBUILD, ATTENTION_REBUILD}, + {IBPI_PATTERN_LOCATE_OFF, ATTENTION_OFF} +}; + #define SYSFS_PCIEHP "/sys/module/pciehp" static char *get_slot_from_syspath(char *path) @@ -61,24 +68,6 @@ static char *get_slot_from_syspath(char *path) return ret; } -static void get_ctrl(enum ibpi_pattern ibpi, uint16_t *new) -{ - switch (ibpi) { - case IBPI_PATTERN_LOCATE: - *new = ATTENTION_LOCATE; - break; - case IBPI_PATTERN_FAILED_DRIVE: - *new = ATTENTION_FAILURE; - break; - case IBPI_PATTERN_REBUILD: - *new = ATTENTION_REBUILD; - break; - default: - *new = ATTENTION_OFF; - break; - } -} - static int check_slot_module(const char *slot_path) { char module_path[PATH_MAX], real_module_path[PATH_MAX]; @@ -120,11 +109,29 @@ struct pci_slot *vmdssd_find_pci_slot(char *device_path) return slot; } -int vmdssd_write(struct block_device *device, enum ibpi_pattern ibpi) +status_t vmdssd_write_attention_buf(struct pci_slot *slot, enum ibpi_pattern ibpi) { char attention_path[PATH_MAX]; char buf[WRITE_BUFFER_SIZE]; uint16_t val; + + log_debug("%s before: 0x%x\n", slot->address, + get_int(slot->sysfs_path, 0, "attention")); + val = get_value_for_ibpi(ibpi, ibpi_to_attention); + snprintf(buf, WRITE_BUFFER_SIZE, "%u", val); + snprintf(attention_path, PATH_MAX, "%s/attention", slot->sysfs_path); + if (buf_write(attention_path, buf) != (ssize_t) strnlen(buf, WRITE_BUFFER_SIZE)) { + log_error("%s write error: %d\n", slot->sysfs_path, errno); + return STATUS_FILE_WRITE_ERROR; + } + log_debug("%s after: 0x%x\n", slot->address, + get_int(slot->sysfs_path, 0, "attention")); + + return STATUS_SUCCESS; +} + +int vmdssd_write(struct block_device *device, enum ibpi_pattern ibpi) +{ struct pci_slot *slot; char *short_name = strrchr(device->sysfs_path, '/'); @@ -145,21 +152,7 @@ int vmdssd_write(struct block_device *device, enum ibpi_pattern ibpi) __set_errno_and_return(ENODEV); } - log_debug("%s before: 0x%x\n", short_name, - get_int(slot->sysfs_path, 0, "attention")); - - get_ctrl(ibpi, &val); - snprintf(buf, WRITE_BUFFER_SIZE, "%u", val); - snprintf(attention_path, PATH_MAX, "%s/attention", slot->sysfs_path); - if (buf_write(attention_path, buf) != (ssize_t) strnlen(buf, WRITE_BUFFER_SIZE)) { - log_error("%s write error: %d\n", slot->sysfs_path, errno); - return -1; - } - - log_debug("%s after: 0x%x\n", short_name, - get_int(slot->sysfs_path, 0, "attention")); - - return 0; + return vmdssd_write_attention_buf(slot, ibpi); } char *vmdssd_get_path(const char *cntrl_path) diff --git a/src/vmdssd.h b/src/vmdssd.h index 4c90fcb35ea2..f07d989280bc 100644 --- a/src/vmdssd.h +++ b/src/vmdssd.h @@ -1,6 +1,6 @@ /* * Intel(R) Enclosure LED Utilities - * Copyright (c) 2016-2019, Intel Corporation + * Copyright (c) 2016-2022, Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -22,9 +22,18 @@ #include "block.h" #include "ibpi.h" +#include "utils.h" + +#define ATTENTION_OFF 0xF /* (1111) Attention Off, Power Off */ +#define ATTENTION_LOCATE 0x7 /* (0111) Attention Off, Power On */ +#define ATTENTION_REBUILD 0x5 /* (0101) Attention On, Power On */ +#define ATTENTION_FAILURE 0xD /* (1101) Attention On, Power Off */ + +extern struct ibpi_value ibpi_to_attention[]; int vmdssd_write(struct block_device *device, enum ibpi_pattern ibpi); char *vmdssd_get_path(const char *cntrl_path); struct pci_slot *vmdssd_find_pci_slot(char *device_path); +status_t vmdssd_write_attention_buf(struct pci_slot *slot, enum ibpi_pattern ibpi); #endif -- 2.34.1