Skip to content

Commit 72b6c7f

Browse files
committed
libcephfs / client: wire up file blockdiff
Fixes: http://tracker.ceph.com/issues/69791 Signed-off-by: Venky Shankar <[email protected]>
1 parent 8cc05b1 commit 72b6c7f

File tree

5 files changed

+323
-0
lines changed

5 files changed

+323
-0
lines changed

src/client/Client.cc

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9872,6 +9872,144 @@ int Client::readdirplus_r(dir_result_t *d, struct dirent *de,
98729872
return 0;
98739873
}
98749874

9875+
static void cleanup_state(Client *client, struct scan_state_t *sst)
9876+
{
9877+
if (sst->fd1 != -1) {
9878+
client->_close(sst->fd1);
9879+
}
9880+
if (sst->fd2 != -1) {
9881+
client->_close(sst->fd2);
9882+
}
9883+
delete sst;
9884+
}
9885+
9886+
int Client::file_blockdiff_init_state(const char* path1, const char* path2,
9887+
const UserPerm &perms, struct scan_state_t **state)
9888+
{
9889+
ldout(cct, 20) << __func__ << dendl;
9890+
9891+
InodeRef inode1, inode2;
9892+
scan_state_t *sst = new scan_state_t();
9893+
sst->fd1 = sst->fd2 = -1;
9894+
9895+
/*
9896+
* lets have a constraint that both snapshot paths should be
9897+
* present - otherwise the caller should do a full copy or a
9898+
* delete on the path.
9899+
*/
9900+
int r = open(path1, O_RDONLY, perms, 0);
9901+
if (r < 0) {
9902+
return r;
9903+
}
9904+
sst->fd1 = r;
9905+
9906+
r = open(path2, O_RDONLY, perms, 0);
9907+
if (r < 0) {
9908+
cleanup_state(this, sst);
9909+
return r;
9910+
}
9911+
sst->fd2 = r;
9912+
9913+
std::unique_lock lock(client_lock);
9914+
r = get_fd_inode(sst->fd1, &inode1);
9915+
if (r < 0) {
9916+
cleanup_state(this, sst);
9917+
return r;
9918+
}
9919+
r = get_fd_inode(sst->fd2, &inode2);
9920+
if (r < 0) {
9921+
cleanup_state(this, sst);
9922+
return r;
9923+
}
9924+
9925+
ldout(cct, 20) << __func__ << ": (snapid1, ino1, size)=(" << inode1->snapid
9926+
<< "," << std::hex << inode1->ino << std::dec << ","
9927+
<< inode1->size <<")" << " (snapid2, ino2, size)=("
9928+
<< inode2->snapid << "," << std::hex << inode2->ino << std::dec
9929+
<< "," << inode2->size << ")" << dendl;
9930+
if (inode1->ino != inode2->ino) {
9931+
cleanup_state(this, sst);
9932+
return -EINVAL;
9933+
}
9934+
9935+
sst->index = 0;
9936+
*state = sst;
9937+
return 0;
9938+
}
9939+
9940+
int Client::file_blockdiff_finish(struct scan_state_t *state)
9941+
{
9942+
std::unique_lock lock(client_lock);
9943+
9944+
_close(state->fd1);
9945+
_close(state->fd2);
9946+
delete state;
9947+
return 0;
9948+
}
9949+
9950+
int Client::file_blockdiff(struct scan_state_t *state, const UserPerm &perms,
9951+
std::vector<std::pair<uint64_t,uint64_t>> *blocks)
9952+
{
9953+
RWRef_t mref_reader(mount_state, CLIENT_MOUNTING);
9954+
if (!mref_reader.is_state_satisfied()) {
9955+
return -ENOTCONN;
9956+
}
9957+
9958+
ldout(cct, 20) << __func__ << dendl;
9959+
9960+
InodeRef inode1;
9961+
InodeRef inode2;
9962+
9963+
std::unique_lock lock(client_lock);
9964+
9965+
int r = get_fd_inode(state->fd1, &inode1);
9966+
if (r < 0) {
9967+
return r;
9968+
}
9969+
r = get_fd_inode(state->fd2, &inode2);
9970+
if (r < 0) {
9971+
return r;
9972+
}
9973+
9974+
ceph_assert(inode1->ino == inode2->ino);
9975+
9976+
MetaRequest *req = new MetaRequest(CEPH_MDS_OP_FILE_BLOCKDIFF);
9977+
9978+
filepath path1, path2;
9979+
inode1->make_nosnap_relative_path(path1);
9980+
req->set_filepath(path1);
9981+
9982+
inode2->make_nosnap_relative_path(path2);
9983+
req->set_filepath2(path2);
9984+
req->set_inode(inode2.get());
9985+
9986+
req->head.args.blockdiff.scan_idx = state->index;
9987+
req->head.args.blockdiff.max_objects =
9988+
cct->_conf.get_val<uint64_t>("client_file_blockdiff_max_concurrent_object_scans");
9989+
9990+
bufferlist bl;
9991+
r = make_request(req, perms, nullptr, nullptr, -1, &bl);
9992+
ldout(cct, 10) << __func__ << ": result=" << r << dendl;
9993+
9994+
if (r < 0) {
9995+
return r;
9996+
}
9997+
9998+
BlockDiff block_diff;
9999+
auto p = bl.cbegin();
10000+
decode(block_diff, p);
10001+
10002+
ldout(cct, 10) << __func__ << ": block_diff=" << block_diff << dendl;
10003+
if (!block_diff.blocks.empty()) {
10004+
for (auto &block : block_diff.blocks) {
10005+
blocks->emplace_back(std::make_pair(block.first, block.second));
10006+
}
10007+
}
10008+
10009+
state->index = block_diff.scan_idx;
10010+
return block_diff.rval;
10011+
}
10012+
987510013
int Client::readdir_snapdiff(dir_result_t* d1, snapid_t snap2,
987610014
struct dirent* out_de,
987710015
snapid_t* out_snap)

src/client/Client.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ class ceph_lock_state_t;
147147
// ========================================================
148148
// client interface
149149

150+
struct scan_state_t {
151+
int fd1;
152+
int fd2;
153+
uint64_t index;
154+
};
155+
150156
struct dir_result_t {
151157
static const int SHIFT = 28;
152158
static const int64_t MASK = (1 << SHIFT) - 1;
@@ -369,6 +375,12 @@ class Client : public Dispatcher, public md_config_obs_t {
369375
int readdir_r(dir_result_t *dirp, struct dirent *de);
370376
int readdirplus_r(dir_result_t *dirp, struct dirent *de, struct ceph_statx *stx, unsigned want, unsigned flags, Inode **out);
371377

378+
int file_blockdiff_init_state(const char *path1, const char *path2,
379+
const UserPerm &perms, struct scan_state_t **state);
380+
int file_blockdiff(struct scan_state_t *state, const UserPerm &perms,
381+
std::vector<std::pair<uint64_t,uint64_t>> *blocks);
382+
int file_blockdiff_finish(struct scan_state_t *state);
383+
372384
/*
373385
* Get the next snapshot delta entry.
374386
*

src/common/options/mds-client.yaml.in

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,3 +589,12 @@ options:
589589
- mds_client
590590
flags:
591591
- runtime
592+
- name: client_file_blockdiff_max_concurrent_object_scans
593+
type: uint
594+
level: advanced
595+
desc: maximum number of concurrent object scans
596+
long_desc: Maximum number of concurrent listsnaps operations sent to RADOS.
597+
default: 16
598+
services:
599+
- mds_client
600+
min: 1

src/include/cephfs/libcephfs.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,71 @@ struct ceph_snapdiff_info
648648
// doesn't exist in the second snapshot
649649
};
650650

651+
struct ceph_file_blockdiff_result;
652+
653+
// blockdiff stream handle
654+
struct ceph_file_blockdiff_info
655+
{
656+
struct ceph_mount_info* cmount;
657+
struct ceph_file_blockdiff_result* blockp;
658+
};
659+
660+
// set of file block diff's
661+
struct cblock
662+
{
663+
uint64_t offset;
664+
uint64_t len;
665+
};
666+
struct ceph_file_blockdiff_changedblocks
667+
{
668+
uint64_t num_blocks;
669+
struct cblock *b;
670+
};
671+
672+
/**
673+
* Initialize blockdiff stream to get file block deltas.
674+
*
675+
* @param cmount the ceph mount handle to use for snapdiff retrieval.
676+
* @param root_path root path for snapshots-in-question
677+
* @param rel_path subpath under the root to build delta for
678+
* @param snap1 the first snapshot name
679+
* @param snap2 the second snapshot name
680+
* @param out_info resulting blockdiff stream handle to be used for blokdiff results
681+
retrieval via ceph_file_blockdiff().
682+
* @returns 0 on success and negative error code otherwise
683+
*/
684+
int ceph_file_blockdiff_init(struct ceph_mount_info* cmount,
685+
const char* root_path,
686+
const char* rel_path,
687+
const char* snap1,
688+
const char* snap2,
689+
struct ceph_file_blockdiff_info* out_info);
690+
691+
/**
692+
* Get a set of file blockdiff's
693+
*
694+
* @param info blockdiff stream handle
695+
* @param blocks next set of file blockdiff's (offset, length)
696+
* @returns 0 or 1 on success and negative error code otherwise
697+
*/
698+
int ceph_file_blockdiff(struct ceph_file_blockdiff_info* info,
699+
struct ceph_file_blockdiff_changedblocks* blocks);
700+
/**
701+
* Free blockdiff buffer
702+
*
703+
* @param blocks file block diff's from ceph_file_blockdiff()
704+
* @returns None
705+
*/
706+
void ceph_free_file_blockdiff_buffer(struct ceph_file_blockdiff_changedblocks* blocks);
707+
708+
/**
709+
* Close blockdiff stream
710+
*
711+
* @param info blockdiff stream handle
712+
* @returns 0 on success and negative error code otherwise
713+
*/
714+
int ceph_file_blockdiff_finish(struct ceph_file_blockdiff_info* info);
715+
651716
/**
652717
* Opens snapdiff stream to get snapshots delta (aka snapdiff).
653718
*

src/libcephfs.cc

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,105 @@ extern "C" int ceph_readdirplus_r(struct ceph_mount_info *cmount, struct ceph_di
739739
return cmount->get_client()->readdirplus_r(reinterpret_cast<dir_result_t*>(dirp), de, stx, want, flags, out);
740740
}
741741

742+
extern "C" int ceph_file_blockdiff_init(struct ceph_mount_info* cmount,
743+
const char* root_path,
744+
const char* rel_path,
745+
const char* snap1,
746+
const char* snap2,
747+
struct ceph_file_blockdiff_info* out_info)
748+
{
749+
if (!cmount->is_mounted()) {
750+
return -ENOTCONN;
751+
}
752+
if (!out_info || !root_path || !rel_path ||
753+
!snap1 || !*snap1 || !snap2 || !*snap2) {
754+
return -EINVAL;
755+
}
756+
757+
char snapdir[PATH_MAX];
758+
cmount->conf_get("client_snapdir", snapdir, sizeof(snapdir) - 1);
759+
760+
char path1[PATH_MAX];
761+
char path2[PATH_MAX];
762+
// construct snapshot paths for the files
763+
int n = snprintf(path1, PATH_MAX, "%s/%s/%s/%s",
764+
root_path, snapdir, snap1, rel_path);
765+
if (n < 0 || n == PATH_MAX) {
766+
errno = ENAMETOOLONG;
767+
return -errno;
768+
}
769+
n = snprintf(path2, PATH_MAX, "%s/%s/%s/%s",
770+
root_path, snapdir, snap2, rel_path);
771+
if (n < 0 || n == PATH_MAX) {
772+
return -ENAMETOOLONG;
773+
}
774+
775+
int r = cmount->get_client()->file_blockdiff_init_state(path1, path2,
776+
cmount->default_perms,
777+
(struct scan_state_t **)&(out_info->blockp));
778+
if (r < 0) {
779+
return r;
780+
}
781+
782+
out_info->cmount = cmount;
783+
return 0;
784+
}
785+
786+
extern "C" int ceph_file_blockdiff(struct ceph_file_blockdiff_info* info,
787+
struct ceph_file_blockdiff_changedblocks* blocks)
788+
{
789+
if (!info->cmount->is_mounted()) {
790+
return -ENOTCONN;
791+
}
792+
793+
std::vector<std::pair<uint64_t,uint64_t>> _blocks;
794+
struct scan_state_t *state = reinterpret_cast<scan_state_t *>(info->blockp);
795+
796+
int r = info->cmount->get_client()->file_blockdiff(state, info->cmount->default_perms, &_blocks);
797+
if (r < 0) {
798+
return r;
799+
}
800+
801+
blocks->b = NULL;
802+
blocks->num_blocks = _blocks.size();
803+
if (blocks->num_blocks) {
804+
struct cblock *b = (struct cblock *)calloc(blocks->num_blocks, sizeof(struct cblock));
805+
if (!b) {
806+
return -ENOMEM;
807+
}
808+
809+
struct cblock *_b = b;
810+
for (auto &_block : _blocks) {
811+
_b->offset = _block.first;
812+
_b->len = _block.second;
813+
++_b;
814+
}
815+
816+
blocks->b = b;
817+
}
818+
819+
return r;
820+
}
821+
822+
extern "C" void ceph_free_file_blockdiff_buffer(struct ceph_file_blockdiff_changedblocks* blocks)
823+
{
824+
if (blocks->b) {
825+
free(blocks->b);
826+
}
827+
blocks->num_blocks = 0;
828+
blocks->b = NULL;
829+
}
830+
831+
extern "C" int ceph_file_blockdiff_finish(struct ceph_file_blockdiff_info* info)
832+
{
833+
if (!info->cmount->is_mounted()) {
834+
return -ENOTCONN;
835+
}
836+
837+
struct scan_state_t *state = reinterpret_cast<scan_state_t *>(info->blockp);
838+
return info->cmount->get_client()->file_blockdiff_finish(state);
839+
}
840+
742841
extern "C" int ceph_open_snapdiff(struct ceph_mount_info* cmount,
743842
const char* root_path,
744843
const char* rel_path,

0 commit comments

Comments
 (0)