Skip to content

Commit 4a1c543

Browse files
Alex Markuzeidryomov
authored andcommitted
ceph: parse subvolume_id from InodeStat v9 and store in inode
Add support for parsing the subvolume_id field from InodeStat v9 and storing it in the inode for later use by subvolume metrics tracking. The subvolume_id identifies which CephFS subvolume an inode belongs to, enabling per-subvolume I/O metrics collection and reporting. This patch: - Adds subvolume_id field to struct ceph_mds_reply_info_in - Adds i_subvolume_id field to struct ceph_inode_info - Parses subvolume_id from v9 InodeStat in parse_reply_info_in() - Adds ceph_inode_set_subvolume() helper to propagate the ID to inodes - Initializes i_subvolume_id in inode allocation and clears on destroy Signed-off-by: Alex Markuze <amarkuze@redhat.com> Reviewed-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
1 parent e58103c commit 4a1c543

4 files changed

Lines changed: 76 additions & 14 deletions

File tree

fs/ceph/inode.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,7 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
638638

639639
ci->i_max_bytes = 0;
640640
ci->i_max_files = 0;
641+
ci->i_subvolume_id = CEPH_SUBVOLUME_ID_NONE;
641642

642643
memset(&ci->i_dir_layout, 0, sizeof(ci->i_dir_layout));
643644
memset(&ci->i_cached_layout, 0, sizeof(ci->i_cached_layout));
@@ -742,6 +743,8 @@ void ceph_evict_inode(struct inode *inode)
742743

743744
percpu_counter_dec(&mdsc->metric.total_inodes);
744745

746+
ci->i_subvolume_id = CEPH_SUBVOLUME_ID_NONE;
747+
745748
netfs_wait_for_outstanding_io(inode);
746749
truncate_inode_pages_final(&inode->i_data);
747750
if (inode_state_read_once(inode) & I_PINNING_NETFS_WB)
@@ -873,6 +876,40 @@ int ceph_fill_file_size(struct inode *inode, int issued,
873876
return queue_trunc;
874877
}
875878

879+
/*
880+
* Set the subvolume ID for an inode.
881+
*
882+
* The subvolume_id identifies which CephFS subvolume this inode belongs to.
883+
* CEPH_SUBVOLUME_ID_NONE (0) means unknown/unset - the MDS only sends
884+
* non-zero IDs for inodes within subvolumes.
885+
*
886+
* An inode's subvolume membership is immutable - once an inode is created
887+
* in a subvolume, it stays there. Therefore, if we already have a valid
888+
* (non-zero) subvolume_id and receive a different one, that indicates a bug.
889+
*/
890+
void ceph_inode_set_subvolume(struct inode *inode, u64 subvolume_id)
891+
{
892+
struct ceph_inode_info *ci;
893+
u64 old;
894+
895+
if (!inode || subvolume_id == CEPH_SUBVOLUME_ID_NONE)
896+
return;
897+
898+
ci = ceph_inode(inode);
899+
old = READ_ONCE(ci->i_subvolume_id);
900+
901+
if (old == subvolume_id)
902+
return;
903+
904+
if (old != CEPH_SUBVOLUME_ID_NONE) {
905+
/* subvolume_id should not change once set */
906+
WARN_ON_ONCE(1);
907+
return;
908+
}
909+
910+
WRITE_ONCE(ci->i_subvolume_id, subvolume_id);
911+
}
912+
876913
void ceph_fill_file_time(struct inode *inode, int issued,
877914
u64 time_warp_seq, struct timespec64 *ctime,
878915
struct timespec64 *mtime, struct timespec64 *atime)
@@ -1076,6 +1113,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
10761113
new_issued = ~issued & info_caps;
10771114

10781115
__ceph_update_quota(ci, iinfo->max_bytes, iinfo->max_files);
1116+
ceph_inode_set_subvolume(inode, iinfo->subvolume_id);
10791117

10801118
#ifdef CONFIG_FS_ENCRYPTION
10811119
if (iinfo->fscrypt_auth_len &&
@@ -1583,6 +1621,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
15831621
goto done;
15841622
}
15851623
if (parent_dir) {
1624+
ceph_inode_set_subvolume(parent_dir,
1625+
rinfo->diri.subvolume_id);
15861626
err = ceph_fill_inode(parent_dir, NULL, &rinfo->diri,
15871627
rinfo->dirfrag, session, -1,
15881628
&req->r_caps_reservation);
@@ -1671,6 +1711,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
16711711
BUG_ON(!req->r_target_inode);
16721712

16731713
in = req->r_target_inode;
1714+
ceph_inode_set_subvolume(in, rinfo->targeti.subvolume_id);
16741715
err = ceph_fill_inode(in, req->r_locked_page, &rinfo->targeti,
16751716
NULL, session,
16761717
(!test_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags) &&

fs/ceph/mds_client.c

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,19 @@ static int parse_reply_info_quota(void **p, void *end,
9696
return -EIO;
9797
}
9898

99-
/*
100-
* parse individual inode info
101-
*/
10299
static int parse_reply_info_in(void **p, void *end,
103100
struct ceph_mds_reply_info_in *info,
104-
u64 features)
101+
u64 features,
102+
struct ceph_mds_client *mdsc)
105103
{
106104
int err = 0;
107105
u8 struct_v = 0;
106+
u8 struct_compat = 0;
107+
u32 struct_len = 0;
108+
109+
info->subvolume_id = CEPH_SUBVOLUME_ID_NONE;
108110

109111
if (features == (u64)-1) {
110-
u32 struct_len;
111-
u8 struct_compat;
112112
ceph_decode_8_safe(p, end, struct_v, bad);
113113
ceph_decode_8_safe(p, end, struct_compat, bad);
114114
/* struct_v is expected to be >= 1. we only understand
@@ -252,6 +252,10 @@ static int parse_reply_info_in(void **p, void *end,
252252
ceph_decode_skip_n(p, end, v8_struct_len, bad);
253253
}
254254

255+
/* struct_v 9 added subvolume_id */
256+
if (struct_v >= 9)
257+
ceph_decode_64_safe(p, end, info->subvolume_id, bad);
258+
255259
*p = end;
256260
} else {
257261
/* legacy (unversioned) struct */
@@ -384,12 +388,13 @@ static int parse_reply_info_lease(void **p, void *end,
384388
*/
385389
static int parse_reply_info_trace(void **p, void *end,
386390
struct ceph_mds_reply_info_parsed *info,
387-
u64 features)
391+
u64 features,
392+
struct ceph_mds_client *mdsc)
388393
{
389394
int err;
390395

391396
if (info->head->is_dentry) {
392-
err = parse_reply_info_in(p, end, &info->diri, features);
397+
err = parse_reply_info_in(p, end, &info->diri, features, mdsc);
393398
if (err < 0)
394399
goto out_bad;
395400

@@ -409,7 +414,8 @@ static int parse_reply_info_trace(void **p, void *end,
409414
}
410415

411416
if (info->head->is_target) {
412-
err = parse_reply_info_in(p, end, &info->targeti, features);
417+
err = parse_reply_info_in(p, end, &info->targeti, features,
418+
mdsc);
413419
if (err < 0)
414420
goto out_bad;
415421
}
@@ -430,7 +436,8 @@ static int parse_reply_info_trace(void **p, void *end,
430436
*/
431437
static int parse_reply_info_readdir(void **p, void *end,
432438
struct ceph_mds_request *req,
433-
u64 features)
439+
u64 features,
440+
struct ceph_mds_client *mdsc)
434441
{
435442
struct ceph_mds_reply_info_parsed *info = &req->r_reply_info;
436443
struct ceph_client *cl = req->r_mdsc->fsc->client;
@@ -545,7 +552,7 @@ static int parse_reply_info_readdir(void **p, void *end,
545552
rde->name_len = oname.len;
546553

547554
/* inode */
548-
err = parse_reply_info_in(p, end, &rde->inode, features);
555+
err = parse_reply_info_in(p, end, &rde->inode, features, mdsc);
549556
if (err < 0)
550557
goto out_bad;
551558
/* ceph_readdir_prepopulate() will update it */
@@ -753,7 +760,8 @@ static int parse_reply_info_extra(void **p, void *end,
753760
if (op == CEPH_MDS_OP_GETFILELOCK)
754761
return parse_reply_info_filelock(p, end, info, features);
755762
else if (op == CEPH_MDS_OP_READDIR || op == CEPH_MDS_OP_LSSNAP)
756-
return parse_reply_info_readdir(p, end, req, features);
763+
return parse_reply_info_readdir(p, end, req, features,
764+
req->r_mdsc);
757765
else if (op == CEPH_MDS_OP_CREATE)
758766
return parse_reply_info_create(p, end, info, features, s);
759767
else if (op == CEPH_MDS_OP_GETVXATTR)
@@ -782,7 +790,8 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
782790
ceph_decode_32_safe(&p, end, len, bad);
783791
if (len > 0) {
784792
ceph_decode_need(&p, end, len, bad);
785-
err = parse_reply_info_trace(&p, p+len, info, features);
793+
err = parse_reply_info_trace(&p, p + len, info, features,
794+
s->s_mdsc);
786795
if (err < 0)
787796
goto out_bad;
788797
}
@@ -791,7 +800,7 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg,
791800
ceph_decode_32_safe(&p, end, len, bad);
792801
if (len > 0) {
793802
ceph_decode_need(&p, end, len, bad);
794-
err = parse_reply_info_extra(&p, p+len, req, features, s);
803+
err = parse_reply_info_extra(&p, p + len, req, features, s);
795804
if (err < 0)
796805
goto out_bad;
797806
}
@@ -3989,6 +3998,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
39893998
goto out_err;
39903999
}
39914000
req->r_target_inode = in;
4001+
ceph_inode_set_subvolume(in, rinfo->targeti.subvolume_id);
39924002
}
39934003

39944004
mutex_lock(&session->s_mutex);

fs/ceph/mds_client.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ struct ceph_mds_reply_info_in {
118118
u32 fscrypt_file_len;
119119
u64 rsnaps;
120120
u64 change_attr;
121+
u64 subvolume_id;
121122
};
122123

123124
struct ceph_mds_reply_dir_entry {

fs/ceph/super.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,15 @@ struct ceph_inode_info {
398398
/* quotas */
399399
u64 i_max_bytes, i_max_files;
400400

401+
/*
402+
* Subvolume ID this inode belongs to. CEPH_SUBVOLUME_ID_NONE (0)
403+
* means unknown/unset, matching the FUSE client convention.
404+
* Once set to a valid (non-zero) value, it should not change
405+
* during the inode's lifetime.
406+
*/
407+
#define CEPH_SUBVOLUME_ID_NONE 0
408+
u64 i_subvolume_id;
409+
401410
s32 i_dir_pin;
402411

403412
struct rb_root i_fragtree;
@@ -1069,6 +1078,7 @@ extern struct inode *ceph_get_inode(struct super_block *sb,
10691078
extern struct inode *ceph_get_snapdir(struct inode *parent);
10701079
extern int ceph_fill_file_size(struct inode *inode, int issued,
10711080
u32 truncate_seq, u64 truncate_size, u64 size);
1081+
extern void ceph_inode_set_subvolume(struct inode *inode, u64 subvolume_id);
10721082
extern void ceph_fill_file_time(struct inode *inode, int issued,
10731083
u64 time_warp_seq, struct timespec64 *ctime,
10741084
struct timespec64 *mtime,

0 commit comments

Comments
 (0)