Skip to content

Commit 9a7645e

Browse files
committed
client: fix file cache cap leak which can stall async read call
We need the `Fc` cap ref when ObjectCacher::file_read returns zero bytes; then wait for the I/O to finish; then do readahead but this cap ref needs to be put down as well at the right place. The problem with the current implementation is that it is difficult to decide whether to revoke the `Fc` cap ref in Client::C_Read_Async_Finisher::finish or not since the destructor cannot differenciate between the cases as it is fed with the bytes that are read after waiting for the I/O to be finished. Therefore provide `Fc` cap ref to the inode before making a call to ObjectCacher::file_read in Client::_read_async and handle revoking it: - if async read then in Client::C_Read_Async_Finisher::finish - else if sync read then within Client::_read_async right before calling Client::do_readahead. Fixes: https://tracker.ceph.com/issues/63896 Signed-off-by: Dhairya Parmar <[email protected]>
1 parent 3163be3 commit 9a7645e

File tree

1 file changed

+14
-4
lines changed

1 file changed

+14
-4
lines changed

src/client/Client.cc

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

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

11061+
// get Fc cap ref before commencing read
11062+
get_cap_ref(in, CEPH_CAP_FILE_CACHE);
11063+
1106111064
if (onfinish != nullptr) {
1106211065
io_finish.reset(new C_Read_Async_Finisher(this, onfinish, f, in,
1106311066
f->pos, off, len));
1106411067
}
1106511068

1106611069
// trim read based on file size?
1106711070
if ((off >= in->size) || (len == 0)) {
11071+
// read is requested at the EOF or the read len is zero, therefore release
11072+
// Fc cap first before proceeding further
11073+
put_cap_ref(in, CEPH_CAP_FILE_CACHE);
11074+
1106811075
// If not async, immediate return of 0 bytes
11069-
if (onfinish == nullptr)
11076+
if (onfinish == nullptr) {
1107011077
return 0;
11078+
}
1107111079

1107211080
// Release C_Read_Async_Finisher from managed pointer, we need to complete
1107311081
// immediately. The C_Read_Async_Finisher is safely handled and won't be
@@ -11100,6 +11108,8 @@ int Client::_read_async(Fh *f, uint64_t off, uint64_t len, bufferlist *bl,
1110011108
off, len, bl, 0, io_finish.get());
1110111109

1110211110
if (onfinish != nullptr) {
11111+
// put the cap ref since we're releasing C_Read_Async_Finisher
11112+
put_cap_ref(in, CEPH_CAP_FILE_CACHE);
1110311113
// Release C_Read_Async_Finisher from managed pointer, either
1110411114
// file_read will result in non-blocking complete, or we need to complete
1110511115
// immediately. In either case, the C_Read_Async_Finisher is safely
@@ -11108,19 +11118,19 @@ int Client::_read_async(Fh *f, uint64_t off, uint64_t len, bufferlist *bl,
1110811118
if (r != 0) {
1110911119
// need to do readahead, so complete the crf
1111011120
crf->complete(r);
11111-
} else {
11112-
get_cap_ref(in, CEPH_CAP_FILE_CACHE);
1111311121
}
1111411122
return 0;
1111511123
}
1111611124

11125+
// Wait for the blocking read to complete and then do readahead
1111711126
if (r == 0) {
11118-
get_cap_ref(in, CEPH_CAP_FILE_CACHE);
1111911127
client_lock.unlock();
1112011128
r = io_finish_cond->wait();
1112111129
client_lock.lock();
1112211130
put_cap_ref(in, CEPH_CAP_FILE_CACHE);
1112311131
update_read_io_size(bl->length());
11132+
} else {
11133+
put_cap_ref(in, CEPH_CAP_FILE_CACHE);
1112411134
}
1112511135

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

0 commit comments

Comments
 (0)