Blame src/block.c

Packit Service db8df9
/*
Packit Service db8df9
 * Intel(R) Enclosure LED Utilities
Packit Service db8df9
 * Copyright (C) 2009-2020 Intel Corporation.
Packit Service db8df9
 *
Packit Service db8df9
 * This program is free software; you can redistribute it and/or modify it
Packit Service db8df9
 * under the terms and conditions of the GNU General Public License,
Packit Service db8df9
 * version 2, as published by the Free Software Foundation.
Packit Service db8df9
 *
Packit Service db8df9
 * This program is distributed in the hope it will be useful, but WITHOUT
Packit Service db8df9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
Packit Service db8df9
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
Packit Service db8df9
 * more details.
Packit Service db8df9
 *
Packit Service db8df9
 * You should have received a copy of the GNU General Public License along with
Packit Service db8df9
 * this program; if not, write to the Free Software Foundation, Inc.,
Packit Service db8df9
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
Packit Service db8df9
 *
Packit Service db8df9
 */
Packit Service db8df9
Packit Service db8df9
#include <dirent.h>
Packit Service db8df9
#include <fcntl.h>
Packit Service db8df9
#include <limits.h>
Packit Service db8df9
#include <stdint.h>
Packit Service db8df9
#include <stdio.h>
Packit Service db8df9
#include <stdlib.h>
Packit Service db8df9
#include <string.h>
Packit Service db8df9
#include <sys/types.h>
Packit Service db8df9
#include <unistd.h>
Packit Service db8df9
Packit Service db8df9
#if _HAVE_DMALLOC_H
Packit Service db8df9
#include <dmalloc.h>
Packit Service db8df9
#endif
Packit Service db8df9
Packit Service db8df9
#include "ahci.h"
Packit Service db8df9
#include "block.h"
Packit Service db8df9
#include "config.h"
Packit Service db8df9
#include "dellssd.h"
Packit Service db8df9
#include "pci_slot.h"
Packit Service db8df9
#include "raid.h"
Packit Service db8df9
#include "scsi.h"
Packit Service db8df9
#include "slave.h"
Packit Service db8df9
#include "smp.h"
Packit Service db8df9
#include "status.h"
Packit Service db8df9
#include "sysfs.h"
Packit Service db8df9
#include "utils.h"
Packit Service db8df9
#include "vmdssd.h"
Packit Service db8df9
#include "npem.h"
Packit Service db8df9
#include "amd.h"
Packit Service db8df9
Packit Service db8df9
/* Global timestamp value. It shell be used to update a timestamp field of block
Packit Service db8df9
   device structure. See block.h for details. */
Packit Service db8df9
time_t timestamp = 0;
Packit Service db8df9
Packit Service db8df9
/**
Packit Service db8df9
 * @brief Determines if disk is attached directly or via expander
Packit Service db8df9
 */
Packit Service db8df9
int dev_directly_attached(const char *path)
Packit Service db8df9
{
Packit Service db8df9
	if (strstr(path, "/expander") == 0)
Packit Service db8df9
		return 1;
Packit Service db8df9
	return 0;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
/**
Packit Service db8df9
 * @brief Determines a send function.
Packit Service db8df9
 *
Packit Service db8df9
 * This is the internal function of 'block device' module. The function tries to
Packit Service db8df9
 * determine a LED management protocol based on controller type and the given
Packit Service db8df9
 * path to block device in sysfs tree. First it checks whether to use
Packit Service db8df9
 * the default send function. If not it tries to read the content
Packit Service db8df9
 * of em_message_type field from sysfs tree and determines
Packit Service db8df9
 * the LED control protocol.
Packit Service db8df9
 *
Packit Service db8df9
 * @param[in]    cntrl            type of a controller a device is connected to.
Packit Service db8df9
 * @param[in]    path             path to a block device in sysfs tree.
Packit Service db8df9
 *
Packit Service db8df9
 * @return Pointer to send message function if successful, otherwise the function
Packit Service db8df9
 *         returns the NULL pointer and it means either the controller does not
Packit Service db8df9
 *         support enclosure management or LED control protocol
Packit Service db8df9
 *         is not supported.
Packit Service db8df9
 */
Packit Service db8df9
static send_message_t _get_send_fn(struct cntrl_device *cntrl, const char *path)
Packit Service db8df9
{
Packit Service db8df9
	send_message_t result = NULL;
Packit Service db8df9
Packit Service db8df9
	if (cntrl->cntrl_type == CNTRL_TYPE_AHCI) {
Packit Service db8df9
		result = ahci_sgpio_write;
Packit Service db8df9
	} else if (cntrl->cntrl_type == CNTRL_TYPE_SCSI
Packit Service db8df9
		   && !dev_directly_attached(path)) {
Packit Service db8df9
		result = scsi_ses_write;
Packit Service db8df9
	} else if (cntrl->cntrl_type == CNTRL_TYPE_SCSI
Packit Service db8df9
		   && dev_directly_attached(path)) {
Packit Service db8df9
		result = scsi_smp_fill_buffer;
Packit Service db8df9
	} else if (cntrl->cntrl_type == CNTRL_TYPE_DELLSSD) {
Packit Service db8df9
		result = dellssd_write;
Packit Service db8df9
	} else if (cntrl->cntrl_type == CNTRL_TYPE_VMD) {
Packit Service db8df9
		result = vmdssd_write;
Packit Service db8df9
	} else if (cntrl->cntrl_type == CNTRL_TYPE_NPEM) {
Packit Service db8df9
		result = npem_write;
Packit Service db8df9
	} else if (cntrl->cntrl_type == CNTRL_TYPE_AMD) {
Packit Service db8df9
		result = amd_write;
Packit Service db8df9
	}
Packit Service db8df9
	return result;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static int do_not_flush(struct block_device *device __attribute__ ((unused)))
Packit Service db8df9
{
Packit Service db8df9
	return 1;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static flush_message_t _get_flush_fn(struct cntrl_device *cntrl, const char *path)
Packit Service db8df9
{
Packit Service db8df9
	flush_message_t result = NULL;
Packit Service db8df9
Packit Service db8df9
	if (cntrl->cntrl_type == CNTRL_TYPE_SCSI) {
Packit Service db8df9
		if (dev_directly_attached(path))
Packit Service db8df9
			result = scsi_smp_write_buffer;
Packit Service db8df9
		else
Packit Service db8df9
			result = scsi_ses_flush;
Packit Service db8df9
	} else {
Packit Service db8df9
		result = do_not_flush;
Packit Service db8df9
	}
Packit Service db8df9
	return result;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
/**
Packit Service db8df9
 * @brief Determines a host path to block device.
Packit Service db8df9
 *
Packit Service db8df9
 * This is the internal function of 'block device' module. The function
Packit Service db8df9
 * determines a host path to block device in sysfs.
Packit Service db8df9
 *
Packit Service db8df9
 * @param[in]      path           path to block device in sysfs.
Packit Service db8df9
 * @param[in]      cntrl          controller device the block
Packit Service db8df9
 *                                device is connected to.
Packit Service db8df9
 *
Packit Service db8df9
 * @return Pointer to memory block containing a host path. The memory block
Packit Service db8df9
 *         should be freed if one don't need the content.
Packit Service db8df9
 */
Packit Service db8df9
static char *_get_host(char *path, struct cntrl_device *cntrl)
Packit Service db8df9
{
Packit Service db8df9
	char *result = NULL;
Packit Service db8df9
Packit Service db8df9
	if (cntrl->cntrl_type == CNTRL_TYPE_SCSI)
Packit Service db8df9
		result = scsi_get_slot_path(path, cntrl->sysfs_path);
Packit Service db8df9
	else if (cntrl->cntrl_type == CNTRL_TYPE_AHCI)
Packit Service db8df9
		result = ahci_get_port_path(path);
Packit Service db8df9
	else if (cntrl->cntrl_type == CNTRL_TYPE_DELLSSD)
Packit Service db8df9
		result = dellssd_get_path(cntrl->sysfs_path);
Packit Service db8df9
	else if (cntrl->cntrl_type == CNTRL_TYPE_VMD)
Packit Service db8df9
		result = vmdssd_get_path(cntrl->sysfs_path);
Packit Service db8df9
	else if (cntrl->cntrl_type == CNTRL_TYPE_NPEM)
Packit Service db8df9
		result = npem_get_path(cntrl->sysfs_path);
Packit Service db8df9
	else if (cntrl->cntrl_type == CNTRL_TYPE_AMD)
Packit Service db8df9
		result = amd_get_path(path, cntrl->sysfs_path);
Packit Service db8df9
  
Packit Service db8df9
	return result;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static int is_host_id_supported(const struct block_device *bd)
Packit Service db8df9
{
Packit Service db8df9
	if (!bd->cntrl)
Packit Service db8df9
		return 0;
Packit Service db8df9
Packit Service db8df9
	switch (bd->cntrl->cntrl_type) {
Packit Service db8df9
	case CNTRL_TYPE_DELLSSD:
Packit Service db8df9
	case CNTRL_TYPE_VMD:
Packit Service db8df9
	case CNTRL_TYPE_NPEM:
Packit Service db8df9
		return 0;
Packit Service db8df9
	default:
Packit Service db8df9
		return 1;
Packit Service db8df9
	}
Packit Service db8df9
}
Packit Service db8df9
/**
Packit Service db8df9
 * @brief Determines a storage controller.
Packit Service db8df9
 *
Packit Service db8df9
 * This is the internal function of 'block device' module. The function gets
Packit Service db8df9
 * a pointer to controller structure the device is connected to.
Packit Service db8df9
 *
Packit Service db8df9
 * @param[in]      cntrl_list     pointer to list of supported controllers.
Packit Service db8df9
 * @param[in]      path           path to block device in sysfs tree.
Packit Service db8df9
 *
Packit Service db8df9
 * @return Pointer to controller structure if successful, otherwise the function
Packit Service db8df9
 *         returns NULL pointer. The NULL pointer means that block devices is
Packit Service db8df9
 *         connected to unsupported storage controller.
Packit Service db8df9
 */
Packit Service db8df9
struct cntrl_device *block_get_controller(const struct list *cntrl_list, char *path)
Packit Service db8df9
{
Packit Service db8df9
	struct cntrl_device *cntrl;
Packit Service db8df9
	struct cntrl_device *non_npem_cntrl = NULL;
Packit Service db8df9
Packit Service db8df9
	list_for_each(cntrl_list, cntrl) {
Packit Service db8df9
		if (strncmp(cntrl->sysfs_path, path,
Packit Service db8df9
			    strlen(cntrl->sysfs_path)) == 0) {
Packit Service db8df9
			if (cntrl->cntrl_type == CNTRL_TYPE_NPEM)
Packit Service db8df9
				return cntrl;
Packit Service db8df9
			non_npem_cntrl = cntrl;
Packit Service db8df9
		}
Packit Service db8df9
	}
Packit Service db8df9
	return non_npem_cntrl;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
struct _host_type *block_get_host(struct cntrl_device *cntrl, int host_id)
Packit Service db8df9
{
Packit Service db8df9
	struct _host_type *hosts = NULL;
Packit Service db8df9
Packit Service db8df9
	if (!cntrl)
Packit Service db8df9
		return hosts;
Packit Service db8df9
Packit Service db8df9
	hosts = cntrl->hosts;
Packit Service db8df9
	while (hosts) {
Packit Service db8df9
		if (hosts->host_id == host_id)
Packit Service db8df9
			break;
Packit Service db8df9
		hosts = hosts->next;
Packit Service db8df9
	}
Packit Service db8df9
	return hosts;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
/*
Packit Service db8df9
 * Allocates a new block device structure. See block.h for details.
Packit Service db8df9
 */
Packit Service db8df9
struct block_device *block_device_init(const struct list *cntrl_list, const char *path)
Packit Service db8df9
{
Packit Service db8df9
	struct cntrl_device *cntrl;
Packit Service db8df9
	char link[PATH_MAX];
Packit Service db8df9
	char *host = NULL;
Packit Service db8df9
	struct block_device *device = NULL;
Packit Service db8df9
	struct pci_slot *pci_slot = NULL;
Packit Service db8df9
	send_message_t send_fn = NULL;
Packit Service db8df9
	flush_message_t flush_fn = NULL;
Packit Service db8df9
	int host_id = -1;
Packit Service db8df9
	char *host_name;
Packit Service db8df9
Packit Service db8df9
	if (realpath(path, link)) {
Packit Service db8df9
		pci_slot = vmdssd_find_pci_slot(link);
Packit Service db8df9
		cntrl = block_get_controller(cntrl_list, link);
Packit Service db8df9
		if (cntrl != NULL) {
Packit Service db8df9
			if (cntrl->cntrl_type == CNTRL_TYPE_VMD && !pci_slot)
Packit Service db8df9
				return NULL;
Packit Service db8df9
			host = _get_host(link, cntrl);
Packit Service db8df9
			if (host == NULL)
Packit Service db8df9
				return NULL;
Packit Service db8df9
			host_name = get_path_hostN(link);
Packit Service db8df9
			if (host_name) {
Packit Service db8df9
				if (sscanf(host_name, "host%d", &host_id) != 1)
Packit Service db8df9
					host_id = -1;
Packit Service db8df9
				free(host_name);
Packit Service db8df9
			}
Packit Service db8df9
			flush_fn = _get_flush_fn(cntrl, link);
Packit Service db8df9
			send_fn = _get_send_fn(cntrl, link);
Packit Service db8df9
			if (send_fn  == NULL) {
Packit Service db8df9
				free(host);
Packit Service db8df9
				return NULL;
Packit Service db8df9
			}
Packit Service db8df9
		} else {
Packit Service db8df9
			return NULL;
Packit Service db8df9
		}
Packit Service db8df9
Packit Service db8df9
		device = calloc(1, sizeof(*device));
Packit Service db8df9
		if (device) {
Packit Service db8df9
			struct _host_type *hosts = cntrl ? cntrl->hosts : NULL;
Packit Service db8df9
Packit Service db8df9
			device->cntrl = cntrl;
Packit Service db8df9
			device->sysfs_path = str_dup(link);
Packit Service db8df9
			device->cntrl_path = host;
Packit Service db8df9
			device->ibpi = IBPI_PATTERN_UNKNOWN;
Packit Service db8df9
			device->ibpi_prev = IBPI_PATTERN_NONE;
Packit Service db8df9
			device->send_fn = send_fn;
Packit Service db8df9
			device->flush_fn = flush_fn;
Packit Service db8df9
			device->timestamp = timestamp;
Packit Service db8df9
			device->host = NULL;
Packit Service db8df9
			device->host_id = host_id;
Packit Service db8df9
			device->encl_index = -1;
Packit Service db8df9
			device->raid_dev = NULL;
Packit Service db8df9
			while (hosts) {
Packit Service db8df9
				if (hosts->host_id == host_id) {
Packit Service db8df9
					device->host = hosts;
Packit Service db8df9
					break;
Packit Service db8df9
				}
Packit Service db8df9
				hosts = hosts->next;
Packit Service db8df9
			}
Packit Service db8df9
			if (cntrl && cntrl->cntrl_type == CNTRL_TYPE_SCSI) {
Packit Service db8df9
				device->phy_index = cntrl_init_smp(link, cntrl);
Packit Service db8df9
				if (!dev_directly_attached(link)
Packit Service db8df9
						&& !scsi_get_enclosure(device)) {
Packit Service db8df9
					log_debug("Device initialization failed for '%s'",
Packit Service db8df9
							path);
Packit Service db8df9
					free(device->sysfs_path);
Packit Service db8df9
					free(device->cntrl_path);
Packit Service db8df9
					free(device);
Packit Service db8df9
					device = NULL;
Packit Service db8df9
				}
Packit Service db8df9
			}
Packit Service db8df9
		} else if (host) {
Packit Service db8df9
			free(host);
Packit Service db8df9
		}
Packit Service db8df9
	}
Packit Service db8df9
	return device;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
/**
Packit Service db8df9
 * Frees memory allocated for block device structure. See block.h for details.
Packit Service db8df9
 */
Packit Service db8df9
void block_device_fini(struct block_device *device)
Packit Service db8df9
{
Packit Service db8df9
	if (device) {
Packit Service db8df9
		if (device->sysfs_path)
Packit Service db8df9
			free(device->sysfs_path);
Packit Service db8df9
Packit Service db8df9
		if (device->cntrl_path)
Packit Service db8df9
			free(device->cntrl_path);
Packit Service db8df9
Packit Service db8df9
		if (device->raid_dev)
Packit Service db8df9
			raid_device_fini(device->raid_dev);
Packit Service db8df9
Packit Service db8df9
		free(device);
Packit Service db8df9
	}
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
/*
Packit Service db8df9
 * Duplicates a block device structure. See block.h for details.
Packit Service db8df9
 */
Packit Service db8df9
struct block_device *block_device_duplicate(struct block_device *block)
Packit Service db8df9
{
Packit Service db8df9
	struct block_device *result = NULL;
Packit Service db8df9
Packit Service db8df9
	if (block) {
Packit Service db8df9
		result = calloc(1, sizeof(*result));
Packit Service db8df9
		if (result) {
Packit Service db8df9
			result->sysfs_path = str_dup(block->sysfs_path);
Packit Service db8df9
			result->cntrl_path = str_dup(block->cntrl_path);
Packit Service db8df9
			if (block->ibpi != IBPI_PATTERN_UNKNOWN)
Packit Service db8df9
				result->ibpi = block->ibpi;
Packit Service db8df9
			else
Packit Service db8df9
				result->ibpi = IBPI_PATTERN_ONESHOT_NORMAL;
Packit Service db8df9
			result->ibpi_prev = block->ibpi_prev;
Packit Service db8df9
			result->send_fn = block->send_fn;
Packit Service db8df9
			result->flush_fn = block->flush_fn;
Packit Service db8df9
			result->timestamp = block->timestamp;
Packit Service db8df9
			result->cntrl = block->cntrl;
Packit Service db8df9
			result->host = block->host;
Packit Service db8df9
			result->host_id = block->host_id;
Packit Service db8df9
			result->phy_index = block->phy_index;
Packit Service db8df9
			result->encl_index = block->encl_index;
Packit Service db8df9
			result->enclosure = block->enclosure;
Packit Service db8df9
			result->raid_dev =
Packit Service db8df9
				raid_device_duplicate(block->raid_dev);
Packit Service db8df9
		}
Packit Service db8df9
	}
Packit Service db8df9
	return result;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
int block_compare(const struct block_device *bd_old,
Packit Service db8df9
		  const struct block_device *bd_new)
Packit Service db8df9
{
Packit Service db8df9
	int i = 0;
Packit Service db8df9
Packit Service db8df9
	if (is_host_id_supported(bd_old) && bd_old->host_id == -1) {
Packit Service db8df9
		log_debug("Device %s : No host_id!",
Packit Service db8df9
			  strstr(bd_old->sysfs_path, "host"));
Packit Service db8df9
		return 0;
Packit Service db8df9
	}
Packit Service db8df9
	if (is_host_id_supported(bd_new) && bd_new->host_id == -1) {
Packit Service db8df9
		log_debug("Device %s : No host_id!",
Packit Service db8df9
			  strstr(bd_new->sysfs_path, "host"));
Packit Service db8df9
		return 0;
Packit Service db8df9
	}
Packit Service db8df9
Packit Service db8df9
	if (bd_old->cntrl->cntrl_type != bd_new->cntrl->cntrl_type)
Packit Service db8df9
		return 0;
Packit Service db8df9
Packit Service db8df9
	switch (bd_old->cntrl->cntrl_type) {
Packit Service db8df9
	case CNTRL_TYPE_AHCI:
Packit Service db8df9
		/* Missing support for port multipliers. Compare just hostX. */
Packit Service db8df9
		i = (bd_old->host_id == bd_new->host_id);
Packit Service db8df9
		break;
Packit Service db8df9
Packit Service db8df9
	case CNTRL_TYPE_SCSI:
Packit Service db8df9
		/* Host and phy is not enough. They might be DA or EA. */
Packit Service db8df9
		if (dev_directly_attached(bd_old->sysfs_path) &&
Packit Service db8df9
		    dev_directly_attached(bd_new->sysfs_path)) {
Packit Service db8df9
			/* Just compare host & phy */
Packit Service db8df9
			i = (bd_old->host_id == bd_new->host_id) &&
Packit Service db8df9
			    (bd_old->phy_index == bd_new->phy_index);
Packit Service db8df9
			break;
Packit Service db8df9
		}
Packit Service db8df9
		if (!dev_directly_attached(bd_old->sysfs_path) &&
Packit Service db8df9
		    !dev_directly_attached(bd_new->sysfs_path)) {
Packit Service db8df9
			/* Both expander attached */
Packit Service db8df9
			i = (bd_old->host_id == bd_new->host_id) &&
Packit Service db8df9
			    (bd_old->phy_index == bd_new->phy_index);
Packit Service db8df9
			i = i && (bd_old->enclosure == bd_new->enclosure);
Packit Service db8df9
			i = i && (bd_old->encl_index == bd_new->encl_index);
Packit Service db8df9
			break;
Packit Service db8df9
		}
Packit Service db8df9
		/* */
Packit Service db8df9
		break;
Packit Service db8df9
Packit Service db8df9
	case CNTRL_TYPE_VMD:
Packit Service db8df9
		/* compare names and address of the drive */
Packit Service db8df9
		i = (strcmp(bd_old->sysfs_path, bd_new->sysfs_path) == 0);
Packit Service db8df9
		if (!i) {
Packit Service db8df9
			struct pci_slot *old_slot, *new_slot;
Packit Service db8df9
Packit Service db8df9
			old_slot = vmdssd_find_pci_slot(bd_old->sysfs_path);
Packit Service db8df9
			new_slot = vmdssd_find_pci_slot(bd_new->sysfs_path);
Packit Service db8df9
			if (old_slot && new_slot)
Packit Service db8df9
				i = (strcmp(old_slot->address, new_slot->address) == 0);
Packit Service db8df9
		}
Packit Service db8df9
		break;
Packit Service db8df9
Packit Service db8df9
	case CNTRL_TYPE_NPEM:
Packit Service db8df9
		/* check controller to determine slot. */
Packit Service db8df9
		i = (strcmp(bd_old->cntrl_path, bd_new->cntrl_path) == 0);
Packit Service db8df9
		break;
Packit Service db8df9
Packit Service db8df9
	case CNTRL_TYPE_DELLSSD:
Packit Service db8df9
	default:
Packit Service db8df9
		/* Just compare names */
Packit Service db8df9
		i = (strcmp(bd_old->sysfs_path, bd_new->sysfs_path) == 0);
Packit Service db8df9
		break;
Packit Service db8df9
	}
Packit Service db8df9
	return i;
Packit Service db8df9
}