| /* |
| * blk_namespaces: tests functionality of multiple block namespaces |
| * |
| * Copyright (c) 2015, Intel Corporation. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms and conditions of the GNU Lesser General Public License, |
| * version 2.1, as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
| * more details. |
| */ |
| #include <stdio.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <limits.h> |
| #include <syslog.h> |
| #include <libkmod.h> |
| #include <uuid/uuid.h> |
| #include <linux/version.h> |
| #include <test.h> |
| |
| #include <ndctl/libndctl.h> |
| |
| static const char *PROVIDER = "nfit_test.0"; |
| |
| static struct ndctl_bus *get_bus_by_provider(struct ndctl_ctx *ctx, |
| const char *provider) |
| { |
| struct ndctl_bus *bus; |
| |
| ndctl_bus_foreach(ctx, bus) |
| if (strcmp(provider, ndctl_bus_get_provider(bus)) == 0) |
| return bus; |
| |
| return NULL; |
| } |
| |
| static struct ndctl_btt *get_idle_btt(struct ndctl_region *region) |
| { |
| struct ndctl_btt *btt; |
| |
| ndctl_btt_foreach(region, btt) |
| if (!ndctl_btt_is_enabled(btt) |
| && !ndctl_btt_is_configured(btt)) |
| return btt; |
| return NULL; |
| } |
| |
| static struct ndctl_namespace *create_blk_namespace(int region_fraction, |
| struct ndctl_region *region, unsigned long long req_size, |
| uuid_t uuid) |
| { |
| struct ndctl_namespace *ndns, *seed_ns = NULL; |
| unsigned long long size; |
| |
| ndctl_namespace_foreach(region, ndns) |
| if (ndctl_namespace_get_size(ndns) == 0) { |
| seed_ns = ndns; |
| break; |
| } |
| |
| if (!seed_ns) |
| return NULL; |
| |
| size = ndctl_region_get_size(region)/region_fraction; |
| if (req_size) |
| size = req_size; |
| |
| if (ndctl_namespace_set_uuid(seed_ns, uuid) < 0) |
| return NULL; |
| |
| if (ndctl_namespace_set_size(seed_ns, size) < 0) |
| return NULL; |
| |
| if (ndctl_namespace_set_sector_size(seed_ns, 512) < 0) |
| return NULL; |
| |
| if (ndctl_namespace_enable(seed_ns) < 0) |
| return NULL; |
| |
| return seed_ns; |
| } |
| |
| static int disable_blk_namespace(struct ndctl_namespace *ndns) |
| { |
| if (ndctl_namespace_disable_invalidate(ndns) < 0) |
| return -ENODEV; |
| |
| if (ndctl_namespace_delete(ndns) < 0) |
| return -ENODEV; |
| |
| return 0; |
| } |
| |
| static struct ndctl_btt *check_valid_btt(struct ndctl_region *region, |
| struct ndctl_namespace *ndns, uuid_t btt_uuid) |
| { |
| struct ndctl_btt *btt = NULL; |
| ndctl_btt_foreach(region, btt) { |
| struct ndctl_namespace *btt_ndns; |
| uuid_t uu; |
| |
| ndctl_btt_get_uuid(btt, uu); |
| if (uuid_compare(uu, btt_uuid) != 0) |
| continue; |
| if (!ndctl_btt_is_enabled(btt)) |
| continue; |
| btt_ndns = ndctl_btt_get_namespace(btt); |
| if (strcmp(ndctl_namespace_get_devname(btt_ndns), |
| ndctl_namespace_get_devname(ndns)) != 0) |
| continue; |
| return btt; |
| } |
| return NULL; |
| } |
| |
| static int do_test(struct ndctl_ctx *ctx) |
| { |
| int rc; |
| struct ndctl_bus *bus; |
| struct ndctl_btt *btt, *found = NULL, *_btt; |
| struct ndctl_region *region, *blk_region = NULL; |
| struct ndctl_namespace *ndns, *_ndns; |
| unsigned long long ns_size = 18874368; |
| uuid_t uuid = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}; |
| uuid_t btt_uuid; |
| |
| bus = get_bus_by_provider(ctx, PROVIDER); |
| if (!bus) { |
| fprintf(stderr, "failed to find NFIT-provider: %s\n", PROVIDER); |
| return -ENODEV; |
| } |
| |
| ndctl_region_foreach(bus, region) |
| if (strcmp(ndctl_region_get_type_name(region), "blk") == 0) { |
| blk_region = region; |
| break; |
| } |
| |
| if (!blk_region) { |
| fprintf(stderr, "failed to find block region\n"); |
| return -ENODEV; |
| } |
| |
| /* create a blk namespace */ |
| ndns = create_blk_namespace(1, blk_region, ns_size, uuid); |
| if (!ndns) { |
| fprintf(stderr, "failed to create block namespace\n"); |
| return -ENXIO; |
| } |
| |
| /* create a btt for this namespace */ |
| uuid_generate(btt_uuid); |
| btt = get_idle_btt(region); |
| if (!btt) |
| return -ENXIO; |
| |
| ndctl_namespace_disable_invalidate(ndns); |
| ndctl_btt_set_uuid(btt, btt_uuid); |
| ndctl_btt_set_sector_size(btt, 512); |
| ndctl_btt_set_namespace(btt, ndns); |
| rc = ndctl_btt_enable(btt); |
| if (rc) { |
| fprintf(stderr, "failed to create btt 0\n"); |
| return rc; |
| } |
| |
| /* re-create the namespace - this should auto-enable the btt */ |
| disable_blk_namespace(ndns); |
| ndns = create_blk_namespace(1, blk_region, ns_size, uuid); |
| if (!ndns) { |
| fprintf(stderr, "failed to re-create block namespace\n"); |
| return -ENXIO; |
| } |
| |
| /* Verify btt was auto-created */ |
| found = check_valid_btt(blk_region, ndns, btt_uuid); |
| if (!found) |
| return -ENXIO; |
| btt = found; |
| |
| /*disable the btt and namespace again */ |
| ndctl_btt_delete(btt); |
| disable_blk_namespace(ndns); |
| |
| /* recreate the namespace with a different uuid */ |
| uuid_generate(uuid); |
| ndns = create_blk_namespace(1, blk_region, ns_size, uuid); |
| if (!ndns) { |
| fprintf(stderr, "failed to re-create block namespace\n"); |
| return -ENXIO; |
| } |
| |
| /* make sure there is no btt on this namespace */ |
| found = check_valid_btt(blk_region, ndns, btt_uuid); |
| if (found) { |
| fprintf(stderr, "found a stale btt\n"); |
| return -ENXIO; |
| } |
| |
| ndctl_btt_foreach_safe(blk_region, btt, _btt) |
| ndctl_btt_delete(btt); |
| |
| ndctl_namespace_foreach_safe(blk_region, ndns, _ndns) |
| if (ndctl_namespace_get_size(ndns) != 0) |
| disable_blk_namespace(ndns); |
| |
| ndctl_region_foreach(bus, region) |
| ndctl_region_disable_invalidate(region); |
| |
| return 0; |
| } |
| |
| int test_parent_uuid(int loglevel, struct ndctl_test *test, struct ndctl_ctx *ctx) |
| { |
| struct kmod_module *mod; |
| struct kmod_ctx *kmod_ctx; |
| int err, result = EXIT_FAILURE; |
| |
| if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 3, 0))) |
| return 77; |
| |
| ndctl_set_log_priority(ctx, loglevel); |
| err = nfit_test_init(&kmod_ctx, &mod, NULL, loglevel, test); |
| if (err < 0) { |
| ndctl_test_skip(test); |
| fprintf(stderr, "nfit_test unavailable skipping tests\n"); |
| return 77; |
| } |
| |
| err = do_test(ctx); |
| if (err == 0) |
| result = EXIT_SUCCESS; |
| kmod_module_remove_module(mod, 0); |
| kmod_unref(kmod_ctx); |
| return result; |
| } |
| |
| int __attribute__((weak)) main(int argc, char *argv[]) |
| { |
| struct ndctl_test *test = ndctl_test_new(0); |
| struct ndctl_ctx *ctx; |
| int rc; |
| |
| if (!test) { |
| fprintf(stderr, "failed to initialize test\n"); |
| return EXIT_FAILURE; |
| } |
| |
| rc = ndctl_new(&ctx); |
| if (rc) |
| return ndctl_test_result(test, rc); |
| |
| rc = test_parent_uuid(LOG_DEBUG, test, ctx); |
| ndctl_unref(ctx); |
| return ndctl_test_result(test, rc); |
| } |