1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2018 FUJITSU LIMITED. All rights reserved.
4 * Author: Xiao Yang <[email protected]>
5 * Copyright (c) Linux Test Project, 2019-2023
6 */
7
8 /*\
9 * [Description]
10 *
11 * Check the basic functionality of the preadv(2) for the file
12 * opened with O_DIRECT in all filesystem.
13 *
14 * preadv(2) should succeed to read the expected content of data
15 * and after reading the file, the file offset is not changed.
16 */
17
18 #define _GNU_SOURCE
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/uio.h>
22 #include <sys/ioctl.h>
23 #include <sys/mount.h>
24 #include "tst_test.h"
25 #include "preadv.h"
26
27 #define MNTPOINT "mntpoint"
28 #define FNAME MNTPOINT"/file"
29
30 static int fd;
31 static off_t blk_off, zero_off;
32 static ssize_t blksz;
33 static char *pop_buf;
34
35 static struct iovec rd_iovec[] = {
36 {NULL, 0},
37 {NULL, 0},
38 };
39
40 static struct tcase {
41 int count;
42 off_t *offset;
43 ssize_t *size;
44 char content;
45 } tcases[] = {
46 {1, &zero_off, &blksz, 0x61},
47 {2, &zero_off, &blksz, 0x61},
48 {1, &blk_off, &blksz, 0x62},
49 };
50
verify_direct_preadv(unsigned int n)51 static void verify_direct_preadv(unsigned int n)
52 {
53 int i;
54 char *vec;
55 struct tcase *tc = &tcases[n];
56
57 vec = rd_iovec[0].iov_base;
58 memset(vec, 0x00, blksz);
59
60 SAFE_LSEEK(fd, 0, SEEK_SET);
61
62 TEST(preadv(fd, rd_iovec, tc->count, *tc->offset));
63 if (TST_RET < 0) {
64 tst_res(TFAIL | TTERRNO, "preadv(O_DIRECT) fails");
65 return;
66 }
67
68 if (TST_RET != *tc->size) {
69 tst_res(TFAIL, "preadv(O_DIRECT) read %li bytes, expected %zi",
70 TST_RET, *tc->size);
71 return;
72 }
73
74 for (i = 0; i < *tc->size; i++) {
75 if (vec[i] != tc->content)
76 break;
77 }
78
79 if (i < *tc->size) {
80 tst_res(TFAIL, "Buffer wrong at %i have %02x expected %02x",
81 i, vec[i], tc->content);
82 return;
83 }
84
85 if (SAFE_LSEEK(fd, 0, SEEK_CUR) != 0) {
86 tst_res(TFAIL, "preadv(O_DIRECT) has changed file offset");
87 return;
88 }
89
90 tst_res(TPASS, "preadv(O_DIRECT) read %zi bytes successfully "
91 "with content '%c' expectedly", *tc->size, tc->content);
92 }
93
setup(void)94 static void setup(void)
95 {
96 int dev_fd, ret;
97
98 dev_fd = SAFE_OPEN(tst_device->dev, O_RDWR);
99 SAFE_IOCTL(dev_fd, BLKSSZGET, &ret);
100 SAFE_CLOSE(dev_fd);
101
102 if (ret <= 0)
103 tst_brk(TBROK, "BLKSSZGET returned invalid block size %i", ret);
104
105 tst_res(TINFO, "Using block size %i", ret);
106
107 blksz = ret;
108 blk_off = ret;
109
110 fd = SAFE_OPEN(FNAME, O_RDWR | O_CREAT | O_DIRECT, 0644);
111
112 pop_buf = SAFE_MEMALIGN(blksz, blksz);
113 memset(pop_buf, 0x61, blksz);
114 SAFE_WRITE(SAFE_WRITE_ALL, fd, pop_buf, blksz);
115
116 memset(pop_buf, 0x62, blksz);
117 SAFE_WRITE(SAFE_WRITE_ALL, fd, pop_buf, blksz);
118
119 rd_iovec[0].iov_base = SAFE_MEMALIGN(blksz, blksz);
120 rd_iovec[0].iov_len = blksz;
121 }
122
cleanup(void)123 static void cleanup(void)
124 {
125 free(pop_buf);
126 free(rd_iovec[0].iov_base);
127
128 if (fd > 0)
129 SAFE_CLOSE(fd);
130 }
131
132 static struct tst_test test = {
133 .tcnt = ARRAY_SIZE(tcases),
134 .setup = setup,
135 .cleanup = cleanup,
136 .test = verify_direct_preadv,
137 .mntpoint = MNTPOINT,
138 .mount_device = 1,
139 .all_filesystems = 1,
140 .skip_filesystems = (const char *[]) {
141 "tmpfs",
142 NULL
143 }
144 };
145