Skip to content

Commit fa6e884

Browse files
committed
Merge PR ceph#58564 into main
* refs/pull/58564/head: client: clamp sizes to INT_MAX in sync i/o code paths client: restrict bufferlist to total write size src/test: test sync/async i/o code paths with huge (4GiB) buffers Reviewed-by: Venky Shankar <[email protected]> Reviewed-by: Kotresh Hiremath Ravishankar <[email protected]> Reviewed-by: Christopher Hoffman <[email protected]>
2 parents bffa446 + d2699c4 commit fa6e884

File tree

4 files changed

+167
-26
lines changed

4 files changed

+167
-26
lines changed

src/client/Client.cc

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11474,7 +11474,9 @@ int Client::write(int fd, const char *buf, loff_t size, loff_t offset)
1147411474
#endif
1147511475
/* We can't return bytes written larger than INT_MAX, clamp size to that */
1147611476
size = std::min(size, (loff_t)INT_MAX);
11477-
int r = _write(fh, offset, size, buf, NULL, false);
11477+
bufferlist bl;
11478+
bl.append(buf, size);
11479+
int r = _write(fh, offset, size, std::move(bl));
1147811480
ldout(cct, 3) << "write(" << fd << ", \"...\", " << size << ", " << offset << ") = " << r << dendl;
1147911481
return r;
1148011482
}
@@ -11499,7 +11501,7 @@ int64_t Client::_preadv_pwritev_locked(Fh *fh, const struct iovec *iov,
1149911501
if(iovcnt < 0) {
1150011502
return -EINVAL;
1150111503
}
11502-
loff_t totallen = 0;
11504+
size_t totallen = 0;
1150311505
for (int i = 0; i < iovcnt; i++) {
1150411506
totallen += iov[i].iov_len;
1150511507
}
@@ -11509,12 +11511,29 @@ int64_t Client::_preadv_pwritev_locked(Fh *fh, const struct iovec *iov,
1150911511
* 32-bit signed integers. Clamp the I/O sizes in those functions so that
1151011512
* we don't do I/Os larger than the values we can return.
1151111513
*/
11514+
bufferlist data;
1151211515
if (clamp_to_int) {
11513-
totallen = std::min(totallen, (loff_t)INT_MAX);
11516+
totallen = std::min(totallen, (size_t)INT_MAX);
11517+
size_t total_appended = 0;
11518+
for (int i = 0; i < iovcnt; i++) {
11519+
if (iov[i].iov_len > 0) {
11520+
if (total_appended + iov[i].iov_len >= totallen) {
11521+
data.append((const char *)iov[i].iov_base, totallen - total_appended);
11522+
break;
11523+
} else {
11524+
data.append((const char *)iov[i].iov_base, iov[i].iov_len);
11525+
total_appended += iov[i].iov_len;
11526+
}
11527+
}
11528+
}
11529+
} else {
11530+
for (int i = 0; i < iovcnt; i++) {
11531+
data.append((const char *)iov[i].iov_base, iov[i].iov_len);
11532+
}
1151411533
}
1151511534

1151611535
if (write) {
11517-
int64_t w = _write(fh, offset, totallen, NULL, iov, iovcnt, onfinish, do_fsync, syncdataonly);
11536+
int64_t w = _write(fh, offset, totallen, std::move(data), onfinish, do_fsync, syncdataonly);
1151811537
ldout(cct, 3) << "pwritev(" << fh << ", \"...\", " << totallen << ", " << offset << ") = " << w << dendl;
1151911538
return w;
1152011539
} else {
@@ -11698,9 +11717,8 @@ bool Client::C_Write_Finisher::try_complete()
1169811717
return false;
1169911718
}
1170011719

11701-
int64_t Client::_write(Fh *f, int64_t offset, uint64_t size, const char *buf,
11702-
const struct iovec *iov, int iovcnt, Context *onfinish,
11703-
bool do_fsync, bool syncdataonly)
11720+
int64_t Client::_write(Fh *f, int64_t offset, uint64_t size, bufferlist bl,
11721+
Context *onfinish, bool do_fsync, bool syncdataonly)
1170411722
{
1170511723
ceph_assert(ceph_mutex_is_locked_by_me(client_lock));
1170611724

@@ -11770,19 +11788,6 @@ int64_t Client::_write(Fh *f, int64_t offset, uint64_t size, const char *buf,
1177011788
ceph_assert(in->inline_version > 0);
1177111789
}
1177211790

11773-
// copy into fresh buffer (since our write may be resub, async)
11774-
bufferlist bl;
11775-
if (buf) {
11776-
if (size > 0)
11777-
bl.append(buf, size);
11778-
} else if (iov){
11779-
for (int i = 0; i < iovcnt; i++) {
11780-
if (iov[i].iov_len > 0) {
11781-
bl.append((const char *)iov[i].iov_base, iov[i].iov_len);
11782-
}
11783-
}
11784-
}
11785-
1178611791
int want, have;
1178711792
if (f->mode & CEPH_FILE_MODE_LAZY)
1178811793
want = CEPH_CAP_FILE_BUFFER | CEPH_CAP_FILE_LAZYIO;
@@ -16149,7 +16154,9 @@ int Client::ll_write(Fh *fh, loff_t off, loff_t len, const char *data)
1614916154
tout(cct) << off << std::endl;
1615016155
tout(cct) << len << std::endl;
1615116156

16152-
int r = _write(fh, off, len, data, NULL, 0);
16157+
bufferlist bl;
16158+
bl.append(data, len);
16159+
int r = _write(fh, off, len, std::move(bl));
1615316160
ldout(cct, 3) << "ll_write " << fh << " " << off << "~" << len << " = " << r
1615416161
<< dendl;
1615516162
return r;
@@ -16167,7 +16174,7 @@ int64_t Client::ll_writev(struct Fh *fh, const struct iovec *iov, int iovcnt, in
1616716174
ldout(cct, 3) << "(fh)" << fh << " is invalid" << dendl;
1616816175
return -EBADF;
1616916176
}
16170-
return _preadv_pwritev_locked(fh, iov, iovcnt, off, true, false);
16177+
return _preadv_pwritev_locked(fh, iov, iovcnt, off, true, true);
1617116178
}
1617216179

1617316180
int64_t Client::ll_readv(struct Fh *fh, const struct iovec *iov, int iovcnt, int64_t off)
@@ -16182,7 +16189,7 @@ int64_t Client::ll_readv(struct Fh *fh, const struct iovec *iov, int iovcnt, int
1618216189
ldout(cct, 3) << "(fh)" << fh << " is invalid" << dendl;
1618316190
return -EBADF;
1618416191
}
16185-
return _preadv_pwritev_locked(fh, iov, iovcnt, off, false, false);
16192+
return _preadv_pwritev_locked(fh, iov, iovcnt, off, false, true);
1618616193
}
1618716194

1618816195
int64_t Client::ll_preadv_pwritev(struct Fh *fh, const struct iovec *iov,

src/client/Client.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,9 +1758,9 @@ class Client : public Dispatcher, public md_config_obs_t {
17581758
void do_readahead(Fh *f, Inode *in, uint64_t off, uint64_t len);
17591759
int64_t _write_success(Fh *fh, utime_t start, uint64_t fpos,
17601760
int64_t offset, uint64_t size, Inode *in);
1761-
int64_t _write(Fh *fh, int64_t offset, uint64_t size, const char *buf,
1762-
const struct iovec *iov, int iovcnt, Context *onfinish = nullptr,
1763-
bool do_fsync = false, bool syncdataonly = false);
1761+
int64_t _write(Fh *fh, int64_t offset, uint64_t size, bufferlist bl,
1762+
Context *onfinish = nullptr, bool do_fsync = false,
1763+
bool syncdataonly = false);
17641764
int64_t _preadv_pwritev_locked(Fh *fh, const struct iovec *iov,
17651765
int iovcnt, int64_t offset,
17661766
bool write, bool clamp_to_int,

src/test/client/nonblocking.cc

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <string>
1919

2020
#include <fmt/format.h>
21+
#include <sys/statvfs.h>
2122

2223
#include "test/client/TestClient.h"
2324

@@ -716,3 +717,74 @@ TEST_F(TestClient, LlreadvContiguousLlwritevNonContiguous) {
716717
client->ll_release(fh);
717718
ASSERT_EQ(0, client->ll_unlink(root, filename, myperm));
718719
}
720+
721+
TEST_F(TestClient, LlreadvLlwritevLargeBuffers) {
722+
/* Test that async I/O code paths handle large buffers (total len >= 4GiB)*/
723+
int mypid = getpid();
724+
char filename[256];
725+
726+
client->unmount();
727+
TearDown();
728+
SetUp();
729+
730+
sprintf(filename, "test_llreadvllwritevlargebuffers%u", mypid);
731+
732+
Inode *root, *file;
733+
root = client->get_root();
734+
ASSERT_NE(root, (Inode *)NULL);
735+
736+
Fh *fh;
737+
struct ceph_statx stx;
738+
739+
ASSERT_EQ(0, client->ll_createx(root, filename, 0666,
740+
O_RDWR | O_CREAT | O_TRUNC,
741+
&file, &fh, &stx, 0, 0, myperm));
742+
743+
struct statvfs stbuf;
744+
int64_t rc;
745+
rc = client->ll_statfs(root, &stbuf, myperm);
746+
ASSERT_EQ(rc, 0);
747+
int64_t fs_available_space = stbuf.f_bfree * stbuf.f_bsize;
748+
ASSERT_GT(fs_available_space, 0);
749+
750+
const size_t BUFSIZE = (size_t)INT_MAX + 1;
751+
int64_t bytes_written = 0, bytes_read = 0;
752+
753+
C_SaferCond writefinish;
754+
C_SaferCond readfinish;
755+
756+
auto out_buf_0 = std::make_unique<char[]>(BUFSIZE);
757+
memset(out_buf_0.get(), 0xDD, BUFSIZE);
758+
auto out_buf_1 = std::make_unique<char[]>(BUFSIZE);
759+
memset(out_buf_1.get(), 0xFF, BUFSIZE);
760+
761+
struct iovec iov_out[2] = {
762+
{out_buf_0.get(), BUFSIZE},
763+
{out_buf_1.get(), BUFSIZE}
764+
};
765+
766+
bufferlist bl;
767+
auto in_buf_0 = std::make_unique<char[]>(BUFSIZE);
768+
auto in_buf_1 = std::make_unique<char[]>(BUFSIZE);
769+
770+
struct iovec iov_in[2] = {
771+
{in_buf_0.get(), BUFSIZE},
772+
{in_buf_1.get(), BUFSIZE}
773+
};
774+
775+
rc = client->ll_preadv_pwritev(fh, iov_out, 2, 0, true, &writefinish,
776+
nullptr);
777+
ASSERT_EQ(rc, 0);
778+
bytes_written = writefinish.wait();
779+
// total write length is clamped to INT_MAX in write paths
780+
ASSERT_EQ(bytes_written, INT_MAX);
781+
782+
rc = client->ll_preadv_pwritev(fh, iov_in, 2, 0, false, &readfinish, &bl);
783+
ASSERT_EQ(rc, 0);
784+
bytes_read = readfinish.wait();
785+
// total read length is clamped to INT_MAX in read paths
786+
ASSERT_EQ(bytes_read, INT_MAX);
787+
788+
client->ll_release(fh);
789+
ASSERT_EQ(0, client->ll_unlink(root, filename, myperm));
790+
}

src/test/client/syncio.cc

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <string>
1919

2020
#include <fmt/format.h>
21+
#include <sys/statvfs.h>
2122

2223
#include "test/client/TestClient.h"
2324

@@ -77,3 +78,64 @@ TEST_F(TestClient, LlreadvLlwritevInvalidFileHandleSync) {
7778
rc = client->ll_readv(fh, iov_in, 2, 0);
7879
ASSERT_EQ(rc, -EBADF);
7980
}
81+
82+
TEST_F(TestClient, LlreadvLlwritevLargeBuffersSync) {
83+
/* Test that sync I/O code paths handle large buffers (total len >= 4GiB)*/
84+
int mypid = getpid();
85+
char filename[256];
86+
87+
client->unmount();
88+
TearDown();
89+
SetUp();
90+
91+
sprintf(filename, "test_llreadvllwritevlargebufferssync%u", mypid);
92+
93+
Inode *root, *file;
94+
root = client->get_root();
95+
ASSERT_NE(root, (Inode *)NULL);
96+
97+
Fh *fh;
98+
struct ceph_statx stx;
99+
100+
ASSERT_EQ(0, client->ll_createx(root, filename, 0666,
101+
O_RDWR | O_CREAT | O_TRUNC,
102+
&file, &fh, &stx, 0, 0, myperm));
103+
104+
struct statvfs stbuf;
105+
int64_t rc;
106+
const size_t BUFSIZE = (size_t)INT_MAX + 1;
107+
rc = client->ll_statfs(root, &stbuf, myperm);
108+
ASSERT_EQ(rc, 0);
109+
int64_t fs_available_space = stbuf.f_bfree * stbuf.f_bsize;
110+
ASSERT_GT(fs_available_space, BUFSIZE * 2);
111+
112+
auto out_buf_0 = std::make_unique<char[]>(BUFSIZE);
113+
memset(out_buf_0.get(), 0xDD, BUFSIZE);
114+
auto out_buf_1 = std::make_unique<char[]>(BUFSIZE);
115+
memset(out_buf_1.get(), 0xFF, BUFSIZE);
116+
117+
struct iovec iov_out[2] = {
118+
{out_buf_0.get(), BUFSIZE},
119+
{out_buf_1.get(), BUFSIZE}
120+
};
121+
122+
bufferlist bl;
123+
auto in_buf_0 = std::make_unique<char[]>(BUFSIZE);
124+
auto in_buf_1 = std::make_unique<char[]>(BUFSIZE);
125+
126+
struct iovec iov_in[2] = {
127+
{in_buf_0.get(), BUFSIZE},
128+
{in_buf_1.get(), BUFSIZE}
129+
};
130+
131+
rc = client->ll_writev(fh, iov_out, 2, 0);
132+
// total write length is clamped to INT_MAX in write paths
133+
ASSERT_EQ(rc, INT_MAX);
134+
135+
rc = client->ll_readv(fh, iov_in, 2, 0);
136+
// total write length is clamped to INT_MAX in write paths
137+
ASSERT_EQ(rc, INT_MAX);
138+
139+
client->ll_release(fh);
140+
ASSERT_EQ(0, client->ll_unlink(root, filename, myperm));
141+
}

0 commit comments

Comments
 (0)