Viewing: lockahead_test.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2016 Cray Inc. All rights reserved.
* Authors: Patrick Farrell, Frank Zago
*
* A few portions are extracted from llapi_layout_test.c
*
* The purpose of this test is to exercise the lockahead advice of ladvise.
*
* The program will exit as soon as a test fails.
*/
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <poll.h>
#include <time.h>
#include <lustre/lustreapi.h>
#include <linux/lustre/lustre_idl.h>
#define ERROR(fmt, ...) \
fprintf(stderr, "%s: %s:%d: %s: " fmt "\n", \
program_invocation_short_name, __FILE__, __LINE__, \
__func__, ## __VA_ARGS__);
#define DIE(fmt, ...) \
do { \
ERROR(fmt, ## __VA_ARGS__); \
exit(-1); \
} while (0)
#define ASSERTF(cond, fmt, ...) \
do { \
if (!(cond)) \
DIE("assertion '%s' failed: "fmt, \
#cond, ## __VA_ARGS__); \
} while (0)
#define PERFORM(testfn) \
do { \
cleanup(); \
fprintf(stderr, "Starting test " #testfn " at %lld\n", \
(unsigned long long)time(NULL)); \
rc = testfn(); \
fprintf(stderr, "Finishing test " #testfn " at %lld\n", \
(unsigned long long)time(NULL)); \
} while (0)
/* Name of file/directory. Will be set once and will not change. */
static char mainpath[PATH_MAX];
/* Path to same file/directory on second mount */
static char mainpath2[PATH_MAX];
static char *mainfile;
static char fsmountdir[PATH_MAX]; /* Lustre mountpoint */
static char *lustre_dir; /* Test directory inside Lustre */
static char *lustre_dir2; /* Same dir but on second mountpoint */
static int single_test; /* Number of a single test to execute*/
/* Cleanup our test file. */
static void cleanup(void)
{
unlink(mainpath);
}
/* Trivial helper for one advice */
static void setup_ladvise_lockahead(struct llapi_lu_ladvise *advice, int mode,
int flags, size_t start, size_t end, bool async)
{
advice->lla_advice = LU_LADVISE_LOCKAHEAD;
advice->lla_lockahead_mode = mode;
if (async)
advice->lla_peradvice_flags = flags | LF_ASYNC;
else
advice->lla_peradvice_flags = flags;
advice->lla_start = start;
advice->lla_end = end;
advice->lla_value3 = 0;
advice->lla_value4 = 0;
}
/* Test valid single lock ahead request */
static int test10(void)
{
struct llapi_lu_ladvise advice;
const int count = 1;
int fd;
size_t write_size = 1024 * 1024;
int rc;
char buf[write_size];
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
setup_ladvise_lockahead(&advice, MODE_WRITE_USER, 0, 0,
write_size - 1, true);
/* Manually set the result so we can verify it's being modified */
advice.lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0,
"cannot lockahead '%s': %s", mainpath, strerror(errno));
ASSERTF(advice.lla_lockahead_result == 0,
"unexpected extent result: %d",
advice.lla_lockahead_result);
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
close(fd);
return 0;
}
/* Get lock, wait until lock is taken */
static int test11(void)
{
struct llapi_lu_ladvise advice;
const int count = 1;
int fd;
size_t write_size = 1024 * 1024;
int rc;
char buf[write_size];
int i;
int enqueue_requests = 0;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
setup_ladvise_lockahead(&advice, MODE_WRITE_USER, 0, 0,
write_size - 1, true);
/* Manually set the result so we can verify it's being modified */
advice.lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0,
"cannot lockahead '%s': %s", mainpath, strerror(errno));
ASSERTF(advice.lla_lockahead_result == 0,
"unexpected extent result: %d",
advice.lla_lockahead_result);
enqueue_requests++;
/* Ask again until we get the lock (status 1). */
for (i = 1; i < 100; i++) {
usleep(100000); /* 0.1 second */
advice.lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
if (advice.lla_lockahead_result > 0)
break;
enqueue_requests++;
}
ASSERTF(advice.lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice.lla_lockahead_result);
/* Again. This time it is always there. */
for (i = 0; i < 100; i++) {
advice.lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
ASSERTF(advice.lla_lockahead_result > 0,
"unexpected extent result: %d",
advice.lla_lockahead_result);
}
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
close(fd);
return enqueue_requests;
}
/* Test with several times the same extent */
static int test12(void)
{
struct llapi_lu_ladvise *advice;
const int count = 10;
int fd;
size_t write_size = 1024 * 1024;
int rc;
char buf[write_size];
int i;
int expected_lock_count = 0;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
for (i = 0; i < count; i++) {
setup_ladvise_lockahead(&(advice[i]), MODE_WRITE_USER, 0, 0,
write_size - 1, true);
advice[i].lla_lockahead_result = 98674;
}
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0,
"cannot lockahead '%s': %s", mainpath, strerror(errno));
for (i = 0; i < count; i++) {
ASSERTF(advice[i].lla_lockahead_result >= 0,
"unexpected extent result for extent %d: %d",
i, advice[i].lla_lockahead_result);
}
/* Since all the requests are for the same extent, we should only have
* one lock at the end.
*/
expected_lock_count = 1;
/* Ask again until we get the locks. */
for (i = 1; i < 100; i++) {
usleep(100000); /* 0.1 second */
advice[count-1].lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
if (advice[count-1].lla_lockahead_result > 0)
break;
}
ASSERTF(advice[count-1].lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice[count-1].lla_lockahead_result);
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
free(advice);
close(fd);
return expected_lock_count;
}
/* Grow a lock forward */
static int test13(void)
{
struct llapi_lu_ladvise *advice = NULL;
const int count = 1;
int fd;
size_t write_size = 1024 * 1024;
int rc;
char buf[write_size];
int i;
int expected_lock_count = 0;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
for (i = 0; i < 100; i++) {
if (advice)
free(advice);
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0,
i * write_size, (i+1)*write_size - 1,
true);
advice[0].lla_lockahead_result = 98674;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' at offset %llu: %s",
mainpath,
advice[0].lla_end,
strerror(errno));
ASSERTF(advice[0].lla_lockahead_result >= 0,
"unexpected extent result for extent %d: %d",
i, advice[0].lla_lockahead_result);
expected_lock_count++;
}
/* Ask again until we get the lock. */
for (i = 1; i < 100; i++) {
usleep(100000); /* 0.1 second */
advice[0].lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
if (advice[0].lla_lockahead_result > 0)
break;
}
ASSERTF(advice[0].lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice[0].lla_lockahead_result);
free(advice);
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
close(fd);
return expected_lock_count;
}
/* Grow a lock backward */
static int test14(void)
{
struct llapi_lu_ladvise *advice = NULL;
const int count = 1;
int fd;
size_t write_size = 1024 * 1024;
int rc;
char buf[write_size];
int i;
const int num_blocks = 100;
int expected_lock_count = 0;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
for (i = 0; i < num_blocks; i++) {
size_t start = (num_blocks - i - 1) * write_size;
size_t end = (num_blocks - i) * write_size - 1;
if (advice)
free(advice);
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start,
end, true);
advice[0].lla_lockahead_result = 98674;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' at offset %llu: %s",
mainpath,
advice[0].lla_end,
strerror(errno));
ASSERTF(advice[0].lla_lockahead_result >= 0,
"unexpected extent result for extent %d: %d",
i, advice[0].lla_lockahead_result);
expected_lock_count++;
}
/* Ask again until we get the lock. */
for (i = 1; i < 100; i++) {
usleep(100000); /* 0.1 second */
advice[0].lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
if (advice[0].lla_lockahead_result > 0)
break;
}
ASSERTF(advice[0].lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice[0].lla_lockahead_result);
free(advice);
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
close(fd);
return expected_lock_count;
}
/* Request many locks at 10MiB intervals */
static int test15(void)
{
struct llapi_lu_ladvise *advice;
const int count = 1;
int fd;
size_t write_size = 1024 * 1024;
int rc;
char buf[write_size];
int i;
int expected_lock_count = 0;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
for (i = 0; i < 100; i++) {
/* The 'UL' designators are required to avoid undefined
* behavior which GCC turns in to an infinite loop
*/
__u64 start = i * 1024UL * 1024UL * 10UL;
__u64 end = start + 1;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start,
end, true);
advice[0].lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
ASSERTF(advice[0].lla_lockahead_result >= 0,
"unexpected extent result for extent %d: %d",
i, advice[0].lla_lockahead_result);
expected_lock_count++;
}
/* Ask again until we get the lock. */
for (i = 1; i < 100; i++) {
usleep(100000); /* 0.1 second */
advice[0].lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
if (advice[0].lla_lockahead_result > 0)
break;
}
ASSERTF(advice[0].lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice[0].lla_lockahead_result);
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
/* The write should cancel the first lock (which was too small)
* and create one of its own, so the net effect on lock count is 0.
*/
free(advice);
close(fd);
/* have to map expected return to range of valid values, 0-255 */
expected_lock_count = expected_lock_count/100;
return expected_lock_count;
}
/* Use lockahead to verify behavior of ladvise locknoexpand */
static int test16(void)
{
struct llapi_lu_ladvise *advice;
struct llapi_lu_ladvise *advice_noexpand;
const int count = 1;
int fd;
size_t write_size = 1024 * 1024;
__u64 start = 0;
__u64 end = write_size - 1;
int rc;
char buf[write_size];
int expected_lock_count = 0;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
advice_noexpand = malloc(sizeof(struct llapi_lu_ladvise));
/* First ask for a read lock, which will conflict with the write */
setup_ladvise_lockahead(advice, MODE_READ_USER, 0, start, end, false);
advice[0].lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
ASSERTF(advice[0].lla_lockahead_result == 0,
"unexpected extent result for extent: %d",
advice[0].lla_lockahead_result);
/* Use an async request to verify we got the read lock we asked for */
setup_ladvise_lockahead(advice, MODE_READ_USER, 0, start, end, true);
advice[0].lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
ASSERTF(advice[0].lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result for extent: %d",
advice[0].lla_lockahead_result);
/* Set noexpand */
advice_noexpand[0].lla_advice = LU_LADVISE_LOCKNOEXPAND;
advice_noexpand[0].lla_peradvice_flags = 0;
rc = llapi_ladvise(fd, 0, 1, advice_noexpand);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
/* This write should generate a lock on exactly "write_size" bytes */
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
/* Write should create one LDLM lock */
expected_lock_count++;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start, end, true);
advice[0].lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
ASSERTF(advice[0].lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result for extent: %d",
advice[0].lla_lockahead_result);
/* Now, disable locknoexpand and try writing again. */
advice_noexpand[0].lla_peradvice_flags = LF_UNSET;
rc = llapi_ladvise(fd, 0, 1, advice_noexpand);
/* This write should get an expanded lock */
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
/* Write should create one LDLM lock */
expected_lock_count++;
/* Verify it didn't get a lock on just the bytes it wrote.*/
usleep(100000); /* 0.1 second, plenty of time to get the lock */
start = start + write_size;
end = end + write_size;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start, end, true);
advice[0].lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
ASSERTF(advice[0].lla_lockahead_result == LLA_RESULT_DIFFERENT,
"unexpected extent result for extent %d",
advice[0].lla_lockahead_result);
free(advice);
close(fd);
return expected_lock_count;
}
/* Use lockahead to verify behavior of ladvise locknoexpand, with O_NONBLOCK.
* There should be no change in behavior.
*/
static int test17(void)
{
struct llapi_lu_ladvise *advice;
struct llapi_lu_ladvise *advice_noexpand;
const int count = 1;
int fd;
size_t write_size = 1024 * 1024;
__u64 start = 0;
__u64 end = write_size - 1;
int rc;
char buf[write_size];
int expected_lock_count = 0;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC | O_NONBLOCK, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
advice_noexpand = malloc(sizeof(struct llapi_lu_ladvise));
/* First ask for a read lock, which will conflict with the write */
setup_ladvise_lockahead(advice, MODE_READ_USER, 0, start, end, false);
advice[0].lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
ASSERTF(advice[0].lla_lockahead_result == 0,
"unexpected extent result for extent: %d",
advice[0].lla_lockahead_result);
/* Use an async request to verify we got the read lock we asked for */
setup_ladvise_lockahead(advice, MODE_READ_USER, 0, start, end, true);
advice[0].lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
ASSERTF(advice[0].lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result for extent: %d",
advice[0].lla_lockahead_result);
/* Set noexpand */
advice_noexpand[0].lla_advice = LU_LADVISE_LOCKNOEXPAND;
advice_noexpand[0].lla_peradvice_flags = 0;
rc = llapi_ladvise(fd, 0, 1, advice_noexpand);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
/* This write should generate a lock on exactly "write_size" bytes */
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
/* Write should create one LDLM lock */
expected_lock_count++;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start, end, true);
advice[0].lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
ASSERTF(advice[0].lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result for extent: %d",
advice[0].lla_lockahead_result);
/* Now, disable locknoexpand and try writing again. */
advice_noexpand[0].lla_peradvice_flags = LF_UNSET;
rc = llapi_ladvise(fd, 0, 1, advice_noexpand);
/* This write should get an expanded lock */
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
/* Write should create one LDLM lock */
expected_lock_count++;
/* Verify it didn't get a lock on just the bytes it wrote.*/
usleep(100000); /* 0.1 second, plenty of time to get the lock */
start = start + write_size;
end = end + write_size;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start, end, true);
advice[0].lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
ASSERTF(advice[0].lla_lockahead_result == LLA_RESULT_DIFFERENT,
"unexpected extent result for extent %d",
advice[0].lla_lockahead_result);
free(advice);
close(fd);
return expected_lock_count;
}
/* Test overlapping requests */
static int test18(void)
{
struct llapi_lu_ladvise *advice;
const int count = 1;
int fd;
int rc;
int i;
int expected_lock_count = 0;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
/* Overlapping locks - Should only end up with 1 */
for (i = 0; i < 10; i++) {
__u64 start = i;
__u64 end = start + 4096;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start,
end, true);
advice[0].lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
ASSERTF(advice[0].lla_lockahead_result >= 0,
"unexpected extent result for extent %d: %d",
i, advice[0].lla_lockahead_result);
}
expected_lock_count = 1;
/* Ask again until we get the lock. */
for (i = 1; i < 100; i++) {
usleep(100000); /* 0.1 second */
advice[0].lla_lockahead_result = 456789;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, 0, 4096,
true);
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
if (advice[0].lla_lockahead_result > 0)
break;
}
ASSERTF(advice[0].lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice[0].lla_lockahead_result);
free(advice);
close(fd);
return expected_lock_count;
}
/* Test that normal request blocks lock ahead requests */
static int test19(void)
{
struct llapi_lu_ladvise *advice;
const int count = 1;
int fd;
size_t write_size = 1024 * 1024;
int rc;
char buf[write_size];
int i;
int expected_lock_count = 0;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
/* This should create a lock on the whole file, which will block lock
* ahead requests.
*/
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
expected_lock_count = 1;
/* These should all be blocked. */
for (i = 0; i < 10; i++) {
__u64 start = i * 4096;
__u64 end = start + 4096;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start,
end, true);
advice[0].lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
ASSERTF(advice[0].lla_lockahead_result == LLA_RESULT_DIFFERENT,
"unexpected extent result for extent %d: %d",
i, advice[0].lla_lockahead_result);
}
free(advice);
close(fd);
return expected_lock_count;
}
/* Test sync requests, and matching with async requests */
static int test20(void)
{
struct llapi_lu_ladvise advice;
const int count = 1;
int fd;
size_t write_size = 1024 * 1024;
int rc;
char buf[write_size];
int i;
int expected_lock_count = 1;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
/* Async request */
setup_ladvise_lockahead(&advice, MODE_WRITE_USER, 0, 0,
write_size - 1, true);
/* Manually set the result so we can verify it's being modified */
advice.lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0,
"cannot lockahead '%s': %s", mainpath, strerror(errno));
ASSERTF(advice.lla_lockahead_result == 0,
"unexpected extent result: %d",
advice.lla_lockahead_result);
/* Ask again until we get the lock (status 1). */
for (i = 1; i < 100; i++) {
usleep(100000); /* 0.1 second */
advice.lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
if (advice.lla_lockahead_result > 0)
break;
}
ASSERTF(advice.lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice.lla_lockahead_result);
/* Convert to a sync request on smaller range, should match and not
* cancel
*/
setup_ladvise_lockahead(&advice, MODE_WRITE_USER, 0, 0,
write_size - 1 - write_size/2, false);
advice.lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
/* Sync requests cannot give detailed results */
ASSERTF(advice.lla_lockahead_result == 0,
"unexpected extent result: %d",
advice.lla_lockahead_result);
/* Use an async request to test original lock is still present */
setup_ladvise_lockahead(&advice, MODE_WRITE_USER, 0, 0,
write_size - 1, true);
advice.lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
ASSERTF(advice.lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice.lla_lockahead_result);
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
close(fd);
return expected_lock_count;
}
/* Test sync requests, and conflict with async requests */
static int test21(void)
{
struct llapi_lu_ladvise advice;
const int count = 1;
int fd;
size_t write_size = 1024 * 1024;
int rc;
char buf[write_size];
int i;
int expected_lock_count = 1;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
/* Async request */
setup_ladvise_lockahead(&advice, MODE_WRITE_USER, 0, 0,
write_size - 1, true);
/* Manually set the result so we can verify it's being modified */
advice.lla_lockahead_result = 345678;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0,
"cannot lockahead '%s': %s", mainpath, strerror(errno));
ASSERTF(advice.lla_lockahead_result == 0,
"unexpected extent result: %d",
advice.lla_lockahead_result);
/* Ask again until we get the lock (status 1). */
for (i = 1; i < 100; i++) {
usleep(100000); /* 0.1 second */
advice.lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
if (advice.lla_lockahead_result > 0)
break;
}
ASSERTF(advice.lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice.lla_lockahead_result);
/* Convert to a sync request on larger range, should cancel existing
* lock
*/
setup_ladvise_lockahead(&advice, MODE_WRITE_USER, 0, 0,
write_size*2 - 1, false);
advice.lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
/* Sync requests cannot give detailed results */
ASSERTF(advice.lla_lockahead_result == 0,
"unexpected extent result: %d",
advice.lla_lockahead_result);
/* Use an async request to test new lock is there */
setup_ladvise_lockahead(&advice, MODE_WRITE_USER, 0, 0,
write_size*2 - 1, true);
advice.lla_lockahead_result = 456789;
rc = llapi_ladvise(fd, 0, count, &advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath, strerror(errno));
ASSERTF(advice.lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice.lla_lockahead_result);
memset(buf, 0xaa, write_size);
rc = write(fd, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath, strerror(errno));
close(fd);
return expected_lock_count;
}
/* Test various valid and invalid inputs */
static int test22(void)
{
struct llapi_lu_ladvise *advice;
const int count = 1;
int fd;
int rc;
size_t start = 0;
size_t end = 0;
fd = open(mainpath, O_CREAT | O_RDWR | O_TRUNC, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
/* A valid async request first */
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
start = 0;
end = 1024*1024;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start, end, true);
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
free(advice);
/* Valid request sync request */
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
start = 0;
end = 1024*1024;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start, end, false);
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == 0, "cannot lockahead '%s' : %s",
mainpath, strerror(errno));
free(advice);
/* No actual block */
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
start = 0;
end = 0;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start, end, true);
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == -1 && errno == EINVAL,
"unexpected return for no block lock: %d %s",
rc, strerror(errno));
free(advice);
/* end before start */
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
start = 1024 * 1024;
end = 0;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0, start, end, true);
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == -1 && errno == EINVAL,
"unexpected return for reversed block: %d %s",
rc, strerror(errno));
free(advice);
/* bogus lock mode - 0x65464 */
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
start = 0;
end = 1024 * 1024;
setup_ladvise_lockahead(advice, 0x65464, 0, start, end, true);
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == -1 && errno == EINVAL,
"unexpected return for bogus lock mode: %d %s",
rc, strerror(errno));
free(advice);
/* bogus flags, 0x80 */
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
start = 0;
end = 1024 * 1024;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0x80, start, end,
true);
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == -1 && errno == EINVAL,
"unexpected return for bogus flags: %u %d %s",
0x80, rc, strerror(errno));
free(advice);
/* bogus flags, 0xff - CEF_MASK */
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
end = 1024 * 1024;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0xff, start, end,
true);
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == -1 && errno == EINVAL,
"unexpected return for bogus flags: %u %d %s",
0xff, rc, strerror(errno));
free(advice);
/* bogus flags, 0xffffffff */
advice = malloc(sizeof(struct llapi_lu_ladvise)*count);
end = 1024 * 1024;
setup_ladvise_lockahead(advice, MODE_WRITE_USER, 0xffffffff, start,
end, true);
rc = llapi_ladvise(fd, 0, count, advice);
ASSERTF(rc == -1 && errno == EINVAL,
"unexpected return for bogus flags: %u %d %s",
0xffffffff, rc, strerror(errno));
free(advice);
close(fd);
return 0;
}
/* Do lockahead requests from two mount points & sanity check size
*
* The key thing here is that client2 updates the size by writing, then asks
* for another lock beyond that. That next lock is never used.
* The bug (LU-11670) is that the glimpse for client1 will only check the
* highest lock and miss the size update made by the lower lock.
*/
static int test23(void)
{
struct llapi_lu_ladvise advice;
size_t write_size = 1024 * 1024;
char buf[write_size];
const int count = 1;
struct stat sb;
struct stat sb2;
int fd;
/* On second mount */
int fd2;
int rc;
int i;
fd = open(mainpath, O_CREAT | O_RDWR, 0600);
ASSERTF(fd >= 0, "open failed for '%s': %s",
mainpath, strerror(errno));
/* mainpath2 is a different Lustre mount */
fd2 = open(mainpath2, O_RDWR, 0600);
ASSERTF(fd2 >= 0, "open failed for '%s': %s",
mainpath2, strerror(errno));
/* Lock + write MiB 1 from second client */
setup_ladvise_lockahead(&advice, MODE_WRITE_USER, 0, 0,
write_size - 1, true);
/* Manually set the result so we can verify it's being modified */
advice.lla_lockahead_result = 345678;
rc = llapi_ladvise(fd2, 0, count, &advice);
ASSERTF(rc == 0,
"cannot lockahead '%s': %s", mainpath2, strerror(errno));
ASSERTF(advice.lla_lockahead_result == 0,
"unexpected extent result: %d",
advice.lla_lockahead_result);
/* Ask again until we get the lock (status 1). */
for (i = 1; i < 100; i++) {
usleep(100000); /* 0.1 second */
advice.lla_lockahead_result = 456789;
rc = llapi_ladvise(fd2, 0, count, &advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath2, strerror(errno));
if (advice.lla_lockahead_result > 0)
break;
}
ASSERTF(advice.lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice.lla_lockahead_result);
rc = write(fd2, buf, write_size);
ASSERTF(rc == sizeof(buf), "write failed for '%s': %s",
mainpath2, strerror(errno));
/* Lock (but don't write) MiB 2 from second client */
setup_ladvise_lockahead(&advice, MODE_WRITE_USER, 0, write_size,
2*write_size - 1, true);
/* Manually set the result so we can verify it's being modified */
advice.lla_lockahead_result = 345678;
rc = llapi_ladvise(fd2, 0, count, &advice);
ASSERTF(rc == 0,
"cannot lockahead '%s': %s", mainpath2, strerror(errno));
ASSERTF(advice.lla_lockahead_result == 0,
"unexpected extent result: %d",
advice.lla_lockahead_result);
/* Ask again until we get the lock (status 1). */
for (i = 1; i < 100; i++) {
usleep(100000); /* 0.1 second */
advice.lla_lockahead_result = 456789;
rc = llapi_ladvise(fd2, 0, count, &advice);
ASSERTF(rc == 0, "cannot lockahead '%s': %s",
mainpath2, strerror(errno));
if (advice.lla_lockahead_result > 0)
break;
}
ASSERTF(advice.lla_lockahead_result == LLA_RESULT_SAME,
"unexpected extent result: %d",
advice.lla_lockahead_result);
rc = fstat(fd, &sb);
ASSERTF(!rc, "stat failed for '%s': %s",
mainpath, strerror(errno));
rc = fstat(fd2, &sb2);
ASSERTF(!rc, "stat failed for '%s': %s",
mainpath2, strerror(errno));
ASSERTF(sb.st_size == sb2.st_size,
"size on %s and %s differs: %lu vs %lu",
mainpath, mainpath2, sb.st_size, sb2.st_size);
ASSERTF(sb.st_size == write_size, "size %lu != bytes written (%lu)",
sb.st_size, write_size);
close(fd);
close(fd2);
return 0;
}
static void usage(char *prog)
{
fprintf(stderr,
"Usage: %s [-d lustre_dir], [-D lustre_dir2] [-t test]\n",
prog);
exit(-1);
}
static void process_args(int argc, char *argv[])
{
int c;
while ((c = getopt(argc, argv, "d:D:f:t:")) != -1) {
switch (c) {
case 'f':
mainfile = optarg;
break;
case 'd':
lustre_dir = optarg;
break;
case 'D':
lustre_dir2 = optarg;
break;
case 't':
single_test = atoi(optarg);
break;
case '?':
default:
fprintf(stderr, "Invalid option '%c'\n", optopt);
usage(argv[0]);
break;
}
}
}
int main(int argc, char *argv[])
{
char fsname[8];
int rc;
process_args(argc, argv);
if (lustre_dir == NULL)
lustre_dir = "/mnt/lustre";
if (mainfile == NULL)
mainfile = "lockahead_test_654";
rc = llapi_search_mounts(lustre_dir, 0, fsmountdir, fsname);
if (rc != 0) {
fprintf(stderr, "Error: '%s': not a Lustre filesystem\n",
lustre_dir);
return -1;
}
if (lustre_dir2) {
rc = llapi_search_mounts(lustre_dir2, 0, fsmountdir, fsname);
if (rc != 0) {
fprintf(stderr,
"Error: '%s': not a Lustre filesystem\n",
lustre_dir2);
return -1;
}
}
/* Play nice with Lustre test scripts. Non-line buffered output
* stream under I/O redirection may appear incorrectly.
*/
setvbuf(stdout, NULL, _IOLBF, 0);
/* Create a test filename and reuse it. Remove possibly old files. */
rc = snprintf(mainpath, sizeof(mainpath), "%s/%s", lustre_dir,
mainfile);
ASSERTF(rc > 0 && rc < sizeof(mainpath), "invalid name for mainpath");
if (lustre_dir2) {
rc = snprintf(mainpath2, sizeof(mainpath2), "%s/%s",
lustre_dir2, mainfile);
ASSERTF(rc > 0 && rc < sizeof(mainpath2),
"invalid name for mainpath2");
}
cleanup();
switch (single_test) {
case 0:
PERFORM(test10);
PERFORM(test11);
PERFORM(test12);
PERFORM(test13);
PERFORM(test14);
PERFORM(test15);
PERFORM(test16);
PERFORM(test17);
PERFORM(test18);
PERFORM(test19);
PERFORM(test20);
PERFORM(test21);
PERFORM(test22);
/* Some tests require a second mount point */
if (lustre_dir2)
PERFORM(test23);
/* When running all the test cases, we can't use the return
* from the last test case, as it might be non-zero to return
* info, rather than for an error. Test cases assert and exit
* if an error occurs.
*/
rc = 0;
break;
case 10:
PERFORM(test10);
break;
case 11:
PERFORM(test11);
break;
case 12:
PERFORM(test12);
break;
case 13:
PERFORM(test13);
break;
case 14:
PERFORM(test14);
break;
case 15:
PERFORM(test15);
break;
case 16:
PERFORM(test16);
break;
case 17:
PERFORM(test17);
break;
case 18:
PERFORM(test18);
break;
case 19:
PERFORM(test19);
break;
case 20:
PERFORM(test20);
break;
case 21:
PERFORM(test21);
break;
case 22:
PERFORM(test22);
break;
case 23:
ASSERTF(lustre_dir2,
"must provide second mount point for test 23");
PERFORM(test23);
break;
default:
fprintf(stderr, "impossible value of single_test %d\n",
single_test);
rc = -1;
break;
}
return rc;
}