Skip to content

Commit fbfeb28

Browse files
committed
Merge PR ceph#55144 into main
* refs/pull/55144/head: client: fix file cache cap leak which can stall async read call test/client: test contiguous read for a non-contiguous write Reviewed-by: Venky Shankar <[email protected]> Reviewed-by: Xiubo Li <[email protected]>
2 parents 6b1d0de + 9a7645e commit fbfeb28

File tree

2 files changed

+115
-4
lines changed

2 files changed

+115
-4
lines changed

src/client/Client.cc

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11110,16 +11110,24 @@ int Client::_read_async(Fh *f, uint64_t off, uint64_t len, bufferlist *bl,
1111011110

1111111111
ldout(cct, 10) << __func__ << " " << *in << " " << off << "~" << len << dendl;
1111211112

11113+
// get Fc cap ref before commencing read
11114+
get_cap_ref(in, CEPH_CAP_FILE_CACHE);
11115+
1111311116
if (onfinish != nullptr) {
1111411117
io_finish.reset(new C_Read_Async_Finisher(this, onfinish, f, in,
1111511118
f->pos, off, len));
1111611119
}
1111711120

1111811121
// trim read based on file size?
1111911122
if ((off >= in->size) || (len == 0)) {
11123+
// read is requested at the EOF or the read len is zero, therefore release
11124+
// Fc cap first before proceeding further
11125+
put_cap_ref(in, CEPH_CAP_FILE_CACHE);
11126+
1112011127
// If not async, immediate return of 0 bytes
11121-
if (onfinish == nullptr)
11128+
if (onfinish == nullptr) {
1112211129
return 0;
11130+
}
1112311131

1112411132
// Release C_Read_Async_Finisher from managed pointer, we need to complete
1112511133
// immediately. The C_Read_Async_Finisher is safely handled and won't be
@@ -11152,6 +11160,8 @@ int Client::_read_async(Fh *f, uint64_t off, uint64_t len, bufferlist *bl,
1115211160
off, len, bl, 0, io_finish.get());
1115311161

1115411162
if (onfinish != nullptr) {
11163+
// put the cap ref since we're releasing C_Read_Async_Finisher
11164+
put_cap_ref(in, CEPH_CAP_FILE_CACHE);
1115511165
// Release C_Read_Async_Finisher from managed pointer, either
1115611166
// file_read will result in non-blocking complete, or we need to complete
1115711167
// immediately. In either case, the C_Read_Async_Finisher is safely
@@ -11160,19 +11170,19 @@ int Client::_read_async(Fh *f, uint64_t off, uint64_t len, bufferlist *bl,
1116011170
if (r != 0) {
1116111171
// need to do readahead, so complete the crf
1116211172
crf->complete(r);
11163-
} else {
11164-
get_cap_ref(in, CEPH_CAP_FILE_CACHE);
1116511173
}
1116611174
return 0;
1116711175
}
1116811176

11177+
// Wait for the blocking read to complete and then do readahead
1116911178
if (r == 0) {
11170-
get_cap_ref(in, CEPH_CAP_FILE_CACHE);
1117111179
client_lock.unlock();
1117211180
r = io_finish_cond->wait();
1117311181
client_lock.lock();
1117411182
put_cap_ref(in, CEPH_CAP_FILE_CACHE);
1117511183
update_read_io_size(bl->length());
11184+
} else {
11185+
put_cap_ref(in, CEPH_CAP_FILE_CACHE);
1117611186
}
1117711187

1117811188
do_readahead(f, in, off, len);

src/test/client/nonblocking.cc

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,3 +611,104 @@ TEST_F(TestClient, LlreadvLlwritevInvalidFileHandle) {
611611
ASSERT_EQ(bytes_read, -CEPHFS_EBADF);
612612
ASSERT_EQ(bl.length(), 0);
613613
}
614+
615+
TEST_F(TestClient, LlreadvContiguousLlwritevNonContiguous) {
616+
/* Test writing at non-contiguous memory locations, and make sure
617+
contiguous read returns bytes requested. */
618+
619+
int mypid = getpid();
620+
char filename[256];
621+
622+
client->unmount();
623+
TearDown();
624+
SetUp();
625+
626+
sprintf(filename, "test_llreadvcontiguousllwritevnoncontiguousfile%u", mypid);
627+
628+
Inode *root, *file;
629+
root = client->get_root();
630+
ASSERT_NE(root, (Inode *)NULL);
631+
632+
Fh *fh;
633+
struct ceph_statx stx;
634+
635+
ASSERT_EQ(0, client->ll_createx(root, filename, 0666,
636+
O_RDWR | O_CREAT | O_TRUNC,
637+
&file, &fh, &stx, 0, 0, myperm));
638+
639+
const int NUM_BUF = 5;
640+
char out_buf_0[] = "hello ";
641+
char out_buf_1[] = "world\n";
642+
char out_buf_2[] = "Ceph - ";
643+
char out_buf_3[] = "a scalable distributed ";
644+
char out_buf_4[] = "storage system\n";
645+
646+
struct iovec iov_out_non_contiguous[NUM_BUF] = {
647+
{out_buf_0, sizeof(out_buf_0)},
648+
{out_buf_1, sizeof(out_buf_1)},
649+
{out_buf_2, sizeof(out_buf_2)},
650+
{out_buf_3, sizeof(out_buf_3)},
651+
{out_buf_4, sizeof(out_buf_4)}
652+
};
653+
654+
char in_buf_0[sizeof(out_buf_0)];
655+
char in_buf_1[sizeof(out_buf_1)];
656+
char in_buf_2[sizeof(out_buf_2)];
657+
char in_buf_3[sizeof(out_buf_3)];
658+
char in_buf_4[sizeof(out_buf_4)];
659+
660+
struct iovec iov_in_contiguous[NUM_BUF] = {
661+
{in_buf_0, sizeof(in_buf_0)},
662+
{in_buf_1, sizeof(in_buf_1)},
663+
{in_buf_2, sizeof(in_buf_2)},
664+
{in_buf_3, sizeof(in_buf_3)},
665+
{in_buf_4, sizeof(in_buf_4)}
666+
};
667+
668+
ssize_t bytes_to_write = 0, total_bytes_written = 0, total_bytes_read = 0;
669+
for(int i = 0; i < NUM_BUF; ++i) {
670+
bytes_to_write += iov_out_non_contiguous[i].iov_len;
671+
}
672+
673+
std::unique_ptr<C_SaferCond> writefinish = nullptr;
674+
std::unique_ptr<C_SaferCond> readfinish = nullptr;
675+
676+
int64_t rc;
677+
bufferlist bl;
678+
679+
struct iovec *current_iov = iov_out_non_contiguous;
680+
681+
for(int i = 0; i < NUM_BUF; ++i) {
682+
writefinish.reset(new C_SaferCond("test-nonblocking-writefinish-non-contiguous"));
683+
rc = client->ll_preadv_pwritev(fh, current_iov++, 1, i * NUM_BUF * 100,
684+
true, writefinish.get(), nullptr);
685+
ASSERT_EQ(rc, 0);
686+
total_bytes_written += writefinish->wait();
687+
}
688+
ASSERT_EQ(total_bytes_written, bytes_to_write);
689+
690+
readfinish.reset(new C_SaferCond("test-nonblocking-readfinish-contiguous"));
691+
rc = client->ll_preadv_pwritev(fh, iov_in_contiguous, NUM_BUF, 0, false,
692+
readfinish.get(), &bl);
693+
ASSERT_EQ(rc, 0);
694+
total_bytes_read = readfinish->wait();
695+
ASSERT_EQ(total_bytes_read, bytes_to_write);
696+
ASSERT_EQ(bl.length(), bytes_to_write);
697+
698+
copy_bufferlist_to_iovec(iov_in_contiguous, NUM_BUF, &bl,
699+
total_bytes_read);
700+
/* since the iovec structures are written at gaps of 100, only the first
701+
iovec structure content should match when reading contiguously while rest
702+
of the read buffers should just be 0s(holes filled with zeros) */
703+
ASSERT_EQ(0, strncmp((const char*)iov_in_contiguous[0].iov_base,
704+
(const char*)iov_out_non_contiguous[0].iov_base,
705+
iov_out_non_contiguous[0].iov_len));
706+
for(int i = 1; i < NUM_BUF; ++i) {
707+
ASSERT_NE(0, strncmp((const char*)iov_in_contiguous[i].iov_base,
708+
(const char*)iov_out_non_contiguous[i].iov_base,
709+
iov_out_non_contiguous[i].iov_len));
710+
}
711+
712+
client->ll_release(fh);
713+
ASSERT_EQ(0, client->ll_unlink(root, filename, myperm));
714+
}

0 commit comments

Comments
 (0)