X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Futils%2Fmount_utils_ldiskfs.c;h=728c349cfe537991c88d4b56a5686bc541209c34;hb=723613a734af240e03d9e8b42afeba442a77142d;hp=e334f580febb852bec8c40c48c6b2e93576f3b12;hpb=af79b4e7e89c6c5f455a0526f1598dae217db2e5;p=fs%2Flustre-release.git diff --git a/lustre/utils/mount_utils_ldiskfs.c b/lustre/utils/mount_utils_ldiskfs.c index e334f58..728c349 100644 --- a/lustre/utils/mount_utils_ldiskfs.c +++ b/lustre/utils/mount_utils_ldiskfs.c @@ -27,7 +27,7 @@ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Use is subject to license terms. * - * Copyright (c) 2011, Whamcloud, Inc. + * Copyright (c) 2012, 2013, Intel Corporation. */ /* * This file is part of Lustre, http://www.lustre.org/ @@ -47,12 +47,14 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif +#include "mount_utils.h" #include #include #include #include #include #include +#include #include #include @@ -77,7 +79,17 @@ #include #include #include -#include "mount_utils.h" + +#ifdef HAVE_SELINUX +#include +#endif + +#define MAX_HW_SECTORS_KB_PATH "queue/max_hw_sectors_kb" +#define MAX_SECTORS_KB_PATH "queue/max_sectors_kb" +#define SCHEDULER_PATH "queue/scheduler" +#define STRIPE_CACHE_SIZE "md/stripe_cache_size" + +#define DEFAULT_SCHEDULER "deadline" extern char *progname; @@ -86,6 +98,29 @@ extern char *progname; #define DUMMY_FILE_NAME_LEN 25 #define EXT3_DIRENT_SIZE DUMMY_FILE_NAME_LEN +/* + * Concatenate context of the temporary mount point iff selinux is enabled + */ +#ifdef HAVE_SELINUX +void append_context_for_mount(char *mntpt, struct mkfs_opts *mop) +{ + security_context_t fcontext; + + if (getfilecon(mntpt, &fcontext) < 0) { + /* Continuing with default behaviour */ + fprintf(stderr, "%s: Get file context failed : %s\n", + progname, strerror(errno)); + return; + } + + if (fcontext != NULL) { + strcat(mop->mo_ldd.ldd_mount_opts, ",context="); + strcat(mop->mo_ldd.ldd_mount_opts, fcontext); + freecon(fcontext); + } +} +#endif + /* Write the server config files */ int ldiskfs_write_ldd(struct mkfs_opts *mop) { @@ -103,6 +138,14 @@ int ldiskfs_write_ldd(struct mkfs_opts *mop) return errno; } + /* + * Append file context to mount options if SE Linux is enabled + */ + #ifdef HAVE_SELINUX + if (is_selinux_enabled() > 0) + append_context_for_mount(mntpt, mop); + #endif + dev = mop->mo_device; if (mop->mo_flags & MO_IS_LOOP) dev = mop->mo_loopdev; @@ -145,6 +188,7 @@ int ldiskfs_write_ldd(struct mkfs_opts *mop) if (num < 1 && ferror(filep)) { fprintf(stderr, "%s: Unable to write to file (%s): %s\n", progname, filepnm, strerror(errno)); + fclose(filep); goto out_umnt; } fclose(filep); @@ -156,6 +200,25 @@ out_rmdir: return ret; } +static int readcmd(char *cmd, char *buf, int len) +{ + FILE *fp; + int red; + + fp = popen(cmd, "r"); + if (!fp) + return errno; + + red = fread(buf, 1, len, fp); + pclose(fp); + + /* strip trailing newline */ + if (buf[red - 1] == '\n') + buf[red - 1] = '\0'; + + return (red == 0) ? -ENOENT : 0; +} + int ldiskfs_read_ldd(char *dev, struct lustre_disk_data *mo_ldd) { char tmpdir[] = "/tmp/dirXXXXXX"; @@ -194,16 +257,19 @@ int ldiskfs_read_ldd(char *dev, struct lustre_disk_data *mo_ldd) if (num_read < 1 && ferror(filep)) { fprintf(stderr, "%s: Unable to read from file %s: %s\n", progname, filepnm, strerror(errno)); - goto out_close; } + fclose(filep); } -out_close: - fclose(filep); snprintf(cmd, cmdsz, "rm -rf %s", tmpdir); run_command(cmd, cmdsz); if (ret) verrprint("Failed to read old data (%d)\n", ret); + + /* As long as we at least have the label, we're good to go */ + snprintf(cmd, sizeof(cmd), E2LABEL" %s", dev); + ret = readcmd(cmd, mo_ldd->ldd_svname, sizeof(mo_ldd->ldd_svname) - 1); + return ret; } @@ -259,7 +325,7 @@ static int file_in_dev(char *file_name, char *dev_name) pclose(fp); return 1; } - i = fread(debugfs_cmd, 1, sizeof(debugfs_cmd), fp); + i = fread(debugfs_cmd, 1, sizeof(debugfs_cmd) - 1, fp); if (i) { debugfs_cmd[i] = 0; fprintf(stderr, "%s", debugfs_cmd); @@ -318,7 +384,8 @@ static int is_e2fsprogs_feature_supp(const char *feature) fprintf(stderr, "%s: %s\n", progname, strerror(errno)); return 0; } - ret = fread(supp_features, 1, sizeof(supp_features), fp); + ret = fread(supp_features, 1, sizeof(supp_features) - 1, fp); + supp_features[ret] = '\0'; fclose(fp); } if (ret > 0 && strstr(supp_features, @@ -376,8 +443,8 @@ static void append_unique(char *buf, char *prefix, char *key, char *val, } } -static void enable_default_ext4_features(struct mkfs_opts *mop, char *anchor, - size_t maxbuflen, int user_spec) +static int enable_default_ext4_features(struct mkfs_opts *mop, char *anchor, + size_t maxbuflen, int user_spec) { if (IS_OST(&mop->mo_ldd)) { append_unique(anchor, user_spec ? "," : " -O ", @@ -408,7 +475,17 @@ static void enable_default_ext4_features(struct mkfs_opts *mop, char *anchor, /* The following options are only valid for ext4-based ldiskfs. * If --backfstype=ext3 is specified, do not enable them. */ if (mop->mo_ldd.ldd_mount_type == LDD_MT_EXT3) - return; + return 0; + + /* Enable quota by default */ + if (is_e2fsprogs_feature_supp("-O quota") == 0) { + append_unique(anchor, ",", "quota", NULL, maxbuflen); + } else { + fatal(); + fprintf(stderr, "\"-O quota\" must be supported by " + "e2fsprogs, please upgrade your e2fsprogs.\n"); + return EINVAL; + } /* Allow files larger than 2TB. Also needs LU-16, but not harmful. */ if (is_e2fsprogs_feature_supp("-O huge_file") == 0) @@ -434,6 +511,7 @@ static void enable_default_ext4_features(struct mkfs_opts *mop, char *anchor, } } /* Don't add any more "-O" options here, see last comment above */ + return 0; } /** @@ -629,13 +707,15 @@ int ldiskfs_make_lustre(struct mkfs_opts *mop) start = moveopts_to_end(start); maxbuflen = sizeof(mop->mo_mkfsopts) - (start - mop->mo_mkfsopts) - strlen(start); - enable_default_ext4_features(mop, start, maxbuflen, 1); + ret = enable_default_ext4_features(mop, start, maxbuflen, 1); } else { start = mop->mo_mkfsopts + strlen(mop->mo_mkfsopts), maxbuflen = sizeof(mop->mo_mkfsopts) - strlen(mop->mo_mkfsopts); - enable_default_ext4_features(mop, start, maxbuflen, 0); + ret = enable_default_ext4_features(mop, start, maxbuflen, 0); } + if (ret) + return ret; /* end handle -O mkfs options */ /* start handle -E mkfs options */ @@ -745,6 +825,293 @@ int ldiskfs_prepare_lustre(struct mkfs_opts *mop, return 0; } +int read_file(const char *path, char *buf, int size) +{ + FILE *fd; + + fd = fopen(path, "r"); + if (fd == NULL) + return errno; + + /* should not ignore fgets(3)'s return value */ + if (!fgets(buf, size, fd)) { + fprintf(stderr, "reading from %s: %s", path, strerror(errno)); + fclose(fd); + return 1; + } + fclose(fd); + return 0; +} + +int write_file(const char *path, const char *buf) +{ + FILE *fd; + + fd = fopen(path, "w"); + if (fd == NULL) + return errno; + + fputs(buf, fd); + fclose(fd); + return 0; +} + +int set_blockdev_scheduler(const char *path, const char *scheduler) +{ + char buf[PATH_MAX], *c; + int rc; + + /* Before setting the scheduler, we need to check to see if it's + * already set to "noop". If it is, we don't want to override + * that setting. If it's set to anything other than "noop", set + * the scheduler to what has been passed in. */ + + rc = read_file(path, buf, sizeof(buf)); + if (rc) { + if (verbose) + fprintf(stderr, "%s: cannot open '%s': %s\n", + progname, path, strerror(errno)); + return rc; + } + + /* The expected format of buf: noop anticipatory deadline [cfq] */ + c = strchr(buf, '['); + + /* If c is NULL, the format is not what we expect. Play it safe + * and error out. */ + if (c == NULL) { + if (verbose) + fprintf(stderr, "%s: cannot parse scheduler " + "options for '%s'\n", progname, path); + return -EINVAL; + } + + if (strncmp(c+1, "noop", 4) == 0) + return 0; + + rc = write_file(path, scheduler); + if (rc) { + if (verbose) + fprintf(stderr, "%s: cannot set scheduler on " + "'%s': %s\n", progname, path, + strerror(errno)); + return rc; + } + + return rc; +} + +/* This is to tune the kernel for good SCSI performance. + * For that we set the value of /sys/block/{dev}/queue/max_sectors_kb + * to the value of /sys/block/{dev}/queue/max_hw_sectors_kb */ +int set_blockdev_tunables(char *source, struct mount_opts *mop) +{ + glob_t glob_info = { 0 }; + struct stat stat_buf; + char *chk_major, *chk_minor; + char *savept = NULL, *dev; + char *ret_path; + char buf[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'}; + char real_path[PATH_MAX] = {'\0'}; + int i, rc = 0; + int major, minor; + char *slave = NULL; + + if (!source) + return -EINVAL; + + ret_path = realpath(source, real_path); + if (ret_path == NULL) { + if (verbose) + fprintf(stderr, "warning: %s: cannot resolve: %s\n", + source, strerror(errno)); + return -EINVAL; + } + + if (strncmp(real_path, "/dev/loop", 9) == 0) + return 0; + + if ((real_path[0] != '/') && (strpbrk(real_path, ",:") != NULL)) + return 0; + + snprintf(path, sizeof(path), "/sys/block%s", real_path + 4); + if (access(path, X_OK) == 0) + goto set_params; + + /* The name of the device say 'X' specified in /dev/X may not + * match any entry under /sys/block/. In that case we need to + * match the major/minor number to find the entry under + * sys/block corresponding to /dev/X */ + + /* Don't chop tail digit on /dev/mapper/xxx, LU-478 */ + if (strncmp(real_path, "/dev/mapper", 11) != 0) { + dev = real_path + strlen(real_path); + while (--dev > real_path && isdigit(*dev)) + *dev = 0; + + if (strncmp(real_path, "/dev/md_", 8) == 0) + *dev = 0; + } + + rc = stat(real_path, &stat_buf); + if (rc) { + if (verbose) + fprintf(stderr, "warning: %s, device %s stat failed\n", + strerror(errno), real_path); + return rc; + } + + major = major(stat_buf.st_rdev); + minor = minor(stat_buf.st_rdev); + rc = glob("/sys/block/*", GLOB_NOSORT, NULL, &glob_info); + if (rc) { + if (verbose) + fprintf(stderr, "warning: failed to read entries under " + "/sys/block\n"); + globfree(&glob_info); + return rc; + } + + for (i = 0; i < glob_info.gl_pathc; i++){ + snprintf(path, sizeof(path), "%s/dev", glob_info.gl_pathv[i]); + + rc = read_file(path, buf, sizeof(buf)); + if (rc) + continue; + + if (buf[strlen(buf) - 1] == '\n') + buf[strlen(buf) - 1] = '\0'; + + chk_major = strtok_r(buf, ":", &savept); + chk_minor = savept; + if (chk_major != NULL && major == atoi(chk_major) && + chk_minor != NULL && minor == atoi(chk_minor)) + break; + } + + if (i == glob_info.gl_pathc) { + if (verbose) + fprintf(stderr,"warning: device %s does not match any " + "entry under /sys/block\n", real_path); + globfree(&glob_info); + return -EINVAL; + } + + /* Chop off "/dev" from path we found */ + path[strlen(glob_info.gl_pathv[i])] = '\0'; + globfree(&glob_info); + +set_params: + if (strncmp(real_path, "/dev/md", 7) == 0) { + snprintf(real_path, sizeof(real_path), "%s/%s", path, + STRIPE_CACHE_SIZE); + + rc = read_file(real_path, buf, sizeof(buf)); + if (rc) { + if (verbose) + fprintf(stderr, "warning: opening %s: %s\n", + real_path, strerror(errno)); + return 0; + } + + if (atoi(buf) >= mop->mo_md_stripe_cache_size) + return 0; + + if (strlen(buf) - 1 > 0) { + snprintf(buf, sizeof(buf), "%d", + mop->mo_md_stripe_cache_size); + rc = write_file(real_path, buf); + if (rc && verbose) + fprintf(stderr, "warning: opening %s: %s\n", + real_path, strerror(errno)); + } + /* Return since raid and disk tunables are different */ + return rc; + } + + snprintf(real_path, sizeof(real_path), "%s/%s", path, + MAX_HW_SECTORS_KB_PATH); + rc = read_file(real_path, buf, sizeof(buf)); + if (rc) { + if (verbose) + fprintf(stderr, "warning: opening %s: %s\n", + real_path, strerror(errno)); + /* No MAX_HW_SECTORS_KB_PATH isn't necessary an + * error for some device. */ + rc = 0; + } + + if (strlen(buf) - 1 > 0) { + snprintf(real_path, sizeof(real_path), "%s/%s", path, + MAX_SECTORS_KB_PATH); + rc = write_file(real_path, buf); + if (rc) { + if (verbose) + fprintf(stderr, "warning: writing to %s: %s\n", + real_path, strerror(errno)); + /* No MAX_SECTORS_KB_PATH isn't necessary an + * error for some device. */ + rc = 0; + } + } + + /* Purposely ignore errors reported from set_blockdev_scheduler. + * The worst that will happen is a block device with an "incorrect" + * scheduler. */ + snprintf(real_path, sizeof(real_path), "%s/%s", path, SCHEDULER_PATH); + set_blockdev_scheduler(real_path, DEFAULT_SCHEDULER); + + /* if device is multipath device, tune its slave devices */ + glob_info.gl_pathc = 0; + glob_info.gl_offs = 0; + snprintf(real_path, sizeof(real_path), "%s/slaves/*", path); + rc = glob(real_path, GLOB_NOSORT, NULL, &glob_info); + + for (i = 0; rc == 0 && i < glob_info.gl_pathc; i++) { + slave = basename(glob_info.gl_pathv[i]); + snprintf(real_path, sizeof(real_path), "/dev/%s", slave); + rc = set_blockdev_tunables(real_path, mop); + } + + if (rc == GLOB_NOMATCH) { + /* no slave device is not an error */ + rc = 0; + } else if (rc && verbose) { + if (slave == NULL) { + fprintf(stderr, "warning: %s, failed to read" + " entries under %s/slaves\n", + strerror(errno), path); + } else { + fprintf(stderr, "unable to set tunables for" + " slave device %s (slave would be" + " unable to handle IO request from" + " master %s)\n", + real_path, source); + } + } + globfree(&glob_info); + + return rc; +} + +int ldiskfs_tune_lustre(char *dev, struct mount_opts *mop) +{ + return set_blockdev_tunables(dev, mop); +} + +int ldiskfs_label_lustre(struct mount_opts *mop) +{ + char label_cmd[PATH_MAX]; + int rc; + + snprintf(label_cmd, sizeof(label_cmd), + TUNE2FS" -f -L '%s' '%s' >/dev/null 2>&1", + mop->mo_ldd.ldd_svname, mop->mo_source); + rc = run_command(label_cmd, sizeof(label_cmd)); + + return rc; +} + /* return canonicalized absolute pathname, even if the target file does not * exist, unlike realpath */ static char *absolute_path(char *devname) @@ -758,12 +1125,22 @@ static char *absolute_path(char *devname) return NULL; if (devname[0] != '/') { - if (getcwd(buf, sizeof(buf) - 1) == NULL) + if (getcwd(buf, sizeof(buf) - 1) == NULL) { + free(path); return NULL; + } strcat(buf, "/"); - strcat(buf, devname); + if (strlen(devname) > sizeof(buf)-strlen(buf)-1) { + free(path); + return NULL; + } + strncat(buf, devname, sizeof(buf)-strlen(buf)-1); } else { - strcpy(buf, devname); + if (strlen(devname) > sizeof(buf)-1) { + free(path); + return NULL; + } + strncpy(buf, devname, sizeof(buf)); } /* truncate filename before calling realpath */ ptr = strrchr(buf, '/'); @@ -820,3 +1197,75 @@ out: return ret; } +static int is_feature_enabled(const char *feature, const char *devpath) +{ + char cmd[PATH_MAX]; + FILE *fp; + char enabled_features[4096] = ""; + int ret = 1; + + snprintf(cmd, sizeof(cmd), "%s -R features %s 2>&1", + DEBUGFS, devpath); + + /* Using popen() instead of run_command() since debugfs does + * not return proper error code if command is not supported */ + fp = popen(cmd, "r"); + if (!fp) { + fprintf(stderr, "%s: %s\n", progname, strerror(errno)); + return 0; + } + + ret = fread(enabled_features, 1, sizeof(enabled_features) - 1, fp); + enabled_features[ret] = '\0'; + fclose(fp); + + if (strstr(enabled_features, feature)) + return 1; + return 0; +} + +/* Enable quota accounting */ +int ldiskfs_enable_quota(struct mkfs_opts *mop) +{ + char *dev; + char cmd[512]; + int cmdsz = sizeof(cmd), ret; + + if (is_e2fsprogs_feature_supp("-O quota") != 0) { + fprintf(stderr, "%s: \"-O quota\" is is not supported by " + "current e2fsprogs\n", progname); + return EINVAL; + } + + dev = mop->mo_device; + if (mop->mo_flags & MO_IS_LOOP) + dev = mop->mo_loopdev; + + /* Quota feature is already enabled? */ + if (is_feature_enabled("quota", dev)) { + vprint("Quota feature is already enabled.\n"); + return 0; + } + + /* Turn on quota feature by "tune2fs -O quota" */ + snprintf(cmd, cmdsz, "%s -O quota %s", TUNE2FS, dev); + ret = run_command(cmd, cmdsz); + if (ret) + fprintf(stderr, "command:%s (%d)", cmd, ret); + + return ret; +} + +int ldiskfs_init(void) +{ + /* Required because full path to DEBUGFS is not specified */ + setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin", 0); + + return 0; +} + +void ldiskfs_fini(void) +{ + return; +} +