Viewing: llapi_pool_test.c
// SPDX-License-Identifier: GPL-2.0
/*
* Tests for the liblustreapi pool pinning helpers.
*
* These tests exercise the llapi_pool_* APIs, which manipulate the
* "lustre.pin" extended attribute to record per-pool pinning state,
* and verify that they behave correctly and coexist with PCC pins.
*/
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/xattr.h>
#include <unistd.h>
#include <lustre/lustreapi.h>
#include "llapi_test_utils.h"
#ifndef ENOATTR
#define ENOATTR ENODATA
#endif
static char lustre_dir[PATH_MAX - 5];
static void usage(char *prog)
{
printf("Usage: %s -d LUSTRE_DIR [-s SKIP[,SKIP...]] [-t ONLY[,ONLY...]]\n",
prog);
exit(0);
}
static void build_path(char *buf, size_t bufsize, const char *name)
{
int rc;
rc = snprintf(buf, bufsize, "%s/%s", lustre_dir, name);
ASSERTF(rc > 0 && (size_t)rc < bufsize,
"invalid path for '%s'", name);
}
static void create_empty_file(const char *name, char *path, size_t path_sz)
{
int rc;
int fd;
build_path(path, path_sz, name);
rc = unlink(path);
ASSERTF(rc == 0 || errno == ENOENT,
"unlink(%s) failed: %s", path, strerror(errno));
fd = creat(path, 0600);
ASSERTF(fd >= 0, "creat(%s) failed: %s", path, strerror(errno));
close(fd);
rc = lremovexattr(path, XATTR_LUSTRE_PIN);
if (rc < 0 && errno != ENODATA && errno != ENOATTR)
ASSERTF(0, "lremovexattr(%s) failed: %s", path,
strerror(errno));
}
static ssize_t get_pin_xattr(const char *path, char *buf, size_t bufsize)
{
ssize_t len;
errno = 0;
len = lgetxattr(path, XATTR_LUSTRE_PIN, buf, bufsize - 1);
if (len < 0)
return -1;
buf[len] = '\0';
return len;
}
#define T0_DESC "llapi_pool_pin_file/llapi_pool_unpin_file basic behavior"
static void test0(void)
{
const char *pool_name = "testpool0";
char path[PATH_MAX];
char val[256];
char expect[32];
ssize_t len;
int pinned;
int rc;
create_empty_file("pool_t0", path, sizeof(path));
pinned = llapi_pool_is_pinned_file(path, pool_name);
ASSERTF(pinned == 0,
"llapi_pool_is_pinned_file expected 0, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
rc = llapi_pool_pin_file(path, pool_name);
ASSERTF(rc == 0, "llapi_pool_pin_file failed: %s", strerror(errno));
pinned = llapi_pool_is_pinned_file(path, pool_name);
ASSERTF(pinned == 1,
"llapi_pool_is_pinned_file expected 1, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
len = get_pin_xattr(path, val, sizeof(val));
ASSERTF(len > 0, "lustre.pin xattr missing after pin: %s",
strerror(errno));
snprintf(expect, sizeof(expect), "%s: %s", "pool", pool_name);
ASSERTF(strstr(val, expect) != NULL,
"lustre.pin xattr '%s' does not contain '%s'", val, expect);
rc = llapi_pool_unpin_file(path, pool_name);
ASSERTF(rc == 0, "llapi_pool_unpin_file failed: %s", strerror(errno));
pinned = llapi_pool_is_pinned_file(path, pool_name);
ASSERTF(pinned == 0,
"llapi_pool_is_pinned_file expected 0, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
errno = 0;
len = lgetxattr(path, XATTR_LUSTRE_PIN, val, sizeof(val));
ASSERTF(len < 0 && (errno == ENODATA || errno == ENOATTR),
"lustre.pin xattr should be removed, len=%zd, errno=%d",
len, errno);
}
#define T1_DESC "llapi_pool_pin_file already pinned"
static void test1(void)
{
const char *pool_name = "testpool1";
char path[PATH_MAX];
char v1[256];
char v2[256];
ssize_t len1;
ssize_t len2;
int rc;
create_empty_file("pool_t1", path, sizeof(path));
rc = llapi_pool_pin_file(path, pool_name);
ASSERTF(rc == 0, "llapi_pool_pin_file failed: %s", strerror(errno));
len1 = get_pin_xattr(path, v1, sizeof(v1));
ASSERTF(len1 > 0, "lustre.pin xattr missing after first pin: %s",
strerror(errno));
rc = llapi_pool_pin_file(path, pool_name);
ASSERTF(rc == 0, "second llapi_pool_pin_file failed: %s",
strerror(errno));
len2 = get_pin_xattr(path, v2, sizeof(v2));
ASSERTF(len2 == len1 && memcmp(v1, v2, len1) == 0,
"lustre.pin xattr changed on re-pin: '%s' vs '%s'", v1, v2);
}
#define T2_DESC "pool pins coexist with PCC hsm pins"
static void test2(void)
{
const __u32 hsm_id = 2;
const char *pool_name = "testpool2";
char path[PATH_MAX];
char val[256];
char expect_hsm[64];
char expect_pool[64];
ssize_t len;
int rc;
create_empty_file("pool_t2", path, sizeof(path));
rc = llapi_pcc_pin_file(path, hsm_id);
ASSERTF(rc == 0, "llapi_pcc_pin_file failed: %s", strerror(-rc));
rc = llapi_pool_pin_file(path, pool_name);
ASSERTF(rc == 0, "llapi_pool_pin_file failed: %s", strerror(errno));
len = get_pin_xattr(path, val, sizeof(val));
ASSERTF(len > 0, "lustre.pin xattr missing after pin: %s",
strerror(errno));
snprintf(expect_hsm, sizeof(expect_hsm), "%s: %u", "hsm", hsm_id);
snprintf(expect_pool, sizeof(expect_pool), "%s: %s", "pool", pool_name);
ASSERTF(strstr(val, expect_hsm) != NULL,
"lustre.pin xattr '%s' missing '%s'", val, expect_hsm);
ASSERTF(strstr(val, expect_pool) != NULL,
"lustre.pin xattr '%s' missing '%s'", val, expect_pool);
rc = llapi_pool_unpin_file(path, pool_name);
ASSERTF(rc == 0, "llapi_pool_unpin_file failed: %s", strerror(errno));
len = get_pin_xattr(path, val, sizeof(val));
ASSERTF(len > 0, "lustre.pin xattr lost hsm entry after pool unpin: %s",
strerror(errno));
ASSERTF(strstr(val, expect_hsm) != NULL,
"hsm entry missing after pool unpin: '%s'", val);
ASSERTF(strstr(val, expect_pool) == NULL,
"pool entry still present after unpin: '%s'", val);
rc = llapi_pcc_unpin_file(path, hsm_id);
ASSERTF(rc == 0, "llapi_pcc_unpin_file failed: %s", strerror(-rc));
errno = 0;
len = lgetxattr(path, XATTR_LUSTRE_PIN, val, sizeof(val));
ASSERTF(len < 0 && (errno == ENODATA || errno == ENOATTR),
"lustre.pin xattr should be removed after final unpin, len=%zd, errno=%d",
len, errno);
}
#define T3_DESC "llapi_pool_pin_fd, llapi_pool_unpin_fd and llapi_pool_is_pinned_fd"
static void test3(void)
{
const char *pool_name = "testpool3";
char path[PATH_MAX];
int fd;
int rc;
int pinned;
create_empty_file("pool_t3", path, sizeof(path));
fd = open(path, O_RDONLY | O_NONBLOCK);
ASSERTF(fd >= 0, "open(%s) failed: %s", path, strerror(errno));
pinned = llapi_pool_is_pinned_fd(fd, pool_name);
ASSERTF(pinned == 0,
"llapi_pool_is_pinned_fd expected 0 before pin_fd, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
rc = llapi_pool_pin_fd(fd, pool_name);
ASSERTF(rc == 0, "llapi_pool_pin_fd failed: %s", strerror(errno));
pinned = llapi_pool_is_pinned_fd(fd, pool_name);
ASSERTF(pinned == 1,
"llapi_pool_is_pinned_fd expected 1 after pin_fd, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
pinned = llapi_pool_is_pinned_file(path, pool_name);
ASSERTF(pinned == 1,
"llapi_pool_is_pinned_file expected 1 after pin_fd, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
rc = llapi_pool_unpin_fd(fd, pool_name);
ASSERTF(rc == 0, "llapi_pool_unpin_fd failed: %s", strerror(errno));
pinned = llapi_pool_is_pinned_fd(fd, pool_name);
ASSERTF(pinned == 0,
"llapi_pool_is_pinned_fd expected 0 after unpin_fd, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
pinned = llapi_pool_is_pinned_file(path, pool_name);
ASSERTF(pinned == 0,
"llapi_pool_is_pinned_file expected 0 after unpin_fd, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
close(fd);
}
#define T4_DESC "llapi_pool_pin_fid, llapi_pool_unpin_fid and llapi_pool_is_pinned_fid"
static void test4(void)
{
const char *pool_name = "testpool4";
char path[PATH_MAX];
struct lu_fid fid;
int pinned;
int rc;
create_empty_file("pool_t4", path, sizeof(path));
rc = llapi_path2fid(path, &fid);
ASSERTF(rc == 0, "llapi_path2fid(%s) failed: %s",
path, strerror(-rc));
pinned = llapi_pool_is_pinned_fid(lustre_dir, &fid, pool_name);
ASSERTF(pinned == 0,
"llapi_pool_is_pinned_fid expected 0 before pin_fid, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
rc = llapi_pool_pin_fid(lustre_dir, &fid, pool_name);
ASSERTF(rc == 0, "llapi_pool_pin_fid failed: %s", strerror(errno));
pinned = llapi_pool_is_pinned_fid(lustre_dir, &fid, pool_name);
ASSERTF(pinned == 1,
"llapi_pool_is_pinned_fid expected 1 after pin_fid, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
pinned = llapi_pool_is_pinned_file(path, pool_name);
ASSERTF(pinned == 1,
"llapi_pool_is_pinned_file expected 1 after pin_fid, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
rc = llapi_pool_unpin_fid(lustre_dir, &fid, pool_name);
ASSERTF(rc == 0, "llapi_pool_unpin_fid failed: %s", strerror(errno));
pinned = llapi_pool_is_pinned_fid(lustre_dir, &fid, pool_name);
ASSERTF(pinned == 0,
"llapi_pool_is_pinned_fid expected 0 after unpin_fid, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
pinned = llapi_pool_is_pinned_file(path, pool_name);
ASSERTF(pinned == 0,
"llapi_pool_is_pinned_file expected 0 after unpin_fid, got %d (%s)",
pinned, pinned < 0 ? strerror(-pinned) : "no error");
}
static struct test_tbl_entry test_tbl[] = {
TEST_REGISTER(0),
TEST_REGISTER(1),
TEST_REGISTER(2),
TEST_REGISTER(3),
TEST_REGISTER(4),
TEST_REGISTER_END
};
static void process_args(int argc, char *argv[])
{
int c;
while ((c = getopt(argc, argv, "d:s:t:")) != -1) {
switch (c) {
case 'd':
if (snprintf(lustre_dir, sizeof(lustre_dir), "%s",
optarg) >= sizeof(lustre_dir))
DIE("Error: test directory name too long\n");
break;
case 's':
set_tests_to_skip(optarg, test_tbl);
break;
case 't':
set_tests_to_run(optarg, test_tbl);
break;
case '?':
default:
fprintf(stderr, "Unknown option '%c'\n", optopt);
usage(argv[0]);
}
}
}
int main(int argc, char *argv[])
{
process_args(argc, argv);
return run_tests(lustre_dir, test_tbl);
}