1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2019 Red Hat Ltd
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 #include "gtest/gtest.h"
16 #include "include/cephfs/libcephfs.h"
17 #include "include/rados/librados.h"
21 #include <sys/types.h>
24 #if defined(__linux__)
25 #include <sys/xattr.h>
30 TEST(LibCephFS
, LazyIOOneWriterMulipleReaders
) {
31 struct ceph_mount_info
*ca
, *cb
;
32 ASSERT_EQ(ceph_create(&ca
, NULL
), 0);
33 ASSERT_EQ(ceph_conf_read_file(ca
, NULL
), 0);
34 ASSERT_EQ(0, ceph_conf_parse_env(ca
, NULL
));
35 ASSERT_EQ(ceph_mount(ca
, NULL
), 0);
37 ASSERT_EQ(ceph_create(&cb
, NULL
), 0);
38 ASSERT_EQ(ceph_conf_read_file(cb
, NULL
), 0);
39 ASSERT_EQ(0, ceph_conf_parse_env(cb
, NULL
));
40 ASSERT_EQ(ceph_mount(cb
, NULL
), 0);
43 snprintf(name
, sizeof(name
), "foo.%d", getpid());
45 int fda
= ceph_open(ca
, name
, O_CREAT
|O_RDWR
, 0644);
48 int fdb
= ceph_open(cb
, name
, O_RDONLY
, 0644);
51 ASSERT_EQ(0, ceph_lazyio(ca
, fda
, 1));
52 ASSERT_EQ(0, ceph_lazyio(cb
, fdb
, 1));
54 char out_buf
[] = "fooooooooo";
56 /* Client a issues a write and propagates/flushes the buffer */
57 ASSERT_EQ((int)sizeof(out_buf
), ceph_write(ca
, fda
, out_buf
, sizeof(out_buf
), 0));
58 ASSERT_EQ(0, ceph_lazyio_propagate(ca
, fda
, 0, 0));
60 /* Client a issues a write and propagates/flushes the buffer */
61 ASSERT_EQ((int)sizeof(out_buf
), ceph_write(ca
, fda
, out_buf
, sizeof(out_buf
), 10));
62 ASSERT_EQ(0, ceph_lazyio_propagate(ca
, fda
, 0, 0));
65 /* Calling ceph_lazyio_synchronize here will invalidate client b's cache and hence enable client a to fetch the propagated write of client a in the subsequent read */
66 ASSERT_EQ(0, ceph_lazyio_synchronize(cb
, fdb
, 0, 0));
67 ASSERT_EQ(ceph_read(cb
, fdb
, in_buf
, sizeof(in_buf
), 0), 2*strlen(out_buf
)+1);
68 ASSERT_STREQ(in_buf
, "fooooooooofooooooooo");
70 /* Client a does not need to call ceph_lazyio_synchronize here because it is the latest writer and fda holds the updated inode*/
71 ASSERT_EQ(ceph_read(ca
, fda
, in_buf
, sizeof(in_buf
), 0), 2*strlen(out_buf
)+1);
72 ASSERT_STREQ(in_buf
, "fooooooooofooooooooo");
81 TEST(LibCephFS
, LazyIOMultipleWritersMulipleReaders
) {
82 struct ceph_mount_info
*ca
, *cb
;
83 ASSERT_EQ(ceph_create(&ca
, NULL
), 0);
84 ASSERT_EQ(ceph_conf_read_file(ca
, NULL
), 0);
85 ASSERT_EQ(0, ceph_conf_parse_env(ca
, NULL
));
86 ASSERT_EQ(ceph_mount(ca
, NULL
), 0);
88 ASSERT_EQ(ceph_create(&cb
, NULL
), 0);
89 ASSERT_EQ(ceph_conf_read_file(cb
, NULL
), 0);
90 ASSERT_EQ(0, ceph_conf_parse_env(cb
, NULL
));
91 ASSERT_EQ(ceph_mount(cb
, NULL
), 0);
94 snprintf(name
, sizeof(name
), "foo2.%d", getpid());
96 int fda
= ceph_open(ca
, name
, O_CREAT
|O_RDWR
, 0644);
99 int fdb
= ceph_open(cb
, name
, O_RDWR
, 0644);
102 ASSERT_EQ(0, ceph_lazyio(ca
, fda
, 1));
103 ASSERT_EQ(0, ceph_lazyio(cb
, fdb
, 1));
105 char out_buf
[] = "fooooooooo";
106 /* Client a issues a write and propagates/flushes the buffer */
107 ASSERT_EQ((int)sizeof(out_buf
), ceph_write(ca
, fda
, out_buf
, sizeof(out_buf
), 0));
108 ASSERT_EQ(0, ceph_lazyio_propagate(ca
, fda
, 0, 0));
110 /* Client b issues a write and propagates/flushes the buffer*/
111 ASSERT_EQ((int)sizeof(out_buf
), ceph_write(cb
, fdb
, out_buf
, sizeof(out_buf
), 10));
112 ASSERT_EQ(0, ceph_lazyio_propagate(cb
, fdb
, 0, 0));
115 /* Calling ceph_lazyio_synchronize here will invalidate client a's cache and hence enable client a to fetch the propagated writes of client b in the subsequent read */
116 ASSERT_EQ(0, ceph_lazyio_synchronize(ca
, fda
, 0, 0));
117 ASSERT_EQ(ceph_read(ca
, fda
, in_buf
, sizeof(in_buf
), 0), 2*strlen(out_buf
)+1);
118 ASSERT_STREQ(in_buf
, "fooooooooofooooooooo");
120 /* Client b does not need to call ceph_lazyio_synchronize here because it is the latest writer and the writes before it have already been propagated*/
121 ASSERT_EQ(ceph_read(cb
, fdb
, in_buf
, sizeof(in_buf
), 0), 2*strlen(out_buf
)+1);
122 ASSERT_STREQ(in_buf
, "fooooooooofooooooooo");
124 /* Client a issues a write */
125 char wait_out_buf
[] = "foobarbars";
126 ASSERT_EQ((int)sizeof(wait_out_buf
), ceph_write(ca
, fda
, wait_out_buf
, sizeof(wait_out_buf
), 20));
127 ASSERT_EQ(0, ceph_lazyio_propagate(ca
, fda
, 0, 0));
129 /* Client a does not need to call ceph_lazyio_synchronize here because it is the latest writer and the writes before it have already been propagated*/
130 ASSERT_EQ(ceph_read(ca
, fda
, in_buf
, sizeof(in_buf
), 0), (2*(strlen(out_buf
)))+strlen(wait_out_buf
)+1);
131 ASSERT_STREQ(in_buf
, "fooooooooofooooooooofoobarbars");
133 /* Calling ceph_lazyio_synchronize here will invalidate client b's cache and hence enable client a to fetch the propagated write of client a in the subsequent read */
134 ASSERT_EQ(0, ceph_lazyio_synchronize(cb
, fdb
, 0, 0));
135 ASSERT_EQ(ceph_read(cb
, fdb
, in_buf
, sizeof(in_buf
), 0), (2*(strlen(out_buf
)))+strlen(wait_out_buf
)+1);
136 ASSERT_STREQ(in_buf
, "fooooooooofooooooooofoobarbars");
145 TEST(LibCephFS
, LazyIOMultipleWritersOneReader
) {
146 struct ceph_mount_info
*ca
, *cb
;
147 ASSERT_EQ(ceph_create(&ca
, NULL
), 0);
148 ASSERT_EQ(ceph_conf_read_file(ca
, NULL
), 0);
149 ASSERT_EQ(0, ceph_conf_parse_env(ca
, NULL
));
150 ASSERT_EQ(ceph_mount(ca
, NULL
), 0);
152 ASSERT_EQ(ceph_create(&cb
, NULL
), 0);
153 ASSERT_EQ(ceph_conf_read_file(cb
, NULL
), 0);
154 ASSERT_EQ(0, ceph_conf_parse_env(cb
, NULL
));
155 ASSERT_EQ(ceph_mount(cb
, NULL
), 0);
158 snprintf(name
, sizeof(name
), "foo3.%d", getpid());
160 int fda
= ceph_open(ca
, name
, O_CREAT
|O_RDWR
, 0644);
163 int fdb
= ceph_open(cb
, name
, O_RDWR
, 0644);
166 ASSERT_EQ(0, ceph_lazyio(ca
, fda
, 1));
167 ASSERT_EQ(0, ceph_lazyio(cb
, fdb
, 1));
169 char out_buf
[] = "fooooooooo";
170 /* Client a issues a write and propagates/flushes the buffer */
171 ASSERT_EQ((int)sizeof(out_buf
), ceph_write(ca
, fda
, out_buf
, sizeof(out_buf
), 0));
172 ASSERT_EQ(0, ceph_lazyio_propagate(ca
, fda
, 0, 0));
174 /* Client b issues a write and propagates/flushes the buffer*/
175 ASSERT_EQ((int)sizeof(out_buf
), ceph_write(cb
, fdb
, out_buf
, sizeof(out_buf
), 10));
176 ASSERT_EQ(0, ceph_lazyio_propagate(cb
, fdb
, 0, 0));
179 /* Client a reads the file and verifies that it only reads it's propagated writes and not Client b's*/
180 ASSERT_EQ(ceph_read(ca
, fda
, in_buf
, sizeof(in_buf
), 0), strlen(out_buf
)+1);
181 ASSERT_STREQ(in_buf
, "fooooooooo");
183 /* Client a reads the file again, this time with a lazyio_synchronize to check if the cache gets invalidated and data is refetched i.e all the propagated writes are being read*/
184 ASSERT_EQ(0, ceph_lazyio_synchronize(ca
, fda
, 0, 0));
185 ASSERT_EQ(ceph_read(ca
, fda
, in_buf
, sizeof(in_buf
), 0), 2*strlen(out_buf
)+1);
186 ASSERT_STREQ(in_buf
, "fooooooooofooooooooo");
195 TEST(LibCephFS
, LazyIOSynchronizeFlush
) {
196 /* Test to make sure lazyio_synchronize flushes dirty buffers */
197 struct ceph_mount_info
*ca
, *cb
;
198 ASSERT_EQ(ceph_create(&ca
, NULL
), 0);
199 ASSERT_EQ(ceph_conf_read_file(ca
, NULL
), 0);
200 ASSERT_EQ(0, ceph_conf_parse_env(ca
, NULL
));
201 ASSERT_EQ(ceph_mount(ca
, NULL
), 0);
203 ASSERT_EQ(ceph_create(&cb
, NULL
), 0);
204 ASSERT_EQ(ceph_conf_read_file(cb
, NULL
), 0);
205 ASSERT_EQ(0, ceph_conf_parse_env(cb
, NULL
));
206 ASSERT_EQ(ceph_mount(cb
, NULL
), 0);
209 snprintf(name
, sizeof(name
), "foo4.%d", getpid());
211 int fda
= ceph_open(ca
, name
, O_CREAT
|O_RDWR
, 0644);
214 int fdb
= ceph_open(cb
, name
, O_RDWR
, 0644);
217 ASSERT_EQ(0, ceph_lazyio(ca
, fda
, 1));
218 ASSERT_EQ(0, ceph_lazyio(cb
, fdb
, 1));
220 char out_buf
[] = "fooooooooo";
222 /* Client a issues a write and propagates it*/
223 ASSERT_EQ((int)sizeof(out_buf
), ceph_write(ca
, fda
, out_buf
, sizeof(out_buf
), 0));
224 ASSERT_EQ(0, ceph_lazyio_propagate(ca
, fda
, 0, 0));
226 /* Client b issues writes and without lazyio_propagate*/
227 ASSERT_EQ((int)sizeof(out_buf
), ceph_write(cb
, fdb
, out_buf
, sizeof(out_buf
), 10));
228 ASSERT_EQ((int)sizeof(out_buf
), ceph_write(cb
, fdb
, out_buf
, sizeof(out_buf
), 20));
231 /* Calling ceph_lazyio_synchronize here will first flush the possibly pending buffered write of client b and invalidate client b's cache and hence enable client b to fetch all the propagated writes */
232 ASSERT_EQ(0, ceph_lazyio_synchronize(cb
, fdb
, 0, 0));
233 ASSERT_EQ(ceph_read(cb
, fdb
, in_buf
, sizeof(in_buf
), 0), 3*strlen(out_buf
)+1);
234 ASSERT_STREQ(in_buf
, "fooooooooofooooooooofooooooooo");
236 /* Required to call ceph_lazyio_synchronize here since client b is the latest writer and client a is out of sync with updated file*/
237 ASSERT_EQ(0, ceph_lazyio_synchronize(ca
, fda
, 0, 0));
238 ASSERT_EQ(ceph_read(ca
, fda
, in_buf
, sizeof(in_buf
), 0), 3*strlen(out_buf
)+1);
239 ASSERT_STREQ(in_buf
, "fooooooooofooooooooofooooooooo");
248 TEST(LibCephFS
, WithoutandWithLazyIO
) {
249 struct ceph_mount_info
*ca
, *cb
;
250 ASSERT_EQ(ceph_create(&ca
, NULL
), 0);
251 ASSERT_EQ(ceph_conf_read_file(ca
, NULL
), 0);
252 ASSERT_EQ(0, ceph_conf_parse_env(ca
, NULL
));
253 ASSERT_EQ(ceph_mount(ca
, NULL
), 0);
255 ASSERT_EQ(ceph_create(&cb
, NULL
), 0);
256 ASSERT_EQ(ceph_conf_read_file(cb
, NULL
), 0);
257 ASSERT_EQ(0, ceph_conf_parse_env(cb
, NULL
));
258 ASSERT_EQ(ceph_mount(cb
, NULL
), 0);
261 snprintf(name
, sizeof(name
), "foo5.%d", getpid());
263 int fda
= ceph_open(ca
, name
, O_CREAT
|O_RDWR
, 0644);
266 int fdb
= ceph_open(cb
, name
, O_RDWR
, 0644);
269 char out_buf_w
[] = "1234567890";
270 /* Doing some non lazyio writes and read*/
271 ASSERT_EQ((int)sizeof(out_buf_w
), ceph_write(ca
, fda
, out_buf_w
, sizeof(out_buf_w
), 0));
273 ASSERT_EQ((int)sizeof(out_buf_w
), ceph_write(cb
, fdb
, out_buf_w
, sizeof(out_buf_w
), 10));
276 ASSERT_EQ(ceph_read(ca
, fda
, in_buf_w
, sizeof(in_buf_w
), 0), 2*strlen(out_buf_w
)+1);
279 ASSERT_EQ(0, ceph_lazyio(ca
, fda
, 1));
280 ASSERT_EQ(0, ceph_lazyio(cb
, fdb
, 1));
282 char out_buf
[] = "fooooooooo";
284 /* Client a issues a write and propagates/flushes the buffer*/
285 ASSERT_EQ((int)sizeof(out_buf
), ceph_write(ca
, fda
, out_buf
, sizeof(out_buf
), 20));
286 ASSERT_EQ(0, ceph_lazyio_propagate(ca
, fda
, 0, 0));
288 /* Client b issues a write and propagates/flushes the buffer*/
289 ASSERT_EQ((int)sizeof(out_buf
), ceph_write(cb
, fdb
, out_buf
, sizeof(out_buf
), 30));
290 ASSERT_EQ(0, ceph_lazyio_propagate(cb
, fdb
, 0, 0));
293 /* Calling ceph_lazyio_synchronize here will invalidate client a's cache and hence enable client a to fetch the propagated writes of client b in the subsequent read */
294 ASSERT_EQ(0, ceph_lazyio_synchronize(ca
, fda
, 0, 0));
295 ASSERT_EQ(ceph_read(ca
, fda
, in_buf
, sizeof(in_buf
), 0), (2*(strlen(out_buf
)))+(2*(strlen(out_buf_w
)))+1);
296 ASSERT_STREQ(in_buf
, "12345678901234567890fooooooooofooooooooo");
298 /* Client b does not need to call ceph_lazyio_synchronize here because it is the latest writer and the writes before it have already been propagated*/
299 ASSERT_EQ(ceph_read(cb
, fdb
, in_buf
, sizeof(in_buf
), 0), (2*(strlen(out_buf
)))+(2*(strlen(out_buf_w
)))+1);
300 ASSERT_STREQ(in_buf
, "12345678901234567890fooooooooofooooooooo");
309 static int update_root_mode()
311 struct ceph_mount_info
*admin
;
312 int r
= ceph_create(&admin
, NULL
);
315 ceph_conf_read_file(admin
, NULL
);
316 ceph_conf_parse_env(admin
, NULL
);
317 ceph_conf_set(admin
, "client_permissions", "false");
318 r
= ceph_mount(admin
, "/");
321 r
= ceph_chmod(admin
, "/", 0777);
323 ceph_shutdown(admin
);
327 int main(int argc
, char **argv
)
329 int r
= update_root_mode();
333 ::testing::InitGoogleTest(&argc
, argv
);
337 r
= rados_create(&cluster
, NULL
);
341 r
= rados_conf_read_file(cluster
, NULL
);
345 rados_conf_parse_env(cluster
, NULL
);
346 r
= rados_connect(cluster
);
352 rados_shutdown(cluster
);