// SPDX-License-Identifier: GPL-2.0#include <linux/ceph/ceph_debug.h> #include <linux/module.h>#include <linux/fs.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/uaccess.h>#include <linux/kernel.h>#include <linux/writeback.h>#include <linux/vmalloc.h>#include <linux/xattr.h>#include <linux/posix_acl.h>#include <linux/random.h>#include <linux/sort.h>#include <linux/iversion.h>#include <linux/fscrypt.h> #include "super.h"#include "mds_client.h"#include "cache.h"#include "crypto.h"#include <linux/ceph/decode.h> /* * Ceph inode operations * * Implement basic inode helpers (get, alloc) and inode ops (getattr, * setattr, etc.), xattr helpers, and helpers for assimilating * metadata returned by the MDS into our cache. * * Also define helpers for doing asynchronous writeback, invalidation, * and truncation for the benefit of those who can't afford to block * (typically because they are in the message handler path). */ static const struct inode_operations ceph_symlink_iops;static const struct inode_operations ceph_encrypted_symlink_iops; static void ceph_inode_work(struct work_struct *work); /* * find or create an inode, given the ceph ino number */static int ceph_set_ino_cb(struct inode *inode, void *data){ struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); ci->i_vino = *(struct ceph_vino *)data; inode->i_ino = ceph_vino_to_ino_t(ci->i_vino); inode_set_iversion_raw(inode, 0); percpu_counter_inc(&mdsc->metric.total_inodes); return 0;} /* * Check if the parent inode matches the vino from directory reply info */static inline bool ceph_vino_matches_parent(struct inode *parent, struct ceph_vino vino){ return ceph_ino(parent) == vino.ino && ceph_snap(parent) == vino.snap;} /* * Validate that the directory inode referenced by @req->r_parent matches the * inode number and snapshot id contained in the reply's directory record. If * they do not match – which can theoretically happen if the parent dentry was * moved between the time the request was issued and the reply arrived – fall * back to looking up the correct inode in the inode cache. * * A reference is *always* returned. Callers that receive a different inode * than the original @parent are responsible for dropping the extra reference * once the reply has been processed. */static struct inode *ceph_get_reply_dir(struct super_block *sb, struct inode *parent, struct ceph_mds_reply_info_parsed *rinfo){ struct ceph_vino vino; if (unlikely(!rinfo->diri.in)) return parent; /* nothing to compare against */ /* If we didn't have a cached parent inode to begin with, just bail out. */ if (!parent) return NULL; vino.ino = le64_to_cpu(rinfo->diri.in->ino); vino.snap = le64_to_cpu(rinfo->diri.in->snapid); if (likely(ceph_vino_matches_parent(parent, vino))) return parent; /* matches – use the original reference */ /* Mismatch – this should be rare. Emit a WARN and obtain the correct inode. */ WARN_ONCE(1, "ceph: reply dir mismatch (parent valid %llx.%llx reply %llx.%llx)\n", ceph_ino(parent), ceph_snap(parent), vino.ino, vino.snap); return ceph_get_inode(sb, vino, NULL);} /** * ceph_new_inode - allocate a new inode in advance of an expected create * @dir: parent directory for new inode * @dentry: dentry that may eventually point to new inode * @mode: mode of new inode * @as_ctx: pointer to inherited security context * * Allocate a new inode in advance of an operation to create a new inode. * This allocates the inode and sets up the acl_sec_ctx with appropriate * info for the new inode. * * Returns a pointer to the new inode or an ERR_PTR. */struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry, umode_t *mode, struct ceph_acl_sec_ctx *as_ctx){ int err; struct inode *inode; inode = new_inode(dir->i_sb); if (!inode) return ERR_PTR(-ENOMEM); inode->i_blkbits = CEPH_FSCRYPT_BLOCK_SHIFT; if (!S_ISLNK(*mode)) { err = ceph_pre_init_acls(dir, mode, as_ctx); if (err < 0) goto out_err; } inode_state_assign_raw(inode, 0); inode->i_mode = *mode; err = ceph_security_init_secctx(dentry, *mode, as_ctx); if (err < 0) goto out_err; /* * We'll skip setting fscrypt context for snapshots, leaving that for * the handle_reply(). */ if (ceph_snap(dir) != CEPH_SNAPDIR) { err = ceph_fscrypt_prepare_context(dir, inode, as_ctx); if (err) goto out_err; } return inode;out_err: iput(inode); return ERR_PTR(err);} void ceph_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as_ctx){ if (as_ctx->pagelist) { req->r_pagelist = as_ctx->pagelist; as_ctx->pagelist = NULL; } ceph_fscrypt_as_ctx_to_req(req, as_ctx);} /** * ceph_get_inode - find or create/hash a new inode * @sb: superblock to search and allocate in * @vino: vino to search for * @newino: optional new inode to insert if one isn't found (may be NULL) * * Search for or insert a new inode into the hash for the given vino, and * return a reference to it. If new is non-NULL, its reference is consumed. */struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino, struct inode *newino){ struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(sb); struct ceph_client *cl = mdsc->fsc->client; struct inode *inode; if (ceph_vino_is_reserved(vino)) return ERR_PTR(-EREMOTEIO); if (newino) { inode = inode_insert5(newino, (unsigned long)vino.ino, ceph_ino_compare, ceph_set_ino_cb, &vino); if (inode != newino) iput(newino); } else { inode = iget5_locked(sb, (unsigned long)vino.ino, ceph_ino_compare, ceph_set_ino_cb, &vino); } if (!inode) { doutc(cl, "no inode found for %llx.%llx\n", vino.ino, vino.snap); return ERR_PTR(-ENOMEM); } doutc(cl, "on %llx=%llx.%llx got %p new %d\n", ceph_present_inode(inode), ceph_vinop(inode), inode, !!(inode_state_read_once(inode) & I_NEW)); return inode;} /* * get/construct snapdir inode for a given directory */struct inode *ceph_get_snapdir(struct inode *parent){ struct ceph_client *cl = ceph_inode_to_client(parent); struct ceph_vino vino = { .ino = ceph_ino(parent), .snap = CEPH_SNAPDIR, }; struct inode *inode = ceph_get_inode(parent->i_sb, vino, NULL); struct ceph_inode_info *ci = ceph_inode(inode); int ret = -ENOTDIR; if (IS_ERR(inode)) return inode; if (!S_ISDIR(parent->i_mode)) { pr_warn_once_client(cl, "bad snapdir parent type (mode=0%o)\n", parent->i_mode); goto err; } if (!(inode_state_read_once(inode) & I_NEW) && !S_ISDIR(inode->i_mode)) { pr_warn_once_client(cl, "bad snapdir inode type (mode=0%o)\n", inode->i_mode); goto err; } inode->i_mode = parent->i_mode; inode->i_uid = parent->i_uid; inode->i_gid = parent->i_gid; inode_set_mtime_to_ts(inode, inode_get_mtime(parent)); inode_set_ctime_to_ts(inode, inode_get_ctime(parent)); inode_set_atime_to_ts(inode, inode_get_atime(parent)); ci->i_rbytes = 0; ci->i_btime = ceph_inode(parent)->i_btime; #ifdef CONFIG_FS_ENCRYPTION /* if encrypted, just borrow fscrypt_auth from parent */ if (IS_ENCRYPTED(parent)) { struct ceph_inode_info *pci = ceph_inode(parent); ci->fscrypt_auth = kmemdup(pci->fscrypt_auth, pci->fscrypt_auth_len, GFP_KERNEL); if (ci->fscrypt_auth) { inode->i_flags |= S_ENCRYPTED; ci->fscrypt_auth_len = pci->fscrypt_auth_len; } else { doutc(cl, "Failed to alloc snapdir fscrypt_auth\n"); ret = -ENOMEM; goto err; } }#endif if (inode_state_read_once(inode) & I_NEW) { inode->i_op = &ceph_snapdir_iops; inode->i_fop = &ceph_snapdir_fops; ci->i_snap_caps = CEPH_CAP_PIN; /* so we can open */ unlock_new_inode(inode); } return inode;err: if ((inode_state_read_once(inode) & I_NEW)) discard_new_inode(inode); else iput(inode); return ERR_PTR(ret);} const struct inode_operations ceph_file_iops = { .permission = ceph_permission, .setattr = ceph_setattr, .getattr = ceph_getattr, .listxattr = ceph_listxattr, .get_inode_acl = ceph_get_acl, .set_acl = ceph_set_acl,}; /* * We use a 'frag tree' to keep track of the MDS's directory fragments * for a given inode (usually there is just a single fragment). We * need to know when a child frag is delegated to a new MDS, or when * it is flagged as replicated, so we can direct our requests * accordingly. */ /* * find/create a frag in the tree */static struct ceph_inode_frag *__get_or_create_frag(struct ceph_inode_info *ci, u32 f){ struct inode *inode = &ci->netfs.inode; struct ceph_client *cl = ceph_inode_to_client(inode); struct rb_node **p; struct rb_node *parent = NULL; struct ceph_inode_frag *frag; int c; p = &ci->i_fragtree.rb_node; while (*p) { parent = *p; frag = rb_entry(parent, struct ceph_inode_frag, node); c = ceph_frag_compare(f, frag->frag); if (c < 0) p = &(*p)->rb_left; else if (c > 0) p = &(*p)->rb_right; else return frag; } frag = kmalloc_obj(*frag, GFP_NOFS); if (!frag) return ERR_PTR(-ENOMEM); frag->frag = f; frag->split_by = 0; frag->mds = -1; frag->ndist = 0; rb_link_node(&frag->node, parent, p); rb_insert_color(&frag->node, &ci->i_fragtree); doutc(cl, "added %p %llx.%llx frag %x\n", inode, ceph_vinop(inode), f); return frag;} /* * find a specific frag @f */struct ceph_inode_frag *__ceph_find_frag(struct ceph_inode_info *ci, u32 f){ struct rb_node *n = ci->i_fragtree.rb_node; while (n) { struct ceph_inode_frag *frag = rb_entry(n, struct ceph_inode_frag, node); int c = ceph_frag_compare(f, frag->frag); if (c < 0) n = n->rb_left; else if (c > 0) n = n->rb_right; else return frag; } return NULL;} /* * Choose frag containing the given value @v. If @pfrag is * specified, copy the frag delegation info to the caller if * it is present. */static u32 __ceph_choose_frag(struct ceph_inode_info *ci, u32 v, struct ceph_inode_frag *pfrag, int *found){ struct ceph_client *cl = ceph_inode_to_client(&ci->netfs.inode); u32 t = ceph_frag_make(0, 0); struct ceph_inode_frag *frag; unsigned nway, i; u32 n; if (found) *found = 0; while (1) { WARN_ON(!ceph_frag_contains_value(t, v)); frag = __ceph_find_frag(ci, t); if (!frag) break; /* t is a leaf */ if (frag->split_by == 0) { if (pfrag) memcpy(pfrag, frag, sizeof(*pfrag)); if (found) *found = 1; break; } /* choose child */ nway = 1 << frag->split_by; doutc(cl, "frag(%x) %x splits by %d (%d ways)\n", v, t, frag->split_by, nway); for (i = 0; i < nway; i++) { n = ceph_frag_make_child(t, frag->split_by, i); if (ceph_frag_contains_value(n, v)) { t = n; break; } } BUG_ON(i == nway); } doutc(cl, "frag(%x) = %x\n", v, t); return t;} u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v, struct ceph_inode_frag *pfrag, int *found){ u32 ret; mutex_lock(&ci->i_fragtree_mutex); ret = __ceph_choose_frag(ci, v, pfrag, found); mutex_unlock(&ci->i_fragtree_mutex); return ret;} /* * Process dirfrag (delegation) info from the mds. Include leaf * fragment in tree ONLY if ndist > 0. Otherwise, only * branches/splits are included in i_fragtree) */static int ceph_fill_dirfrag(struct inode *inode, struct ceph_mds_reply_dirfrag *dirinfo){ struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_client *cl = ceph_inode_to_client(inode); struct ceph_inode_frag *frag; u32 id = le32_to_cpu(dirinfo->frag); int mds = le32_to_cpu(dirinfo->auth); int ndist = le32_to_cpu(dirinfo->ndist); int diri_auth = -1; int i; int err = 0; spin_lock(&ci->i_ceph_lock); if (ci->i_auth_cap) diri_auth = ci->i_auth_cap->mds; spin_unlock(&ci->i_ceph_lock); if (mds == -1) /* CDIR_AUTH_PARENT */ mds = diri_auth; mutex_lock(&ci->i_fragtree_mutex); if (ndist == 0 && mds == diri_auth) { /* no delegation info needed. */ frag = __ceph_find_frag(ci, id); if (!frag) goto out; if (frag->split_by == 0) { /* tree leaf, remove */ doutc(cl, "removed %p %llx.%llx frag %x (no ref)\n", inode, ceph_vinop(inode), id); rb_erase(&frag->node, &ci->i_fragtree); kfree(frag); } else { /* tree branch, keep and clear */ doutc(cl, "cleared %p %llx.%llx frag %x referral\n", inode, ceph_vinop(inode), id); frag->mds = -1; frag->ndist = 0; } goto out; } /* find/add this frag to store mds delegation info */ frag = __get_or_create_frag(ci, id); if (IS_ERR(frag)) { /* this is not the end of the world; we can continue with bad/inaccurate delegation info */ pr_err_client(cl, "ENOMEM on mds ref %p %llx.%llx fg %x\n", inode, ceph_vinop(inode), le32_to_cpu(dirinfo->frag)); err = -ENOMEM; goto out; } frag->mds = mds; frag->ndist = min_t(u32, ndist, CEPH_MAX_DIRFRAG_REP); for (i = 0; i < frag->ndist; i++) frag->dist[i] = le32_to_cpu(dirinfo->dist[i]); doutc(cl, "%p %llx.%llx frag %x ndist=%d\n", inode, ceph_vinop(inode), frag->frag, frag->ndist); out: mutex_unlock(&ci->i_fragtree_mutex); return err;} static int frag_tree_split_cmp(const void *l, const void *r){ struct ceph_frag_tree_split *ls = (struct ceph_frag_tree_split*)l; struct ceph_frag_tree_split *rs = (struct ceph_frag_tree_split*)r; return ceph_frag_compare(le32_to_cpu(ls->frag), le32_to_cpu(rs->frag));} static bool is_frag_child(u32 f, struct ceph_inode_frag *frag)Lines 1–500 of 3259. Load more
// SPDX-License-Identifier: GPL-2.0 #include <linux/ceph/ceph_debug.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/uaccess.h> #include <linux/kernel.h> #include <linux/writeback.h> #include <linux/vmalloc.h> #include <linux/xattr.h> #include <linux/posix_acl.h> #include <linux/random.h> #include <linux/sort.h> #include <linux/iversion.h> #include <linux/fscrypt.h> #include "super.h" #include "mds_client.h" #include "cache.h" #include "crypto.h" #include <linux/ceph/decode.h> /* * Ceph inode operations * * Implement basic inode helpers (get, alloc) and inode ops (getattr, * setattr, etc.), xattr helpers, and helpers for assimilating * metadata returned by the MDS into our cache. * * Also define helpers for doing asynchronous writeback, invalidation, * and truncation for the benefit of those who can't afford to block * (typically because they are in the message handler path). */ static const struct inode_operations ceph_symlink_iops; static const struct inode_operations ceph_encrypted_symlink_iops; static void ceph_inode_work(struct work_struct *work); /* * find or create an inode, given the ceph ino number */ static int ceph_set_ino_cb(struct inode *inode, void *data) { struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); ci->i_vino = *(struct ceph_vino *)data; inode->i_ino = ceph_vino_to_ino_t(ci->i_vino); inode_set_iversion_raw(inode, 0); percpu_counter_inc(&mdsc->metric.total_inodes); return 0; } /* * Check if the parent inode matches the vino from directory reply info */ static inline bool ceph_vino_matches_parent(struct inode *parent, struct ceph_vino vino) { return ceph_ino(parent) == vino.ino && ceph_snap(parent) == vino.snap; } /* * Validate that the directory inode referenced by @req->r_parent matches the * inode number and snapshot id contained in the reply's directory record. If * they do not match – which can theoretically happen if the parent dentry was * moved between the time the request was issued and the reply arrived – fall * back to looking up the correct inode in the inode cache. * * A reference is *always* returned. Callers that receive a different inode * than the original @parent are responsible for dropping the extra reference * once the reply has been processed. */ static struct inode *ceph_get_reply_dir(struct super_block *sb, struct inode *parent, struct ceph_mds_reply_info_parsed *rinfo) { struct ceph_vino vino; if (unlikely(!rinfo->diri.in)) return parent; /* nothing to compare against */ /* If we didn't have a cached parent inode to begin with, just bail out. */ if (!parent) return NULL; vino.ino = le64_to_cpu(rinfo->diri.in->ino); vino.snap = le64_to_cpu(rinfo->diri.in->snapid); if (likely(ceph_vino_matches_parent(parent, vino))) return parent; /* matches – use the original reference */ /* Mismatch – this should be rare. Emit a WARN and obtain the correct inode. */ WARN_ONCE(1, "ceph: reply dir mismatch (parent valid %llx.%llx reply %llx.%llx)\n", ceph_ino(parent), ceph_snap(parent), vino.ino, vino.snap); return ceph_get_inode(sb, vino, NULL); } /** * ceph_new_inode - allocate a new inode in advance of an expected create * @dir: parent directory for new inode * @dentry: dentry that may eventually point to new inode * @mode: mode of new inode * @as_ctx: pointer to inherited security context * * Allocate a new inode in advance of an operation to create a new inode. * This allocates the inode and sets up the acl_sec_ctx with appropriate * info for the new inode. * * Returns a pointer to the new inode or an ERR_PTR. */ struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry, umode_t *mode, struct ceph_acl_sec_ctx *as_ctx) { int err; struct inode *inode; inode = new_inode(dir->i_sb); if (!inode) return ERR_PTR(-ENOMEM); inode->i_blkbits = CEPH_FSCRYPT_BLOCK_SHIFT; if (!S_ISLNK(*mode)) { err = ceph_pre_init_acls(dir, mode, as_ctx); if (err < 0) goto out_err; } inode_state_assign_raw(inode, 0); inode->i_mode = *mode; err = ceph_security_init_secctx(dentry, *mode, as_ctx); if (err < 0) goto out_err; /* * We'll skip setting fscrypt context for snapshots, leaving that for * the handle_reply(). */ if (ceph_snap(dir) != CEPH_SNAPDIR) { err = ceph_fscrypt_prepare_context(dir, inode, as_ctx); if (err) goto out_err; } return inode; out_err: iput(inode); return ERR_PTR(err); } void ceph_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as_ctx) { if (as_ctx->pagelist) { req->r_pagelist = as_ctx->pagelist; as_ctx->pagelist = NULL; } ceph_fscrypt_as_ctx_to_req(req, as_ctx); } /** * ceph_get_inode - find or create/hash a new inode * @sb: superblock to search and allocate in * @vino: vino to search for * @newino: optional new inode to insert if one isn't found (may be NULL) * * Search for or insert a new inode into the hash for the given vino, and * return a reference to it. If new is non-NULL, its reference is consumed. */ struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino, struct inode *newino) { struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(sb); struct ceph_client *cl = mdsc->fsc->client; struct inode *inode; if (ceph_vino_is_reserved(vino)) return ERR_PTR(-EREMOTEIO); if (newino) { inode = inode_insert5(newino, (unsigned long)vino.ino, ceph_ino_compare, ceph_set_ino_cb, &vino); if (inode != newino) iput(newino); } else { inode = iget5_locked(sb, (unsigned long)vino.ino, ceph_ino_compare, ceph_set_ino_cb, &vino); } if (!inode) { doutc(cl, "no inode found for %llx.%llx\n", vino.ino, vino.snap); return ERR_PTR(-ENOMEM); } doutc(cl, "on %llx=%llx.%llx got %p new %d\n", ceph_present_inode(inode), ceph_vinop(inode), inode, !!(inode_state_read_once(inode) & I_NEW)); return inode; } /* * get/construct snapdir inode for a given directory */ struct inode *ceph_get_snapdir(struct inode *parent) { struct ceph_client *cl = ceph_inode_to_client(parent); struct ceph_vino vino = { .ino = ceph_ino(parent), .snap = CEPH_SNAPDIR, }; struct inode *inode = ceph_get_inode(parent->i_sb, vino, NULL); struct ceph_inode_info *ci = ceph_inode(inode); int ret = -ENOTDIR; if (IS_ERR(inode)) return inode; if (!S_ISDIR(parent->i_mode)) { pr_warn_once_client(cl, "bad snapdir parent type (mode=0%o)\n", parent->i_mode); goto err; } if (!(inode_state_read_once(inode) & I_NEW) && !S_ISDIR(inode->i_mode)) { pr_warn_once_client(cl, "bad snapdir inode type (mode=0%o)\n", inode->i_mode); goto err; } inode->i_mode = parent->i_mode; inode->i_uid = parent->i_uid; inode->i_gid = parent->i_gid; inode_set_mtime_to_ts(inode, inode_get_mtime(parent)); inode_set_ctime_to_ts(inode, inode_get_ctime(parent)); inode_set_atime_to_ts(inode, inode_get_atime(parent)); ci->i_rbytes = 0; ci->i_btime = ceph_inode(parent)->i_btime; #ifdef CONFIG_FS_ENCRYPTION /* if encrypted, just borrow fscrypt_auth from parent */ if (IS_ENCRYPTED(parent)) { struct ceph_inode_info *pci = ceph_inode(parent); ci->fscrypt_auth = kmemdup(pci->fscrypt_auth, pci->fscrypt_auth_len, GFP_KERNEL); if (ci->fscrypt_auth) { inode->i_flags |= S_ENCRYPTED; ci->fscrypt_auth_len = pci->fscrypt_auth_len; } else { doutc(cl, "Failed to alloc snapdir fscrypt_auth\n"); ret = -ENOMEM; goto err; } } #endif if (inode_state_read_once(inode) & I_NEW) { inode->i_op = &ceph_snapdir_iops; inode->i_fop = &ceph_snapdir_fops; ci->i_snap_caps = CEPH_CAP_PIN; /* so we can open */ unlock_new_inode(inode); } return inode; err: if ((inode_state_read_once(inode) & I_NEW)) discard_new_inode(inode); else iput(inode); return ERR_PTR(ret); } const struct inode_operations ceph_file_iops = { .permission = ceph_permission, .setattr = ceph_setattr, .getattr = ceph_getattr, .listxattr = ceph_listxattr, .get_inode_acl = ceph_get_acl, .set_acl = ceph_set_acl, }; /* * We use a 'frag tree' to keep track of the MDS's directory fragments * for a given inode (usually there is just a single fragment). We * need to know when a child frag is delegated to a new MDS, or when * it is flagged as replicated, so we can direct our requests * accordingly. */ /* * find/create a frag in the tree */ static struct ceph_inode_frag *__get_or_create_frag(struct ceph_inode_info *ci, u32 f) { struct inode *inode = &ci->netfs.inode; struct ceph_client *cl = ceph_inode_to_client(inode); struct rb_node **p; struct rb_node *parent = NULL; struct ceph_inode_frag *frag; int c; p = &ci->i_fragtree.rb_node; while (*p) { parent = *p; frag = rb_entry(parent, struct ceph_inode_frag, node); c = ceph_frag_compare(f, frag->frag); if (c < 0) p = &(*p)->rb_left; else if (c > 0) p = &(*p)->rb_right; else return frag; } frag = kmalloc_obj(*frag, GFP_NOFS); if (!frag) return ERR_PTR(-ENOMEM); frag->frag = f; frag->split_by = 0; frag->mds = -1; frag->ndist = 0; rb_link_node(&frag->node, parent, p); rb_insert_color(&frag->node, &ci->i_fragtree); doutc(cl, "added %p %llx.%llx frag %x\n", inode, ceph_vinop(inode), f); return frag; } /* * find a specific frag @f */ struct ceph_inode_frag *__ceph_find_frag(struct ceph_inode_info *ci, u32 f) { struct rb_node *n = ci->i_fragtree.rb_node; while (n) { struct ceph_inode_frag *frag = rb_entry(n, struct ceph_inode_frag, node); int c = ceph_frag_compare(f, frag->frag); if (c < 0) n = n->rb_left; else if (c > 0) n = n->rb_right; else return frag; } return NULL; } /* * Choose frag containing the given value @v. If @pfrag is * specified, copy the frag delegation info to the caller if * it is present. */ static u32 __ceph_choose_frag(struct ceph_inode_info *ci, u32 v, struct ceph_inode_frag *pfrag, int *found) { struct ceph_client *cl = ceph_inode_to_client(&ci->netfs.inode); u32 t = ceph_frag_make(0, 0); struct ceph_inode_frag *frag; unsigned nway, i; u32 n; if (found) *found = 0; while (1) { WARN_ON(!ceph_frag_contains_value(t, v)); frag = __ceph_find_frag(ci, t); if (!frag) break; /* t is a leaf */ if (frag->split_by == 0) { if (pfrag) memcpy(pfrag, frag, sizeof(*pfrag)); if (found) *found = 1; break; } /* choose child */ nway = 1 << frag->split_by; doutc(cl, "frag(%x) %x splits by %d (%d ways)\n", v, t, frag->split_by, nway); for (i = 0; i < nway; i++) { n = ceph_frag_make_child(t, frag->split_by, i); if (ceph_frag_contains_value(n, v)) { t = n; break; } } BUG_ON(i == nway); } doutc(cl, "frag(%x) = %x\n", v, t); return t; } u32 ceph_choose_frag(struct ceph_inode_info *ci, u32 v, struct ceph_inode_frag *pfrag, int *found) { u32 ret; mutex_lock(&ci->i_fragtree_mutex); ret = __ceph_choose_frag(ci, v, pfrag, found); mutex_unlock(&ci->i_fragtree_mutex); return ret; } /* * Process dirfrag (delegation) info from the mds. Include leaf * fragment in tree ONLY if ndist > 0. Otherwise, only * branches/splits are included in i_fragtree) */ static int ceph_fill_dirfrag(struct inode *inode, struct ceph_mds_reply_dirfrag *dirinfo) { struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_client *cl = ceph_inode_to_client(inode); struct ceph_inode_frag *frag; u32 id = le32_to_cpu(dirinfo->frag); int mds = le32_to_cpu(dirinfo->auth); int ndist = le32_to_cpu(dirinfo->ndist); int diri_auth = -1; int i; int err = 0; spin_lock(&ci->i_ceph_lock); if (ci->i_auth_cap) diri_auth = ci->i_auth_cap->mds; spin_unlock(&ci->i_ceph_lock); if (mds == -1) /* CDIR_AUTH_PARENT */ mds = diri_auth; mutex_lock(&ci->i_fragtree_mutex); if (ndist == 0 && mds == diri_auth) { /* no delegation info needed. */ frag = __ceph_find_frag(ci, id); if (!frag) goto out; if (frag->split_by == 0) { /* tree leaf, remove */ doutc(cl, "removed %p %llx.%llx frag %x (no ref)\n", inode, ceph_vinop(inode), id); rb_erase(&frag->node, &ci->i_fragtree); kfree(frag); } else { /* tree branch, keep and clear */ doutc(cl, "cleared %p %llx.%llx frag %x referral\n", inode, ceph_vinop(inode), id); frag->mds = -1; frag->ndist = 0; } goto out; } /* find/add this frag to store mds delegation info */ frag = __get_or_create_frag(ci, id); if (IS_ERR(frag)) { /* this is not the end of the world; we can continue with bad/inaccurate delegation info */ pr_err_client(cl, "ENOMEM on mds ref %p %llx.%llx fg %x\n", inode, ceph_vinop(inode), le32_to_cpu(dirinfo->frag)); err = -ENOMEM; goto out; } frag->mds = mds; frag->ndist = min_t(u32, ndist, CEPH_MAX_DIRFRAG_REP); for (i = 0; i < frag->ndist; i++) frag->dist[i] = le32_to_cpu(dirinfo->dist[i]); doutc(cl, "%p %llx.%llx frag %x ndist=%d\n", inode, ceph_vinop(inode), frag->frag, frag->ndist); out: mutex_unlock(&ci->i_fragtree_mutex); return err; } static int frag_tree_split_cmp(const void *l, const void *r) { struct ceph_frag_tree_split *ls = (struct ceph_frag_tree_split*)l; struct ceph_frag_tree_split *rs = (struct ceph_frag_tree_split*)r; return ceph_frag_compare(le32_to_cpu(ls->frag), le32_to_cpu(rs->frag)); } static bool is_frag_child(u32 f, struct ceph_inode_frag *frag)