Blame src/cntrl.c

Packit 7e09eb
/*
Packit 7e09eb
 * Intel(R) Enclosure LED Utilities
Packit 7e09eb
 * Copyright (C) 2009-2020 Intel Corporation.
Packit 7e09eb
 *
Packit 7e09eb
 * This program is free software; you can redistribute it and/or modify it
Packit 7e09eb
 * under the terms and conditions of the GNU General Public License,
Packit 7e09eb
 * version 2, as published by the Free Software Foundation.
Packit 7e09eb
 *
Packit 7e09eb
 * This program is distributed in the hope it will be useful, but WITHOUT
Packit 7e09eb
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
Packit 7e09eb
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
Packit 7e09eb
 * more details.
Packit 7e09eb
 *
Packit 7e09eb
 * You should have received a copy of the GNU General Public License along with
Packit 7e09eb
 * this program; if not, write to the Free Software Foundation, Inc.,
Packit 7e09eb
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
Packit 7e09eb
 *
Packit 7e09eb
 */
Packit 7e09eb
Packit 7e09eb
#include <dirent.h>
Packit 7e09eb
#include <limits.h>
Packit 7e09eb
#include <stdint.h>
Packit 7e09eb
#include <stdio.h>
Packit 7e09eb
#include <stdlib.h>
Packit 7e09eb
#include <string.h>
Packit 7e09eb
Packit 7e09eb
#if _HAVE_DMALLOC_H
Packit 7e09eb
#include <dmalloc.h>
Packit 7e09eb
#endif
Packit 7e09eb
Packit 7e09eb
#include "cntrl.h"
Packit 7e09eb
#include "config.h"
Packit 7e09eb
#include "config_file.h"
Packit 7e09eb
#include "list.h"
Packit 7e09eb
#include "smp.h"
Packit 7e09eb
#include "status.h"
Packit 7e09eb
#include "sysfs.h"
Packit 7e09eb
#include "utils.h"
Packit 7e09eb
#include "amd.h"
Packit 7e09eb
#include "npem.h"
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Name of controllers types.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal array with names of controller types. Array can be use to
Packit 7e09eb
 * translate enumeration type into the string.
Packit 7e09eb
 */
Packit 7e09eb
static const char * const ctrl_type_str[] = {
Packit 7e09eb
	[CNTRL_TYPE_UNKNOWN] = "?",
Packit 7e09eb
	[CNTRL_TYPE_DELLSSD] = "Dell SSD",
Packit 7e09eb
	[CNTRL_TYPE_VMD]     = "VMD",
Packit 7e09eb
	[CNTRL_TYPE_SCSI]    = "SCSI",
Packit 7e09eb
	[CNTRL_TYPE_AHCI]    = "AHCI",
Packit 7e09eb
	[CNTRL_TYPE_NPEM]    = "NPEM",
Packit 7e09eb
	[CNTRL_TYPE_AMD]     = "AMD",
Packit 7e09eb
};
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 */
Packit 7e09eb
static int _is_storage_controller(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	uint64_t class = get_uint64(path, 0, "class");
Packit 7e09eb
	return (class & 0xff0000L) == 0x010000L;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 */
Packit 7e09eb
static int _is_isci_cntrl(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	return sysfs_check_driver(path, "isci");
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 */
Packit 7e09eb
static int _is_cntrl(const char *path, const char *type)
Packit 7e09eb
{
Packit 7e09eb
	char temp[PATH_MAX], link[PATH_MAX], *t;
Packit 7e09eb
Packit 7e09eb
	snprintf(temp, sizeof(temp), "%s/driver", path);
Packit 7e09eb
Packit 7e09eb
	if (realpath(temp, link) == NULL)
Packit 7e09eb
		return 0;
Packit 7e09eb
Packit 7e09eb
	t = strrchr(link, '/');
Packit 7e09eb
	if ((t != NULL) && (strcmp(t + 1, type) != 0))
Packit 7e09eb
		return 0;
Packit 7e09eb
Packit 7e09eb
	return 1;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static int _is_ahci_cntrl(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	return _is_cntrl(path, "ahci");
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static int _is_nvme_cntrl(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	return _is_cntrl(path, "nvme");
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static int _is_intel_ahci_cntrl(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	if (!_is_ahci_cntrl(path))
Packit 7e09eb
		return 0;
Packit 7e09eb
Packit 7e09eb
	return get_uint64(path, 0, "vendor") == 0x8086L;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static int _is_amd_ahci_cntrl(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	if (!_is_ahci_cntrl(path))
Packit 7e09eb
		return 0;
Packit 7e09eb
Packit 7e09eb
	return get_uint64(path, 0, "vendor") == 0x1022L;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static int _is_amd_nvme_cntrl(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	char tmp[PATH_MAX];
Packit 7e09eb
	char *t;
Packit 7e09eb
Packit 7e09eb
	if (!_is_nvme_cntrl(path))
Packit 7e09eb
		return 0;
Packit 7e09eb
Packit 7e09eb
	sprintf(tmp, "%s", path);
Packit 7e09eb
	t = strrchr(tmp, '/');
Packit 7e09eb
	if (!t)
Packit 7e09eb
		return 0;
Packit 7e09eb
Packit 7e09eb
	t++;
Packit 7e09eb
	*t = '\0';
Packit 7e09eb
	return get_uint64(tmp, 0, "vendor") == 0x1022L;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static int _is_amd_cntrl(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	if (_is_amd_ahci_cntrl(path))
Packit 7e09eb
		return 1;
Packit 7e09eb
Packit 7e09eb
	if (_is_amd_nvme_cntrl(path))
Packit 7e09eb
		return 1;
Packit 7e09eb
Packit 7e09eb
	return 0;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
extern int get_dell_server_type(void);
Packit 7e09eb
Packit 7e09eb
static int _is_dellssd_cntrl(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	uint64_t vdr, dev, svdr, cls;
Packit 7e09eb
	int gen = 0;
Packit 7e09eb
Packit 7e09eb
	vdr = get_uint64(path, 0, "vendor");
Packit 7e09eb
	dev = get_uint64(path, 0, "device");
Packit 7e09eb
	cls = get_uint64(path, 0, "class");
Packit 7e09eb
	svdr = get_uint64(path, 0, "subsystem_vendor");
Packit 7e09eb
	if (cls == 0x10802)
Packit 7e09eb
		gen = get_dell_server_type();
Packit 7e09eb
Packit 7e09eb
	return ((vdr == 0x1344L && dev == 0x5150L) || /* micron ssd */
Packit 7e09eb
		(gen != 0) ||			      /* Dell Server+NVME */
Packit 7e09eb
	        (svdr == 0x1028 && cls == 0x10802));  /* nvmhci ssd */
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 */
Packit 7e09eb
static int _is_smp_cntrl(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	int result = 0;
Packit 7e09eb
	struct list dir;
Packit 7e09eb
	char *p;
Packit 7e09eb
	char host_path[PATH_MAX] = { 0 };
Packit 7e09eb
	if (scan_dir(path, &dir) == 0) {
Packit 7e09eb
		const char *dir_path;
Packit 7e09eb
Packit 7e09eb
		list_for_each(&dir, dir_path) {
Packit 7e09eb
			p = strrchr(dir_path, '/');
Packit 7e09eb
			if (!p++)
Packit 7e09eb
				break;
Packit 7e09eb
			if (strncmp(p, "host", strlen("host")) == 0) {
Packit 7e09eb
				snprintf(host_path, sizeof(host_path),
Packit 7e09eb
					"%s/%s/bsg/sas_%s", path, p, p);
Packit 7e09eb
				result = smp_write_gpio(host_path,
Packit 7e09eb
					GPIO_REG_TYPE_TX,
Packit 7e09eb
					0,
Packit 7e09eb
					0,
Packit 7e09eb
					"",
Packit 7e09eb
					0) == 0;
Packit 7e09eb
			}
Packit 7e09eb
		}
Packit 7e09eb
		list_erase(&dir;;
Packit 7e09eb
	}
Packit 7e09eb
Packit 7e09eb
	return result;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static int _is_vmd_cntrl(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	return sysfs_check_driver(path, "vmd");
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
static int _is_npem_cntrl(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	return is_npem_capable(path);
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Determines the type of controller.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of 'controller device' module. The function
Packit 7e09eb
 * determines the type of controller device. It might be AHCI, SCSI or
Packit 7e09eb
 * UNKNOWN device type.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]      path           path to controller device in sysfs tree.
Packit 7e09eb
 *
Packit 7e09eb
 * @return The type of controller device. If the type returned is
Packit 7e09eb
 *         CNTRL_TYPE_UNKNOWN this means a controller device is not
Packit 7e09eb
 *         supported.
Packit 7e09eb
 */
Packit 7e09eb
static enum cntrl_type _get_type(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	enum cntrl_type type = CNTRL_TYPE_UNKNOWN;
Packit 7e09eb
	if (_is_npem_cntrl(path)) {
Packit 7e09eb
		type = CNTRL_TYPE_NPEM;
Packit 7e09eb
	} else if (_is_vmd_cntrl(path)) {
Packit 7e09eb
		type = CNTRL_TYPE_VMD;
Packit 7e09eb
	} else if (_is_dellssd_cntrl(path)) {
Packit 7e09eb
		type = CNTRL_TYPE_DELLSSD;
Packit 7e09eb
	} else if (_is_storage_controller(path)) {
Packit 7e09eb
		if (_is_intel_ahci_cntrl(path))
Packit 7e09eb
			type = CNTRL_TYPE_AHCI;
Packit 7e09eb
		else if (_is_amd_cntrl(path))
Packit 7e09eb
			type = CNTRL_TYPE_AMD;
Packit 7e09eb
		else if (_is_isci_cntrl(path)
Packit 7e09eb
				|| sysfs_enclosure_attached_to_cntrl(path)
Packit 7e09eb
				|| _is_smp_cntrl(path))
Packit 7e09eb
			type = CNTRL_TYPE_SCSI;
Packit 7e09eb
	}
Packit 7e09eb
	return type;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
struct _host_type *alloc_host(int id, struct _host_type *next)
Packit 7e09eb
{
Packit 7e09eb
	struct _host_type *host = NULL;
Packit 7e09eb
	host = malloc(sizeof(struct _host_type));
Packit 7e09eb
	if (host) {
Packit 7e09eb
		host->host_id = id;
Packit 7e09eb
		host->ibpi_state_buffer = NULL;
Packit 7e09eb
		memset(host->bitstream, 0, sizeof(host->bitstream));
Packit 7e09eb
		host->flush = 0;
Packit 7e09eb
		host->ports = 0;
Packit 7e09eb
		host->next = next;
Packit 7e09eb
	}
Packit 7e09eb
	return host;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
void free_hosts(struct _host_type *h)
Packit 7e09eb
{
Packit 7e09eb
	struct _host_type *t;
Packit 7e09eb
	while (h) {
Packit 7e09eb
		t = h->next;
Packit 7e09eb
		free(h->ibpi_state_buffer);
Packit 7e09eb
		free(h);
Packit 7e09eb
		h = t;
Packit 7e09eb
	}
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
void _find_host(const char *path, struct _host_type **hosts)
Packit 7e09eb
{
Packit 7e09eb
	const int host_len = sizeof("host") - 1;
Packit 7e09eb
	char *p;
Packit 7e09eb
	int index = -1;
Packit 7e09eb
	struct _host_type *th;
Packit 7e09eb
	DIR *d;
Packit 7e09eb
	struct dirent *de;
Packit 7e09eb
Packit 7e09eb
	p = strrchr(path, '/');
Packit 7e09eb
	if (!p++)
Packit 7e09eb
		return;
Packit 7e09eb
	if (strncmp(p, "host", host_len - 1) == 0) {
Packit 7e09eb
		index = atoi(p + host_len);
Packit 7e09eb
		th = alloc_host(index, (*hosts) ? (*hosts) : NULL);
Packit 7e09eb
		if (!th)
Packit 7e09eb
			return;
Packit 7e09eb
Packit 7e09eb
		d = opendir(path);
Packit 7e09eb
		if(!d) {
Packit 7e09eb
			free(th);
Packit 7e09eb
			return;
Packit 7e09eb
		}
Packit 7e09eb
Packit 7e09eb
		while ((de = readdir(d))) {
Packit 7e09eb
			if (strncmp(de->d_name, "phy-", strlen("phy-")) == 0) {
Packit 7e09eb
				th->ports++;
Packit 7e09eb
			}
Packit 7e09eb
		}
Packit 7e09eb
Packit 7e09eb
		closedir(d);
Packit 7e09eb
Packit 7e09eb
		*hosts = th;
Packit 7e09eb
	}
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Get all instances of separate hosts on isci controller.
Packit 7e09eb
 *
Packit 7e09eb
 */
Packit 7e09eb
static struct _host_type *_cntrl_get_hosts(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	struct _host_type *hosts = NULL;
Packit 7e09eb
	struct list dir;
Packit 7e09eb
	if (scan_dir(path, &dir) == 0) {
Packit 7e09eb
		const char *dir_path;
Packit 7e09eb
Packit 7e09eb
		list_for_each(&dir, dir_path)
Packit 7e09eb
			_find_host(dir_path, &hosts);
Packit 7e09eb
		list_erase(&dir;;
Packit 7e09eb
	}
Packit 7e09eb
	return hosts;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/**
Packit 7e09eb
 * @brief Check if enclosure management is enabled.
Packit 7e09eb
 *
Packit 7e09eb
 * This is internal function of 'controller device' module. The function checks
Packit 7e09eb
 * whether enclosure management is enabled for AHCI controller.
Packit 7e09eb
 *
Packit 7e09eb
 * @param[in]      path           path to controller device in sysfs tree.
Packit 7e09eb
 *
Packit 7e09eb
 * @return 1 if enclosure management is enabled, otherwise the function returns 0.
Packit 7e09eb
 */
Packit 7e09eb
static unsigned int _ahci_em_messages(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	/* first, open ...driver/module/parameters/ahci_em_messages
Packit 7e09eb
	 * then open /sys/module/libahci/parameters/ahci_em_messages
Packit 7e09eb
	 * and check if it is set to enabled.
Packit 7e09eb
	 * if so, check if 'holders' of libahci points the same driver name
Packit 7e09eb
	 * as device given by path
Packit 7e09eb
	 */
Packit 7e09eb
	char buf[PATH_MAX];
Packit 7e09eb
	char *link, *name;
Packit 7e09eb
	DIR *dh;
Packit 7e09eb
	struct dirent *de = NULL;
Packit 7e09eb
Packit 7e09eb
	/* old kernel (prior to 2.6.36) */
Packit 7e09eb
	if (get_int(path, 0, "driver/module/parameters/ahci_em_messages") != 0)
Packit 7e09eb
		return 1;
Packit 7e09eb
Packit 7e09eb
	/* parameter type changed from int to bool since kernel v3.13 */
Packit 7e09eb
	if (!get_int("", 0, "sys/module/libahci/parameters/ahci_em_messages")) {
Packit 7e09eb
		if (!get_bool("", 0, "sys/module/libahci/parameters/ahci_em_messages"))
Packit 7e09eb
			return 0;
Packit 7e09eb
	}
Packit 7e09eb
Packit 7e09eb
	if (snprintf(buf, sizeof(buf), "%s/%s", path, "driver") < 0)
Packit 7e09eb
		return 0;
Packit 7e09eb
Packit 7e09eb
	link = realpath(buf, NULL);
Packit 7e09eb
	if (!link)
Packit 7e09eb
		return 0;
Packit 7e09eb
Packit 7e09eb
	name = strrchr(link, '/');
Packit 7e09eb
	if (!name++) {
Packit 7e09eb
		free(link);
Packit 7e09eb
		return 0;
Packit 7e09eb
	}
Packit 7e09eb
Packit 7e09eb
	/* check if the directory /sys/module/libahci/holders exists */
Packit 7e09eb
	dh = opendir("/sys/module/libahci/holders");
Packit 7e09eb
	if (dh) {
Packit 7e09eb
		/* name contain controller name (ie. ahci),*/
Packit 7e09eb
		/* so check if libahci holds this driver   */
Packit 7e09eb
		while ((de = readdir(dh))) {
Packit 7e09eb
			if (!strcmp(de->d_name, name))
Packit 7e09eb
				break;
Packit 7e09eb
		}
Packit 7e09eb
		closedir(dh);
Packit 7e09eb
		free(link);
Packit 7e09eb
		return de ? 1 : 0;
Packit 7e09eb
	} else {
Packit 7e09eb
		free(link);
Packit 7e09eb
		return 1;
Packit 7e09eb
	}
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/*
Packit 7e09eb
 * Allocates memory for a new controller device structure. See cntrl.h for
Packit 7e09eb
 * details.
Packit 7e09eb
 */
Packit 7e09eb
struct cntrl_device *cntrl_device_init(const char *path)
Packit 7e09eb
{
Packit 7e09eb
	unsigned int em_enabled;
Packit 7e09eb
	enum cntrl_type type;
Packit 7e09eb
	struct cntrl_device *device = NULL;
Packit 7e09eb
Packit 7e09eb
	type = _get_type(path);
Packit 7e09eb
	if (type != CNTRL_TYPE_UNKNOWN) {
Packit 7e09eb
		switch (type) {
Packit 7e09eb
		case CNTRL_TYPE_DELLSSD:
Packit 7e09eb
		case CNTRL_TYPE_SCSI:
Packit 7e09eb
		case CNTRL_TYPE_VMD:
Packit 7e09eb
		case CNTRL_TYPE_NPEM:
Packit 7e09eb
			em_enabled = 1;
Packit 7e09eb
			break;
Packit 7e09eb
		case CNTRL_TYPE_AHCI:
Packit 7e09eb
			em_enabled = _ahci_em_messages(path);
Packit 7e09eb
			break;
Packit 7e09eb
		case CNTRL_TYPE_AMD:
Packit 7e09eb
			em_enabled = amd_em_enabled(path);
Packit 7e09eb
			break;
Packit 7e09eb
		default:
Packit 7e09eb
			em_enabled = 0;
Packit 7e09eb
		}
Packit 7e09eb
		if (em_enabled) {
Packit 7e09eb
			if (!list_is_empty(&conf.cntrls_whitelist)) {
Packit 7e09eb
				char *cntrl = NULL;
Packit 7e09eb
Packit 7e09eb
				list_for_each(&conf.cntrls_whitelist, cntrl) {
Packit 7e09eb
					if (match_string(cntrl, path))
Packit 7e09eb
						break;
Packit 7e09eb
					cntrl = NULL;
Packit 7e09eb
				}
Packit 7e09eb
				if (!cntrl) {
Packit 7e09eb
					log_debug("%s not found on whitelist, ignoring", path);
Packit 7e09eb
					return NULL;
Packit 7e09eb
				}
Packit 7e09eb
			} else if (!list_is_empty(&conf.cntrls_blacklist)) {
Packit 7e09eb
				char *cntrl;
Packit 7e09eb
Packit 7e09eb
				list_for_each(&conf.cntrls_blacklist, cntrl) {
Packit 7e09eb
					if (match_string(cntrl, path)) {
Packit 7e09eb
						log_debug("%s found on blacklist, ignoring",
Packit 7e09eb
							  path);
Packit 7e09eb
						return NULL;
Packit 7e09eb
					}
Packit 7e09eb
				}
Packit 7e09eb
			}
Packit 7e09eb
			device = malloc(sizeof(struct cntrl_device));
Packit 7e09eb
			if (device) {
Packit 7e09eb
				if (type == CNTRL_TYPE_SCSI) {
Packit 7e09eb
					device->isci_present = _is_isci_cntrl(path);
Packit 7e09eb
					device->hosts = _cntrl_get_hosts(path);
Packit 7e09eb
				} else {
Packit 7e09eb
					device->isci_present = 0;
Packit 7e09eb
					device->hosts = NULL;
Packit 7e09eb
				}
Packit 7e09eb
				device->cntrl_type = type;
Packit 7e09eb
				device->sysfs_path = str_dup(path);
Packit 7e09eb
			}
Packit 7e09eb
		} else {
Packit 7e09eb
			log_error
Packit 7e09eb
			    ("controller discovery: %s - enclosure " \
Packit 7e09eb
			     "management not supported.", path);
Packit 7e09eb
		}
Packit 7e09eb
	}
Packit 7e09eb
	return device;
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
/*
Packit 7e09eb
 * Frees memory allocated for controller device structure. See cntrl.h for
Packit 7e09eb
 * details.
Packit 7e09eb
 */
Packit 7e09eb
void cntrl_device_fini(struct cntrl_device *device)
Packit 7e09eb
{
Packit 7e09eb
	if (device) {
Packit 7e09eb
		free(device->sysfs_path);
Packit 7e09eb
		free_hosts(device->hosts);
Packit 7e09eb
		free(device);
Packit 7e09eb
	}
Packit 7e09eb
}
Packit 7e09eb
Packit 7e09eb
void print_cntrl(struct cntrl_device *ctrl_dev)
Packit 7e09eb
{
Packit 7e09eb
		printf("%s (%s)\n", ctrl_dev->sysfs_path,
Packit 7e09eb
			ctrl_type_str[ctrl_dev->cntrl_type]);
Packit 7e09eb
}