Whamcloud - gitweb
LU-16747 llapi: fix race in get_root_path_slow()
[fs/lustre-release.git] / lustre / tests / llapi_root_test.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22
23 /*
24  * The purpose of this test is to check Lustre API root fd cache.
25  *
26  * The program will exit as soon as a non zero error code is returned.
27  */
28
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <getopt.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <poll.h>
35 #include <sys/ioctl.h>
36 #include <time.h>
37 #include <pthread.h>
38
39
40 #include <lustre/lustreapi.h>
41 #include <linux/lustre/lustre_idl.h>
42
43 #define ERROR(fmt, ...)                                                 \
44         fprintf(stderr, "%s: %s:%d: %s: " fmt "\n",                     \
45                 program_invocation_short_name, __FILE__, __LINE__,      \
46                 __func__, ## __VA_ARGS__)
47
48 #define DIE(fmt, ...)                  \
49         do {                           \
50                 ERROR(fmt, ## __VA_ARGS__);     \
51                 exit(EXIT_FAILURE);             \
52         } while (0)
53
54 #define ASSERTF(cond, fmt, ...)                                         \
55         do {                                                            \
56                 if (!(cond))                                            \
57                         DIE("assertion '%s' failed: "fmt,               \
58                             #cond, ## __VA_ARGS__);                     \
59         } while (0)
60
61 #define PERFORM(testfn) \
62         do {                                                            \
63                 cleanup();                                              \
64                 fprintf(stderr, "Starting test " #testfn " at %lld\n",  \
65                         (unsigned long long)time(NULL));                \
66                 testfn();                                               \
67                 fprintf(stderr, "Finishing test " #testfn " at %lld\n", \
68                        (unsigned long long)time(NULL));                 \
69                 cleanup();                                              \
70         } while (0)
71
72 /* Name of file/directory. Will be set once and will not change. */
73 static char *mainpath;  /* path to file on mountpoint 1 */
74 static char *mainpath2; /* path to file on mountpoint 2 */
75
76 static char mnt_dir[PATH_MAX];  /* Lustre mountpoint 1 */
77 static char mnt_dir2[PATH_MAX]; /* Lustre mountpoint 2 */
78 static int mnt_fd = -1;
79 static int mnt_fd2 = -1;
80
81 /* Cleanup our test directory. */
82 static void cleanup(void)
83 {
84         int rc;
85
86         rc = remove(mainpath);
87         ASSERTF(!rc || errno == ENOENT,
88                 "Failed to unlink %s: %s", mainpath, strerror(errno));
89 }
90
91 #define TEST1_THR_NBR 20
92 void *test1_thr(void *arg)
93 {
94         char *fidstr = arg;
95         char path[PATH_MAX];
96         long long recno = -1;
97         int linkno = 0;
98         long long rc;
99
100         rc = llapi_fid2path(mnt_dir2, fidstr, path,
101                             sizeof(path), &recno, &linkno);
102
103         return (void *) rc;
104 }
105
106 /* Race on root cache at startup */
107 static void test1(void)
108 {
109         static pthread_t thread[TEST1_THR_NBR];
110         int fd, i, iter;
111         long long rc;
112         struct lu_fid fid;
113         char fidstr[FID_LEN + 1];
114
115         fd = creat(mainpath, 00660);
116         ASSERTF(fd >= 0, "creat failed for '%s': %s",
117                 mainpath, strerror(errno));
118
119         rc = llapi_fd2fid(fd, &fid);
120         ASSERTF(rc == 0, "llapi_fd2fid failed for '%s': %s",
121                 mainpath, strerror(-rc));
122         close(fd);
123
124         snprintf(fidstr, sizeof(fidstr), DFID_NOBRACE, PFID(&fid));
125         for (iter = 0; iter < 100; iter++) {
126                 /* reset cache on first mountpoint */
127                 fd = llapi_open_by_fid(mnt_dir, &fid, O_RDONLY);
128                 ASSERTF(fd >= 0, "llapi_open_by_fid for " DFID_NOBRACE ": %d",
129                         PFID(&fid), fd);
130                 close(fd);
131
132                 /* start threads with llapi_open_by_fid() */
133                 for (i = 0; i < TEST1_THR_NBR; i++)
134                         pthread_create(&thread[i], NULL, &test1_thr, fidstr);
135
136                 for (i = 0; i < TEST1_THR_NBR; i++) {
137                         pthread_join(thread[i], (void **) &rc);
138                         ASSERTF(rc == 0,
139                                 "llapi_fid2path for " DFID_NOBRACE " (iter: %d, thr:%d): %s",
140                                 PFID(&fid), iter, i, strerror(-rc));
141                 }
142         }
143 }
144
145 static void usage(char *prog)
146 {
147         fprintf(stderr, "Usage: %s [-h]\n", basename(prog));
148         fprintf(stderr, "or:    %s FILEPATH1 FILEPATH2\n", basename(prog));
149         exit(EXIT_FAILURE);
150 }
151
152 static void process_args(int argc, char *argv[])
153 {
154         /* default mountpoints used */
155         if (argc == 1)
156                 return;
157
158         if (argc > 1 && argv[1][0] == '-')
159                 usage(argv[0]);
160
161         if (argc <= 2 || argv[1][0] == '\0' || argv[2][0] == '\0')
162                 usage(argv[0]);
163
164         mainpath = argv[1];
165         mainpath2 = argv[2];
166 }
167
168
169 static int fill_default_paths(void)
170 {
171         static char tmp1[PATH_MAX] = "/mnt/lustre/llapi_root_test.XXXXXX";
172         static char tmp2[PATH_MAX] = "/mnt/lustre2/";
173         int fd;
174
175         /* default paths needed?*/
176         if (mainpath || mainpath2)
177                 return 0;
178
179         fd = mkstemp(tmp1);
180         if (fd < 0) {
181                 fprintf(stderr, "Failed to creat %s: %s\n",
182                         tmp1, strerror(errno));
183                 exit(EXIT_FAILURE);
184         }
185
186         close(fd);
187         strcat(tmp2, basename(tmp1));
188
189         mainpath = tmp1;
190         mainpath2 = tmp2;
191
192         return 0;
193 }
194
195 int main(int argc, char *argv[])
196 {
197         char fsname[8 + 1];
198         char fsname2[8 + 1];
199         int rc;
200
201         process_args(argc, argv);
202         fill_default_paths();
203         atexit(cleanup);
204
205         if (strcmp(basename(mainpath), basename(mainpath2)) != 0 ||
206                    strcmp(mainpath, mainpath2) == 0) {
207                 fprintf(stderr, "%s and %s should be the same file on 2 distinct mountpoints\n",
208                         mainpath, mainpath2);
209                 return EXIT_FAILURE;
210         }
211
212         rc = llapi_search_mounts(mainpath, 0, mnt_dir, fsname);
213         if (rc != 0) {
214                 fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
215                         mainpath);
216                 return EXIT_FAILURE;
217         }
218
219         rc = llapi_search_mounts(mainpath2, 0, mnt_dir2, fsname2);
220         if (rc != 0) {
221                 fprintf(stderr, "Error: %s: not a Lustre filesystem\n",
222                         mainpath2);
223                 return EXIT_FAILURE;
224         }
225
226         if (strcmp(fsname, fsname2) != 0) {
227                 fprintf(stderr, "%s and %s are not on the same filesystem (%s, %s)\n",
228                         mnt_dir, mnt_dir2, fsname, fsname2);
229                 return EXIT_FAILURE;
230         }
231
232         mnt_fd = open(mnt_dir, O_RDONLY|O_DIRECTORY);
233         ASSERTF(mnt_fd >= 0, "cannot open '%s': %s\n", mnt_dir, strerror(errno));
234
235         mnt_fd2 = open(mnt_dir2, O_RDONLY|O_DIRECTORY);
236         ASSERTF(mnt_fd2 >= 0, "cannot open '%s': %s\n", mnt_dir2, strerror(errno));
237
238         fprintf(stderr, "Starting: %s %s %s\n\n",
239                 basename(argv[0]), mainpath, mainpath2);
240
241         /* Play nice with Lustre test scripts. Non-line buffered output
242          * stream under I/O redirection may appear incorrectly.
243          */
244         setvbuf(stdout, NULL, _IOLBF, 0);
245
246         PERFORM(test1);
247
248         return EXIT_SUCCESS;
249 }