diff --git a/fs/ceph/Kconfig b/fs/ceph/Kconfig index 3d64a316ca31..3652e55ce36f 100644 --- a/fs/ceph/Kconfig +++ b/fs/ceph/Kconfig @@ -17,6 +17,19 @@ config CEPH_FS If unsure, say N. if CEPH_FS + +config CEPH_FS_INLINE_DATA + bool "Enable CephFS inline data support (deprecated)" + depends on CEPH_FS + default n + help + Inline data support has been deprecated in the CephFS MDS + and is being formally removed. Existing inline data can be + migrated to data pool objects via MDS scrub. Choose N + unless you have a legacy filesystem that requires it, + and ensure all inline data has been migrated (see + ceph fs set inline_data false) before enabling. + config CEPH_FSCACHE bool "Enable Ceph client caching support" depends on CEPH_FS=m && FSCACHE || CEPH_FS=y && FSCACHE=y diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 18060dac8d92..31a3175fdd7a 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -277,6 +277,7 @@ static void finish_netfs_read(struct ceph_osd_request *req) ceph_dec_osd_stopping_blocker(fsc->mdsc); } +#ifdef CONFIG_CEPH_FS_INLINE_DATA static bool ceph_netfs_issue_op_inline(struct netfs_io_subrequest *subreq) { struct netfs_io_request *rreq = subreq->rreq; @@ -337,6 +338,12 @@ out: netfs_read_subreq_terminated(subreq); return true; } +#else +static bool ceph_netfs_issue_op_inline(struct netfs_io_subrequest *subreq) +{ + return false; +} +#endif /* CONFIG_CEPH_FS_INLINE_DATA */ static int ceph_netfs_prepare_read(struct netfs_io_subrequest *subreq) { @@ -2181,6 +2188,7 @@ out_free: return ret; } +#ifdef CONFIG_CEPH_FS_INLINE_DATA void ceph_fill_inline_data(struct inode *inode, struct page *locked_page, char *data, size_t len) { @@ -2242,7 +2250,7 @@ int ceph_uninline_data(struct file *file) u64 len; spin_lock(&ci->i_ceph_lock); - inline_version = ci->i_inline_version; + inline_version = ceph_inline_version(ci); spin_unlock(&ci->i_ceph_lock); doutc(cl, "%llx.%llx inline_version %llu\n", ceph_vinop(inode), @@ -2358,7 +2366,7 @@ out_uninline: /* Set to CAP_INLINE_NONE and dirty the caps */ down_read(&fsc->mdsc->snap_rwsem); spin_lock(&ci->i_ceph_lock); - ci->i_inline_version = CEPH_INLINE_NONE; + ceph_set_inline_version(ci, CEPH_INLINE_NONE); dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR, &prealloc_cf); spin_unlock(&ci->i_ceph_lock); up_read(&fsc->mdsc->snap_rwsem); @@ -2381,6 +2389,7 @@ out: ceph_vinop(inode), inline_version, err); return err; } +#endif /* CONFIG_CEPH_FS_INLINE_DATA */ static const struct vm_operations_struct ceph_vmops = { .fault = ceph_filemap_fault, diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 645d9096674d..1847badddd87 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1500,7 +1500,7 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_inode_info *ci, arg->gid = inode->i_gid; arg->mode = inode->i_mode; - arg->inline_data = ci->i_inline_version != CEPH_INLINE_NONE; + arg->inline_data = ceph_inline_version(ci) != CEPH_INLINE_NONE; if (!(flags & CEPH_CLIENT_CAPS_PENDING_CAPSNAP) && !list_empty(&ci->i_cap_snaps)) { struct ceph_cap_snap *capsnap; @@ -3541,6 +3541,32 @@ struct cap_extra_info { u64 fscrypt_file_size; }; +/* + * Cap grant messages (msg_version >= 4) always carry inline_version + * followed by inline_data. With inline data support disabled we still + * have to consume the fields, we just don't keep the data around. + */ +static int parse_inline_data(void **p, void *end, + struct cap_extra_info *extra_info) +{ +#ifdef CONFIG_CEPH_FS_INLINE_DATA + ceph_decode_64_safe(p, end, extra_info->inline_version, bad); + ceph_decode_32_safe(p, end, extra_info->inline_len, bad); + ceph_decode_need(p, end, extra_info->inline_len, bad); + extra_info->inline_data = *p; + *p += extra_info->inline_len; +#else + u32 len; + + ceph_decode_skip_64(p, end, bad); + ceph_decode_32_safe(p, end, len, bad); + ceph_decode_skip_n(p, end, len, bad); +#endif + return 0; +bad: + return -EIO; +} + /* * Handle a cap GRANT message from the MDS. (Note that a GRANT may * actually be a revocation if it specifies a smaller cap set.) @@ -3833,9 +3859,9 @@ static void handle_cap_grant(struct inode *inode, } if (extra_info->inline_version > 0 && - extra_info->inline_version >= ci->i_inline_version) { - ci->i_inline_version = extra_info->inline_version; - if (ci->i_inline_version != CEPH_INLINE_NONE && + extra_info->inline_version >= ceph_inline_version(ci)) { + ceph_set_inline_version(ci, extra_info->inline_version); + if (extra_info->inline_version != CEPH_INLINE_NONE && (newcaps & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO))) fill_inline = true; } @@ -4427,6 +4453,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, struct ceph_snap_realm *realm = NULL; int op; int msg_version = le16_to_cpu(msg->hdr.version); + int err; u32 seq, mseq, issue_seq; struct ceph_vino vino; void *snaptrace; @@ -4478,12 +4505,9 @@ void ceph_handle_caps(struct ceph_mds_session *session, } if (msg_version >= 4) { - ceph_decode_64_safe(&p, end, extra_info.inline_version, bad); - ceph_decode_32_safe(&p, end, extra_info.inline_len, bad); - if (p + extra_info.inline_len > end) + err = parse_inline_data(&p, end, &extra_info); + if (err < 0) goto bad; - extra_info.inline_data = p; - p += extra_info.inline_len; } if (msg_version >= 5) { diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index f913b8289976..b1282cd26526 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -636,7 +636,7 @@ struct inode *ceph_alloc_inode(struct super_block *sb) spin_lock_init(&ci->i_ceph_lock); ci->i_version = 0; - ci->i_inline_version = 0; + ceph_set_inline_version(ci, 0); ci->i_time_warp_seq = 0; ci->i_ceph_flags = 0; atomic64_set(&ci->i_ordered_count, 1); @@ -1372,9 +1372,9 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, } if (iinfo->inline_version > 0 && - iinfo->inline_version >= ci->i_inline_version) { + iinfo->inline_version >= ceph_inline_version(ci)) { int cache_caps = CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO; - ci->i_inline_version = iinfo->inline_version; + ceph_set_inline_version(ci, iinfo->inline_version); if (ceph_has_inline_data(ci) && (locked_page || (info_caps & cache_caps))) fill_inline = true; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 9a07deae1421..e23bb16c43cb 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -114,6 +114,32 @@ bad: return -EIO; } +/* + * Decode the inline_version + inline_data fields the MDS puts into + * reply inodes. With inline data support disabled we still have to + * consume them, we just don't keep the data around. + */ +static int parse_reply_info_inline_data(void **p, void *end, + struct ceph_mds_reply_info_in *info) +{ +#ifdef CONFIG_CEPH_FS_INLINE_DATA + ceph_decode_64_safe(p, end, info->inline_version, bad); + ceph_decode_32_safe(p, end, info->inline_len, bad); + ceph_decode_need(p, end, info->inline_len, bad); + info->inline_data = *p; + *p += info->inline_len; +#else + u32 len; + + ceph_decode_skip_64(p, end, bad); + ceph_decode_32_safe(p, end, len, bad); + ceph_decode_skip_n(p, end, len, bad); +#endif + return 0; +bad: + return -EIO; +} + static int parse_reply_info_in(void **p, void *end, struct ceph_mds_reply_info_in *info, u64 features, @@ -158,11 +184,9 @@ static int parse_reply_info_in(void **p, void *end, if (features == (u64)-1) { /* inline data */ - ceph_decode_64_safe(p, end, info->inline_version, bad); - ceph_decode_32_safe(p, end, info->inline_len, bad); - ceph_decode_need(p, end, info->inline_len, bad); - info->inline_data = *p; - *p += info->inline_len; + err = parse_reply_info_inline_data(p, end, info); + if (err < 0) + goto out_bad; /* quota */ err = parse_reply_info_quota(p, end, info); if (err < 0) @@ -278,13 +302,12 @@ static int parse_reply_info_in(void **p, void *end, } else { /* legacy (unversioned) struct */ if (features & CEPH_FEATURE_MDS_INLINE_DATA) { - ceph_decode_64_safe(p, end, info->inline_version, bad); - ceph_decode_32_safe(p, end, info->inline_len, bad); - ceph_decode_need(p, end, info->inline_len, bad); - info->inline_data = *p; - *p += info->inline_len; - } else + err = parse_reply_info_inline_data(p, end, info); + if (err < 0) + goto out_bad; + } else { info->inline_version = CEPH_INLINE_NONE; + } if (features & CEPH_FEATURE_MDS_QUOTA) { err = parse_reply_info_quota(p, end, info); diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index f064b5ad3bf4..498ffc1a91cb 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -613,7 +613,7 @@ static void ceph_queue_cap_snap(struct ceph_inode_info *ci, capsnap->xattr_version = 0; } - capsnap->inline_data = ci->i_inline_version != CEPH_INLINE_NONE; + capsnap->inline_data = ceph_inline_version(ci) != CEPH_INLINE_NONE; /* dirty page count moved from _head to this cap_snap; all subsequent writes page dirties occur _after_ this diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 42287dba9e64..a1c0f28ed4b2 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -401,7 +401,9 @@ struct ceph_inode_info { spinlock_t i_ceph_lock; u64 i_version; +#ifdef CONFIG_CEPH_FS_INLINE_DATA u64 i_inline_version; +#endif u32 i_time_warp_seq; unsigned long i_ceph_flags; @@ -1404,15 +1406,64 @@ extern void __ceph_touch_fmode(struct ceph_inode_info *ci, extern const struct address_space_operations ceph_aops; extern const struct netfs_request_ops ceph_netfs_ops; int ceph_mmap_prepare(struct vm_area_desc *desc); -extern int ceph_uninline_data(struct file *file); extern int ceph_pool_perm_check(struct inode *inode, int need); extern void ceph_pool_perm_destroy(struct ceph_mds_client* mdsc); int ceph_purge_inode_cap(struct inode *inode, struct ceph_cap *cap, bool *invalidate); +#ifdef CONFIG_CEPH_FS_INLINE_DATA +extern int ceph_uninline_data(struct file *file); +extern void ceph_fill_inline_data(struct inode *inode, struct page *locked_page, + char *data, size_t len); + +static inline u64 ceph_inline_version(const struct ceph_inode_info *ci) +{ + return ci->i_inline_version; +} + +static inline void ceph_set_inline_version(struct ceph_inode_info *ci, + u64 version) +{ + ci->i_inline_version = version; +} +#else +/* + * Only ever called under ceph_has_inline_data(), which is a constant + * false here, so this is unreachable; 0 keeps the error paths above + * simple without implying anything was done. + */ +static inline int ceph_uninline_data(struct file *file) +{ + return 0; +} + +static inline void ceph_fill_inline_data(struct inode *inode, + struct page *locked_page, + char *data, size_t len) +{ +} + +/* + * Without inline data support an inode never has any, so report the + * version the MDS uses for "no inline data" and ignore any update. + * This constant-folds all the inline data handling away. + */ +static inline u64 ceph_inline_version(const struct ceph_inode_info *ci) +{ + return CEPH_INLINE_NONE; +} + +static inline void ceph_set_inline_version(struct ceph_inode_info *ci, + u64 version) +{ +} +#endif /* CONFIG_CEPH_FS_INLINE_DATA */ + static inline bool ceph_has_inline_data(const struct ceph_inode_info *ci) { - if (ci->i_inline_version == CEPH_INLINE_NONE || - ci->i_inline_version == 1) /* initial version, no data */ + u64 version = ceph_inline_version(ci); + + if (version == CEPH_INLINE_NONE || + version == 1) /* initial version, no data */ return false; return true; } @@ -1428,8 +1479,6 @@ extern ssize_t __ceph_sync_read(struct inode *inode, loff_t *ki_pos, struct iov_iter *to, int *retry_op, u64 *last_objver); extern int ceph_release(struct inode *inode, struct file *filp); -extern void ceph_fill_inline_data(struct inode *inode, struct page *locked_page, - char *data, size_t len); /* dir.c */ extern const struct file_operations ceph_dir_fops; diff --git a/include/linux/ceph/ceph_features.h b/include/linux/ceph/ceph_features.h index 3a47acd9cc14..1e4dce93a8a1 100644 --- a/include/linux/ceph/ceph_features.h +++ b/include/linux/ceph/ceph_features.h @@ -173,6 +173,18 @@ DEFINE_CEPH_FEATURE(62, 1, RESERVED) // do not use; used as a sentinal DEFINE_CEPH_FEATURE_DEPRECATED(63, 1, RESERVED_BROKEN, LUMINOUS) // client-facing +/* + * Only advertise CEPH_FEATURE_MDS_INLINE_DATA when inline data support is + * built in. Without it the MDS refuses to hand out files that still have + * inline data instead of letting the client read the data pool objects + * that do not hold it yet. + */ +#ifdef CONFIG_CEPH_FS_INLINE_DATA +#define CEPH_FEATURE_MDS_INLINE_DATA_SUPPORTED CEPH_FEATURE_MDS_INLINE_DATA +#else +#define CEPH_FEATURE_MDS_INLINE_DATA_SUPPORTED 0 +#endif + /* * Features supported. */ @@ -204,7 +216,7 @@ DEFINE_CEPH_FEATURE_DEPRECATED(63, 1, RESERVED_BROKEN, LUMINOUS) // client-facin CEPH_FEATURE_CRUSH_V2 | \ CEPH_FEATURE_EXPORT_PEER | \ CEPH_FEATURE_OSDMAP_ENC | \ - CEPH_FEATURE_MDS_INLINE_DATA | \ + CEPH_FEATURE_MDS_INLINE_DATA_SUPPORTED |\ CEPH_FEATURE_CRUSH_TUNABLES3 | \ CEPH_FEATURE_OSD_PRIMARY_AFFINITY | \ CEPH_FEATURE_MSGR_KEEPALIVE2 | \ diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h index 69ac3e55a3fe..0adc526ec321 100644 --- a/include/linux/ceph/ceph_fs.h +++ b/include/linux/ceph/ceph_fs.h @@ -707,8 +707,13 @@ int ceph_flags_to_mode(int flags); CEPH_CAP_LINK_SHARED | \ CEPH_CAP_FILE_SHARED | \ CEPH_CAP_XATTR_SHARED) +#ifdef CONFIG_CEPH_FS_INLINE_DATA #define CEPH_STAT_CAP_INLINE_DATA (CEPH_CAP_FILE_SHARED | \ CEPH_CAP_FILE_RD) +#else +/* no inline data to fetch, so no extra caps are needed for it */ +#define CEPH_STAT_CAP_INLINE_DATA 0 +#endif #define CEPH_STAT_RSTAT CEPH_CAP_FILE_WREXTEND #define CEPH_CAP_ANY_SHARED (CEPH_CAP_AUTH_SHARED | \