~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/cachestat/test_cachestat.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 #define _GNU_SOURCE
  3 #define __SANE_USERSPACE_TYPES__ // Use ll64
  4 
  5 #include <stdio.h>
  6 #include <stdbool.h>
  7 #include <linux/kernel.h>
  8 #include <linux/magic.h>
  9 #include <linux/mman.h>
 10 #include <sys/mman.h>
 11 #include <sys/shm.h>
 12 #include <sys/syscall.h>
 13 #include <sys/vfs.h>
 14 #include <unistd.h>
 15 #include <string.h>
 16 #include <fcntl.h>
 17 #include <errno.h>
 18 
 19 #include "../kselftest.h"
 20 
 21 #define NR_TESTS        9
 22 
 23 static const char * const dev_files[] = {
 24         "/dev/zero", "/dev/null", "/dev/urandom",
 25         "/proc/version", "/proc"
 26 };
 27 
 28 void print_cachestat(struct cachestat *cs)
 29 {
 30         ksft_print_msg(
 31         "Using cachestat: Cached: %llu, Dirty: %llu, Writeback: %llu, Evicted: %llu, Recently Evicted: %llu\n",
 32         cs->nr_cache, cs->nr_dirty, cs->nr_writeback,
 33         cs->nr_evicted, cs->nr_recently_evicted);
 34 }
 35 
 36 bool write_exactly(int fd, size_t filesize)
 37 {
 38         int random_fd = open("/dev/urandom", O_RDONLY);
 39         char *cursor, *data;
 40         int remained;
 41         bool ret;
 42 
 43         if (random_fd < 0) {
 44                 ksft_print_msg("Unable to access urandom.\n");
 45                 ret = false;
 46                 goto out;
 47         }
 48 
 49         data = malloc(filesize);
 50         if (!data) {
 51                 ksft_print_msg("Unable to allocate data.\n");
 52                 ret = false;
 53                 goto close_random_fd;
 54         }
 55 
 56         remained = filesize;
 57         cursor = data;
 58 
 59         while (remained) {
 60                 ssize_t read_len = read(random_fd, cursor, remained);
 61 
 62                 if (read_len <= 0) {
 63                         ksft_print_msg("Unable to read from urandom.\n");
 64                         ret = false;
 65                         goto out_free_data;
 66                 }
 67 
 68                 remained -= read_len;
 69                 cursor += read_len;
 70         }
 71 
 72         /* write random data to fd */
 73         remained = filesize;
 74         cursor = data;
 75         while (remained) {
 76                 ssize_t write_len = write(fd, cursor, remained);
 77 
 78                 if (write_len <= 0) {
 79                         ksft_print_msg("Unable write random data to file.\n");
 80                         ret = false;
 81                         goto out_free_data;
 82                 }
 83 
 84                 remained -= write_len;
 85                 cursor += write_len;
 86         }
 87 
 88         ret = true;
 89 out_free_data:
 90         free(data);
 91 close_random_fd:
 92         close(random_fd);
 93 out:
 94         return ret;
 95 }
 96 
 97 /*
 98  * fsync() is implemented via noop_fsync() on tmpfs. This makes the fsync()
 99  * test fail below, so we need to check for test file living on a tmpfs.
100  */
101 static bool is_on_tmpfs(int fd)
102 {
103         struct statfs statfs_buf;
104 
105         if (fstatfs(fd, &statfs_buf))
106                 return false;
107 
108         return statfs_buf.f_type == TMPFS_MAGIC;
109 }
110 
111 /*
112  * Open/create the file at filename, (optionally) write random data to it
113  * (exactly num_pages), then test the cachestat syscall on this file.
114  *
115  * If test_fsync == true, fsync the file, then check the number of dirty
116  * pages.
117  */
118 static int test_cachestat(const char *filename, bool write_random, bool create,
119                           bool test_fsync, unsigned long num_pages,
120                           int open_flags, mode_t open_mode)
121 {
122         size_t PS = sysconf(_SC_PAGESIZE);
123         int filesize = num_pages * PS;
124         int ret = KSFT_PASS;
125         long syscall_ret;
126         struct cachestat cs;
127         struct cachestat_range cs_range = { 0, filesize };
128 
129         int fd = open(filename, open_flags, open_mode);
130 
131         if (fd == -1) {
132                 ksft_print_msg("Unable to create/open file.\n");
133                 ret = KSFT_FAIL;
134                 goto out;
135         } else {
136                 ksft_print_msg("Create/open %s\n", filename);
137         }
138 
139         if (write_random) {
140                 if (!write_exactly(fd, filesize)) {
141                         ksft_print_msg("Unable to access urandom.\n");
142                         ret = KSFT_FAIL;
143                         goto out1;
144                 }
145         }
146 
147         syscall_ret = syscall(__NR_cachestat, fd, &cs_range, &cs, 0);
148 
149         ksft_print_msg("Cachestat call returned %ld\n", syscall_ret);
150 
151         if (syscall_ret) {
152                 ksft_print_msg("Cachestat returned non-zero.\n");
153                 ret = KSFT_FAIL;
154                 goto out1;
155 
156         } else {
157                 print_cachestat(&cs);
158 
159                 if (write_random) {
160                         if (cs.nr_cache + cs.nr_evicted != num_pages) {
161                                 ksft_print_msg(
162                                         "Total number of cached and evicted pages is off.\n");
163                                 ret = KSFT_FAIL;
164                         }
165                 }
166         }
167 
168         if (test_fsync) {
169                 if (is_on_tmpfs(fd)) {
170                         ret = KSFT_SKIP;
171                 } else if (fsync(fd)) {
172                         ksft_print_msg("fsync fails.\n");
173                         ret = KSFT_FAIL;
174                 } else {
175                         syscall_ret = syscall(__NR_cachestat, fd, &cs_range, &cs, 0);
176 
177                         ksft_print_msg("Cachestat call (after fsync) returned %ld\n",
178                                 syscall_ret);
179 
180                         if (!syscall_ret) {
181                                 print_cachestat(&cs);
182 
183                                 if (cs.nr_dirty) {
184                                         ret = KSFT_FAIL;
185                                         ksft_print_msg(
186                                                 "Number of dirty should be zero after fsync.\n");
187                                 }
188                         } else {
189                                 ksft_print_msg("Cachestat (after fsync) returned non-zero.\n");
190                                 ret = KSFT_FAIL;
191                                 goto out1;
192                         }
193                 }
194         }
195 
196 out1:
197         close(fd);
198 
199         if (create)
200                 remove(filename);
201 out:
202         return ret;
203 }
204 
205 bool test_cachestat_shmem(void)
206 {
207         size_t PS = sysconf(_SC_PAGESIZE);
208         size_t filesize = PS * 512 * 2; /* 2 2MB huge pages */
209         int syscall_ret;
210         size_t compute_len = PS * 512;
211         struct cachestat_range cs_range = { PS, compute_len };
212         char *filename = "tmpshmcstat";
213         struct cachestat cs;
214         bool ret = true;
215         unsigned long num_pages = compute_len / PS;
216         int fd = shm_open(filename, O_CREAT | O_RDWR, 0600);
217 
218         if (fd < 0) {
219                 ksft_print_msg("Unable to create shmem file.\n");
220                 ret = false;
221                 goto out;
222         }
223 
224         if (ftruncate(fd, filesize)) {
225                 ksft_print_msg("Unable to truncate shmem file.\n");
226                 ret = false;
227                 goto close_fd;
228         }
229 
230         if (!write_exactly(fd, filesize)) {
231                 ksft_print_msg("Unable to write to shmem file.\n");
232                 ret = false;
233                 goto close_fd;
234         }
235 
236         syscall_ret = syscall(__NR_cachestat, fd, &cs_range, &cs, 0);
237 
238         if (syscall_ret) {
239                 ksft_print_msg("Cachestat returned non-zero.\n");
240                 ret = false;
241                 goto close_fd;
242         } else {
243                 print_cachestat(&cs);
244                 if (cs.nr_cache + cs.nr_evicted != num_pages) {
245                         ksft_print_msg(
246                                 "Total number of cached and evicted pages is off.\n");
247                         ret = false;
248                 }
249         }
250 
251 close_fd:
252         shm_unlink(filename);
253 out:
254         return ret;
255 }
256 
257 int main(void)
258 {
259         int ret;
260 
261         ksft_print_header();
262 
263         ret = syscall(__NR_cachestat, -1, NULL, NULL, 0);
264         if (ret == -1 && errno == ENOSYS)
265                 ksft_exit_skip("cachestat syscall not available\n");
266 
267         ksft_set_plan(NR_TESTS);
268 
269         if (ret == -1 && errno == EBADF) {
270                 ksft_test_result_pass("bad file descriptor recognized\n");
271                 ret = 0;
272         } else {
273                 ksft_test_result_fail("bad file descriptor ignored\n");
274                 ret = 1;
275         }
276 
277         for (int i = 0; i < 5; i++) {
278                 const char *dev_filename = dev_files[i];
279 
280                 if (test_cachestat(dev_filename, false, false, false,
281                         4, O_RDONLY, 0400) == KSFT_PASS)
282                         ksft_test_result_pass("cachestat works with %s\n", dev_filename);
283                 else {
284                         ksft_test_result_fail("cachestat fails with %s\n", dev_filename);
285                         ret = 1;
286                 }
287         }
288 
289         if (test_cachestat("tmpfilecachestat", true, true,
290                 false, 4, O_CREAT | O_RDWR, 0600) == KSFT_PASS)
291                 ksft_test_result_pass("cachestat works with a normal file\n");
292         else {
293                 ksft_test_result_fail("cachestat fails with normal file\n");
294                 ret = 1;
295         }
296 
297         switch (test_cachestat("tmpfilecachestat", true, true,
298                 true, 4, O_CREAT | O_RDWR, 0600)) {
299         case KSFT_FAIL:
300                 ksft_test_result_fail("cachestat fsync fails with normal file\n");
301                 ret = KSFT_FAIL;
302                 break;
303         case KSFT_PASS:
304                 ksft_test_result_pass("cachestat fsync works with a normal file\n");
305                 break;
306         case KSFT_SKIP:
307                 ksft_test_result_skip("tmpfilecachestat is on tmpfs\n");
308                 break;
309         }
310 
311         if (test_cachestat_shmem())
312                 ksft_test_result_pass("cachestat works with a shmem file\n");
313         else {
314                 ksft_test_result_fail("cachestat fails with a shmem file\n");
315                 ret = 1;
316         }
317 
318         return ret;
319 }
320 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php