X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lustre%2Futils%2Fmount_utils_ldiskfs.c;h=bf8e2c86008c2596509493ea46b0b52b2879659e;hb=848f9e20320cb7c01eaf7f1b5c27f5efd54e4818;hp=fe8be905acd5ca06c2f1d8b0e097ddf96276333b;hpb=fd4cee4cc545679d2a00c2a20939564e36700631;p=fs%2Flustre-release.git diff --git a/lustre/utils/mount_utils_ldiskfs.c b/lustre/utils/mount_utils_ldiskfs.c index fe8be90..bf8e2c8 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,13 @@ #include #include #include -#include "mount_utils.h" + +#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; @@ -87,7 +95,7 @@ extern char *progname; #define EXT3_DIRENT_SIZE DUMMY_FILE_NAME_LEN /* Write the server config files */ -int write_local_files(struct mkfs_opts *mop) +int ldiskfs_write_ldd(struct mkfs_opts *mop) { char mntpt[] = "/tmp/mntXXXXXX"; char filepnm[128]; @@ -145,6 +153,7 @@ int write_local_files(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 +165,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 +222,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 +290,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 +349,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 +408,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 +440,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 +476,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 +672,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 +790,294 @@ 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, int fan_out) +{ + 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; + + 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 (major == atoi(chk_major) &&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 (fan_out) { + char *slave = NULL; + glob_info.gl_pathc = 0; + glob_info.gl_offs = 0; + /* if device is multipath device, tune its slave devices */ + 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, 0); + } + + 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, 1); +} + +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,8 +1091,10 @@ 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); } else { @@ -820,3 +1155,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; +} +