X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fblkmapd%2Fdevice-discovery.c;fp=utils%2Fblkmapd%2Fdevice-discovery.c;h=c21de3e7d6903be3f1da76c94b4c4efe6b9c456d;hp=0000000000000000000000000000000000000000;hb=6b9d7ab4df98cf9881c7cc4341631eec25bb0e26;hpb=4ee1190080fbb8cdd5ed2bef7c00a04799650b1b diff --git a/utils/blkmapd/device-discovery.c b/utils/blkmapd/device-discovery.c new file mode 100644 index 0000000..c21de3e --- /dev/null +++ b/utils/blkmapd/device-discovery.c @@ -0,0 +1,453 @@ +/* + * device-discovery.c: main function, discovering device and processing + * pipe request from kernel. + * + * Copyright (c) 2010 EMC Corporation, Haiying Tang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "device-discovery.h" + +#define BL_PIPE_FILE "/var/lib/nfs/rpc_pipefs/nfs/blocklayout" +#define PID_FILE "/var/run/blkmapd.pid" + +struct bl_disk *visible_disk_list; + +struct bl_disk_path *bl_get_path(const char *filepath, + struct bl_disk_path *paths) +{ + struct bl_disk_path *tmp = paths; + + while (tmp) { + if (!strcmp(tmp->full_path, filepath)) + break; + tmp = tmp->next; + } + return tmp; +} + +/* Check whether valid_path is a substring(partition) of path */ +int bl_is_partition(struct bl_disk_path *valid_path, struct bl_disk_path *path) +{ + if (!strncmp(valid_path->full_path, path->full_path, + strlen(valid_path->full_path))) + return 1; + + return 0; +} + +/* + * For multipath devices, devices state could be PASSIVE/ACTIVE/PSEUDO, + * where PSEUDO > ACTIVE > PASSIVE. Device with highest state is used to + * create pseudo device. So if state is higher, the device path needs to + * be updated. + * If device-mapper multipath support is a must, pseudo devices should + * exist for each multipath device. If not, active device path will be + * chosen for device creation. + * Treat partition as invalid path. + */ +int bl_update_path(struct bl_disk_path *path, enum bl_path_state_e state, + struct bl_disk *disk) +{ + struct bl_disk_path *valid_path = disk->valid_path; + + if (valid_path) { + if (valid_path->state >= state) { + if (bl_is_partition(valid_path, path)) + return 0; + } + } + return 1; +} + +void bl_release_disk(void) +{ + struct bl_disk *disk; + struct bl_disk_path *path = NULL; + + while (visible_disk_list) { + disk = visible_disk_list; + path = disk->paths; + while (path) { + disk->paths = path->next; + free(path->full_path); + free(path); + path = disk->paths; + } + if (disk->serial) + free(disk->serial); + visible_disk_list = disk->next; + free(disk); + } +} + +void bl_add_disk(char *filepath) +{ + struct bl_disk *disk = NULL; + int fd = 0; + struct stat sb; + off_t size = 0; + struct bl_serial *serial = NULL; + enum bl_path_state_e ap_state; + struct bl_disk_path *diskpath = NULL, *path = NULL; + dev_t dev; + + fd = open(filepath, O_RDONLY | O_LARGEFILE); + if (fd < 0) + return; + + if (fstat(fd, &sb)) { + close(fd); + return; + } + + if (!sb.st_size) + ioctl(fd, BLKGETSIZE, &size); + else + size = sb.st_size; + + if (!size) { + close(fd); + return; + } + + dev = sb.st_rdev; + serial = bldev_read_serial(fd, filepath); + if (dm_is_dm_major(major(dev))) + ap_state = BL_PATH_STATE_PSEUDO; + else + ap_state = bldev_read_ap_state(fd); + close(fd); + + if (ap_state != BL_PATH_STATE_ACTIVE) + return; + + for (disk = visible_disk_list; disk != NULL; disk = disk->next) { + /* Already scanned or a partition? + * XXX: if released each time, maybe not need to compare + */ + if ((serial->len == disk->serial->len) && + !memcmp(serial->data, disk->serial->data, serial->len)) { + diskpath = bl_get_path(filepath, disk->paths); + break; + } + } + + if (disk && diskpath) + return; + + /* add path */ + path = malloc(sizeof(struct bl_disk_path)); + if (!path) { + BL_LOG_ERR("%s: Out of memory!\n", __func__); + goto out_err; + } + path->next = NULL; + path->state = ap_state; + path->full_path = strdup(filepath); + if (!path->full_path) + goto out_err; + + if (!disk) { /* add disk */ + disk = malloc(sizeof(struct bl_disk)); + if (!disk) { + BL_LOG_ERR("%s: Out of memory!\n", __func__); + goto out_err; + } + disk->next = visible_disk_list; + disk->dev = dev; + disk->size = size; + disk->serial = serial; + disk->valid_path = path; + disk->paths = path; + visible_disk_list = disk; + } else { + path->next = disk->paths; + disk->paths = path; + /* check whether we need to update disk info */ + if (bl_update_path(path, path->state, disk)) { + disk->dev = dev; + disk->size = size; + disk->valid_path = path; + } + } + return; + + out_err: + if (path) { + if (path->full_path) + free(path->full_path); + free(path); + } + return; +} + +int bl_discover_devices(void) +{ + FILE *f; + int n; + char buf[PATH_MAX], devname[PATH_MAX], fulldevname[PATH_MAX]; + + /* release previous list */ + bl_release_disk(); + + /* scan all block devices */ + f = fopen("/proc/partitions", "r"); + if (f == NULL) + return 0; + + while (1) { + if (fgets(buf, sizeof buf, f) == NULL) + break; + n = sscanf(buf, "%*d %*d %*d %31s", devname); + if (n != 1) + continue; + snprintf(fulldevname, sizeof fulldevname, "/sys/block/%s", + devname); + if (access(fulldevname, F_OK) < 0) + continue; + snprintf(fulldevname, sizeof fulldevname, "/dev/%s", devname); + bl_add_disk(fulldevname); + } + + fclose(f); + + return 0; +} + +/* process kernel request + * return 0: request processed, and no more request waiting; + * return 1: request processed, and more requests waiting; + * return < 0: error + */ +int bl_disk_inquiry_process(int fd) +{ + int ret = 0; + struct bl_pipemsg_hdr head; + char *buf = NULL; + uint32_t major, minor; + uint16_t buflen; + struct bl_dev_msg reply; + + /* read request */ + if (atomicio(read, fd, &head, sizeof(head)) != sizeof(head)) { + /* Note that an error in this or the next read is pretty + * catastrophic, as there is no good way to resync into + * the pipe's stream. + */ + BL_LOG_ERR("Read pipefs head error!\n"); + ret = -EIO; + goto out; + } + + buflen = head.totallen; + buf = malloc(buflen); + if (!buf) { + BL_LOG_ERR("%s: Out of memory!\n", __func__); + ret = -ENOMEM; + goto out; + } + + if (atomicio(read, fd, buf, buflen) != buflen) { + BL_LOG_ERR("Read pipefs content error!\n"); + ret = -EIO; + goto out; + } + + reply.status = BL_DEVICE_REQUEST_PROC; + + switch (head.type) { + case BL_DEVICE_MOUNT: + /* + * It shouldn't be necessary to discover devices here, since + * process_deviceinfo() will re-discover if it can't find + * the devices it needs. But in the case of multipath + * devices (ones that appear more than once, for example an + * active and a standby LUN), this will re-order them in the + * correct priority. + */ + bl_discover_devices(); + if (!process_deviceinfo(buf, buflen, &major, &minor)) { + reply.status = BL_DEVICE_REQUEST_ERR; + break; + } + reply.major = major; + reply.minor = minor; + break; + case BL_DEVICE_UMOUNT: + if (!dm_device_remove_all((uint64_t *) buf)) + reply.status = BL_DEVICE_REQUEST_ERR; + break; + default: + reply.status = BL_DEVICE_REQUEST_ERR; + break; + } + + /* write to pipefs */ + if (atomicio((void *)write, fd, &reply, sizeof(reply)) + != sizeof(reply)) { + BL_LOG_ERR("Write pipefs error!\n"); + ret = -EIO; + } + + out: + if (buf) + free(buf); + return ret; +} + +/* TODO: set bl_process_stop to 1 in command */ +unsigned int bl_process_stop; + +int bl_run_disk_inquiry_process(int fd) +{ + fd_set rset; + int ret; + + bl_process_stop = 0; + + for (;;) { + if (bl_process_stop) + return 1; + FD_ZERO(&rset); + FD_SET(fd, &rset); + ret = 0; + switch (select(fd + 1, &rset, NULL, NULL, NULL)) { + case -1: + if (errno == EINTR) + continue; + else { + ret = -errno; + goto out; + } + case 0: + goto out; + default: + if (FD_ISSET(fd, &rset)) + ret = bl_disk_inquiry_process(fd); + } + } + out: + return ret; +} + +/* Daemon */ +int main(int argc, char **argv) +{ + int fd, pidfd = -1, opt, dflag = 0, fg = 0, ret = 1; + struct stat statbuf; + char pidbuf[64]; + + while ((opt = getopt(argc, argv, "df")) != -1) { + switch (opt) { + case 'd': + dflag = 1; + break; + case 'f': + fg = 1; + break; + } + } + + if (fg) { + openlog("blkmapd", LOG_PERROR, 0); + } else { + if (!stat(PID_FILE, &statbuf)) { + fprintf(stderr, "Pid file %s already existed\n", PID_FILE); + exit(1); + } + + if (daemon(0, 0) != 0) { + fprintf(stderr, "Daemonize failed\n"); + exit(1); + } + + openlog("blkmapd", LOG_PID, 0); + pidfd = open(PID_FILE, O_WRONLY | O_CREAT, 0644); + if (pidfd < 0) { + BL_LOG_ERR("Create pid file %s failed\n", PID_FILE); + exit(1); + } + + if (lockf(pidfd, F_TLOCK, 0) < 0) { + BL_LOG_ERR("Lock pid file %s failed\n", PID_FILE); + close(pidfd); + exit(1); + } + ftruncate(pidfd, 0); + sprintf(pidbuf, "%d\n", getpid()); + write(pidfd, pidbuf, strlen(pidbuf)); + } + + if (dflag) { + bl_discover_devices(); + exit(0); + } + + /* open pipe file */ + fd = open(BL_PIPE_FILE, O_RDWR); + if (fd < 0) { + BL_LOG_ERR("open pipe file %s error\n", BL_PIPE_FILE); + exit(1); + } + + while (1) { + /* discover device when needed */ + bl_discover_devices(); + + ret = bl_run_disk_inquiry_process(fd); + if (ret < 0) { + /* what should we do with process error? */ + BL_LOG_ERR("inquiry process return %d\n", ret); + } + } + + if (pidfd >= 0) { + close(pidfd); + unlink(PID_FILE); + } + + exit(ret); +}