#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
+#include <sys/syscall.h>
#include "vm_util.h"
#include "kselftest.h"
-void run_dio_using_hugetlb(unsigned int start_off, unsigned int end_off)
+#ifndef STATX_DIOALIGN
+#define STATX_DIOALIGN 0x00002000U
+#endif
+
+static int get_dio_alignment(int fd)
+{
+ struct statx stx;
+ int ret;
+
+ ret = syscall(__NR_statx, fd, "", AT_EMPTY_PATH, STATX_DIOALIGN, &stx);
+ if (ret < 0)
+ return -1;
+
+ /*
+ * If STATX_DIOALIGN is unsupported, assume no alignment
+ * constraint and let the test proceed.
+ */
+ if (!(stx.stx_mask & STATX_DIOALIGN) || !stx.stx_dio_offset_align)
+ return 1;
+
+ return stx.stx_dio_offset_align;
+}
+
+static bool check_dio_alignment(unsigned int start_off,
+ unsigned int end_off, unsigned int align)
+{
+ unsigned int writesize = end_off - start_off;
+
+ /*
+ * The kernel's DIO path checks that file offset, length, and
+ * buffer address are all multiples of dio_offset_align. When
+ * this test case's parameters don't satisfy that, the write
+ * would fail with -EINVAL before exercising the hugetlb unpin
+ * path, so skip.
+ */
+ if (start_off % align != 0 || writesize % align != 0) {
+ ksft_test_result_skip("DIO align=%u incompatible with offset %u writesize %u\n",
+ align, start_off, writesize);
+ return false;
+ }
+
+ return true;
+}
+
+static void run_dio_using_hugetlb(int fd, unsigned int start_off,
+ unsigned int end_off, unsigned int align)
{
- int fd;
char *buffer = NULL;
char *orig_buffer = NULL;
size_t h_pagesize = 0;
const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB;
const int mmap_prot = PROT_READ | PROT_WRITE;
+ if (!check_dio_alignment(start_off, end_off, align))
+ return;
+
writesize = end_off - start_off;
/* Get the default huge page size */
if (!h_pagesize)
ksft_exit_fail_msg("Unable to determine huge page size\n");
- /* Open the file to DIO */
- fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664);
- if (fd < 0)
- ksft_exit_fail_perror("Error opening file\n");
+ /* Reset file position since fd is shared across tests */
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ ksft_exit_fail_perror("lseek failed\n");
/* Get the free huge pages before allocation */
free_hpage_b = get_free_hugepages();
/* unmap the huge page */
munmap(orig_buffer, h_pagesize);
- close(fd);
/* Get the free huge pages after unmap*/
free_hpage_a = get_free_hugepages();
int main(void)
{
- size_t pagesize = 0;
- int fd;
+ int fd, align;
+ const size_t pagesize = psize();
ksft_print_header();
- /* Open the file to DIO */
- fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664);
- if (fd < 0)
- ksft_exit_skip("Unable to allocate file: %s\n", strerror(errno));
- close(fd);
-
/* Check if huge pages are free */
if (!get_free_hugepages())
ksft_exit_skip("No free hugepage, exiting\n");
- ksft_set_plan(4);
+ fd = open("/tmp", O_TMPFILE | O_RDWR | O_DIRECT, 0664);
+ if (fd < 0)
+ ksft_exit_skip("Unable to allocate file: %s\n", strerror(errno));
- /* Get base page size */
- pagesize = psize();
+ align = get_dio_alignment(fd);
+ if (align < 0)
+ ksft_exit_skip("Unable to obtain DIO alignment: %s\n",
+ strerror(errno));
+ ksft_set_plan(4);
/* start and end is aligned to pagesize */
- run_dio_using_hugetlb(0, (pagesize * 3));
+ run_dio_using_hugetlb(fd, 0, (pagesize * 3), align);
/* start is aligned but end is not aligned */
- run_dio_using_hugetlb(0, (pagesize * 3) - (pagesize / 2));
+ run_dio_using_hugetlb(fd, 0, (pagesize * 3) - (pagesize / 2), align);
/* start is unaligned and end is aligned */
- run_dio_using_hugetlb(pagesize / 2, (pagesize * 3));
+ run_dio_using_hugetlb(fd, pagesize / 2, (pagesize * 3), align);
/* both start and end are unaligned */
- run_dio_using_hugetlb(pagesize / 2, (pagesize * 3) + (pagesize / 2));
+ run_dio_using_hugetlb(fd, pagesize / 2, (pagesize * 3) + (pagesize / 2), align);
+
+ close(fd);
ksft_finished();
}