/* * 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).
*/
/* * Check if the parent inode matches the vino from directory reply info
*/ staticinlinebool 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.
*/ staticstruct 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;
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;
}
/** * 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);
#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->i_state & 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);
}
/* * 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
*/ staticstruct 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; elseif (c > 0)
p = &(*p)->rb_right; else return frag;
}
frag = kmalloc(sizeof(*frag), GFP_NOFS); if (!frag) return ERR_PTR(-ENOMEM);
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; elseif (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);
/* * 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)
*/ staticint 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);
if (__ceph_has_quota(ci, QUOTA_GET_ANY))
ceph_adjust_quota_realms_count(inode, false);
/* * we may still have a snap_realm reference if there are stray * caps in i_snap_caps.
*/ if (ci->i_snap_realm) { if (ceph_snap(inode) == CEPH_NOSNAP) {
doutc(cl, " dropping residual ref to snap realm %p\n",
ci->i_snap_realm);
ceph_change_snap_realm(inode, NULL);
} else {
ceph_put_snapid_map(mdsc, ci->i_snapid_map);
ci->i_snap_realm = NULL;
}
}
__ceph_destroy_xattrs(ci); if (ci->i_xattrs.blob)
ceph_buffer_put(ci->i_xattrs.blob); if (ci->i_xattrs.prealloc_blob)
ceph_buffer_put(ci->i_xattrs.prealloc_blob);
/* * Helpers to fill in size, ctime, mtime, and atime. We have to be * careful because either the client or MDS may have more up to date * info, depending on which capabilities are held, and whether * time_warp_seq or truncate_seq have increased. (Ordinarily, mtime * and size are monotonically increasing, except when utimes() or * truncate() increments the corresponding _seq values.)
*/ int ceph_fill_file_size(struct inode *inode, int issued,
u32 truncate_seq, u64 truncate_size, u64 size)
{ struct ceph_client *cl = ceph_inode_to_client(inode); struct ceph_inode_info *ci = ceph_inode(inode); int queue_trunc = 0;
loff_t isize = i_size_read(inode);
if (ceph_seq_cmp(truncate_seq, ci->i_truncate_seq) > 0 ||
(truncate_seq == ci->i_truncate_seq && size > isize)) {
doutc(cl, "size %lld -> %llu\n", isize, size); if (size > 0 && S_ISDIR(inode->i_mode)) {
pr_err_client(cl, "non-zero size for directory\n");
size = 0;
}
i_size_write(inode, size);
inode->i_blocks = calc_inode_blocks(size); /* * If we're expanding, then we should be able to just update * the existing cookie.
*/ if (size > isize)
ceph_fscache_update(inode);
ci->i_reported_size = size; if (truncate_seq != ci->i_truncate_seq) {
doutc(cl, "truncate_seq %u -> %u\n",
ci->i_truncate_seq, truncate_seq);
ci->i_truncate_seq = truncate_seq;
/* the MDS should have revoked these caps */
WARN_ON_ONCE(issued & (CEPH_CAP_FILE_RD |
CEPH_CAP_FILE_LAZYIO)); /* * If we hold relevant caps, or in the case where we're * not the only client referencing this file and we * don't hold those caps, then we need to check whether * the file is either opened or mmaped
*/ if ((issued & (CEPH_CAP_FILE_CACHE|
CEPH_CAP_FILE_BUFFER)) ||
mapping_mapped(inode->i_mapping) ||
__ceph_is_file_opened(ci)) {
ci->i_truncate_pending++;
queue_trunc = 1;
}
}
}
/* * It's possible that the new sizes of the two consecutive * size truncations will be in the same fscrypt last block, * and we need to truncate the corresponding page caches * anyway.
*/ if (ceph_seq_cmp(truncate_seq, ci->i_truncate_seq) >= 0) {
doutc(cl, "truncate_size %lld -> %llu, encrypted %d\n",
ci->i_truncate_size, truncate_size,
!!IS_ENCRYPTED(inode));
/* * Populate an inode based on info from mds. May be called on new or * existing inodes.
*/ int ceph_fill_inode(struct inode *inode, struct page *locked_page, struct ceph_mds_reply_info_in *iinfo, struct ceph_mds_reply_dirfrag *dirinfo, struct ceph_mds_session *session, int cap_fmode, struct ceph_cap_reservation *caps_reservation)
{ struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); struct ceph_client *cl = mdsc->fsc->client; struct ceph_mds_reply_inode *info = iinfo->in; struct ceph_inode_info *ci = ceph_inode(inode); int issued, new_issued, info_caps; struct timespec64 mtime, atime, ctime; struct ceph_buffer *xattr_blob = NULL; struct ceph_buffer *old_blob = NULL; struct ceph_string *pool_ns = NULL; struct ceph_cap *new_cap = NULL; int err = 0; bool wake = false; bool queue_trunc = false; bool new_version = false; bool fill_inline = false;
umode_t mode = le32_to_cpu(info->mode);
dev_t rdev = le32_to_cpu(info->rdev);
lockdep_assert_held(&mdsc->snap_rwsem);
doutc(cl, "%p ino %llx.%llx v %llu had %llu\n", inode, ceph_vinop(inode),
le64_to_cpu(info->version), ci->i_version);
/* Once I_NEW is cleared, we can't change type or dev numbers */ if (inode->i_state & I_NEW) {
inode->i_mode = mode;
} else { if (inode_wrong_type(inode, mode)) {
pr_warn_once_client(cl, "inode type changed! (ino %llx.%llx is 0%o, mds says 0%o)\n",
ceph_vinop(inode), inode->i_mode, mode); return -ESTALE;
}
/* prealloc new cap struct */ if (info_caps && ceph_snap(inode) == CEPH_NOSNAP) {
new_cap = ceph_get_cap(mdsc, caps_reservation); if (!new_cap) return -ENOMEM;
}
/* * prealloc xattr data, if it looks like we'll need it. only * if len > 4 (meaning there are actually xattrs; the first 4 * bytes are the xattr count).
*/ if (iinfo->xattr_len > 4) {
xattr_blob = ceph_buffer_new(iinfo->xattr_len, GFP_NOFS); if (!xattr_blob)
pr_err_client(cl, "ENOMEM xattr blob %d bytes\n",
iinfo->xattr_len);
}
if (iinfo->pool_ns_len > 0)
pool_ns = ceph_find_or_create_string(iinfo->pool_ns_data,
iinfo->pool_ns_len);
if (ceph_snap(inode) != CEPH_NOSNAP && !ci->i_snapid_map)
ci->i_snapid_map = ceph_get_snapid_map(mdsc, ceph_snap(inode));
spin_lock(&ci->i_ceph_lock);
/* * provided version will be odd if inode value is projected, * even if stable. skip the update if we have newer stable * info (ours>=theirs, e.g. due to racing mds replies), unless * we are getting projected (unstable) info (in which case the * version is odd, and we want ours>theirs). * us them * 2 2 skip * 3 2 skip * 3 3 update
*/ if (ci->i_version == 0 ||
((info->cap.flags & CEPH_CAP_FLAG_AUTH) &&
le64_to_cpu(info->version) > (ci->i_version & ~1)))
new_version = true;
/* directories have fl_stripe_unit set to zero */ if (IS_ENCRYPTED(inode))
inode->i_blkbits = CEPH_FSCRYPT_BLOCK_SHIFT; elseif (le32_to_cpu(info->layout.fl_stripe_unit))
inode->i_blkbits =
fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1; else
inode->i_blkbits = CEPH_BLOCK_SHIFT;
queue_trunc = ceph_fill_file_size(inode, issued,
le32_to_cpu(info->truncate_seq),
le64_to_cpu(info->truncate_size),
size); /* only update max_size on auth cap */ if ((info->cap.flags & CEPH_CAP_FLAG_AUTH) &&
ci->i_max_size != le64_to_cpu(info->max_size)) {
doutc(cl, "max_size %lld -> %llu\n",
ci->i_max_size, le64_to_cpu(info->max_size));
ci->i_max_size = le64_to_cpu(info->max_size);
}
}
/* layout and rstat are not tracked by capability, update them if
* the inode info is from auth mds */ if (new_version || (info->cap.flags & CEPH_CAP_FLAG_AUTH)) { if (S_ISDIR(inode->i_mode)) {
ci->i_dir_layout = iinfo->dir_layout;
ci->i_rbytes = le64_to_cpu(info->rbytes);
ci->i_rfiles = le64_to_cpu(info->rfiles);
ci->i_rsubdirs = le64_to_cpu(info->rsubdirs);
ci->i_dir_pin = iinfo->dir_pin;
ci->i_rsnaps = iinfo->rsnaps;
ceph_decode_timespec64(&ci->i_rctime, &info->rctime);
}
}
/* xattrs */ /* note that if i_xattrs.len <= 4, i_xattrs.data will still be NULL. */ if ((ci->i_xattrs.version == 0 || !(issued & CEPH_CAP_XATTR_EXCL)) &&
le64_to_cpu(info->xattr_version) > ci->i_xattrs.version) { if (ci->i_xattrs.blob)
old_blob = ci->i_xattrs.blob;
ci->i_xattrs.blob = xattr_blob; if (xattr_blob)
memcpy(ci->i_xattrs.blob->vec.iov_base,
iinfo->xattr_data, iinfo->xattr_len);
ci->i_xattrs.version = le64_to_cpu(info->xattr_version);
ceph_forget_all_cached_acls(inode);
ceph_security_invalidate_secctx(inode);
xattr_blob = NULL;
}
spin_lock(&ci->i_ceph_lock); if (!ci->i_symlink)
ci->i_symlink = sym; else
kfree(sym); /* lost a race */
}
if (IS_ENCRYPTED(inode)) { /* * Encrypted symlinks need to be decrypted before we can * cache their targets in i_link. Don't touch it here.
*/
inode->i_op = &ceph_encrypted_symlink_iops;
} else {
inode->i_link = ci->i_symlink;
inode->i_op = &ceph_symlink_iops;
} break; case S_IFDIR:
inode->i_op = &ceph_dir_iops;
inode->i_fop = &ceph_dir_fops; break; default:
pr_err_client(cl, "%p %llx.%llx BAD mode 0%o\n", inode,
ceph_vinop(inode), inode->i_mode);
}
/* were we issued a capability? */ if (info_caps) { if (ceph_snap(inode) == CEPH_NOSNAP) {
ceph_add_cap(inode, session,
le64_to_cpu(info->cap.cap_id),
info_caps,
le32_to_cpu(info->cap.wanted),
le32_to_cpu(info->cap.seq),
le32_to_cpu(info->cap.mseq),
le64_to_cpu(info->cap.realm),
info->cap.flags, &new_cap);
if (iinfo->inline_version > 0 &&
iinfo->inline_version >= ci->i_inline_version) { int cache_caps = CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO;
ci->i_inline_version = iinfo->inline_version; if (ceph_has_inline_data(ci) &&
(locked_page || (info_caps & cache_caps)))
fill_inline = true;
}
if (cap_fmode >= 0) { if (!info_caps)
pr_warn_client(cl, "mds issued no caps on %llx.%llx\n",
ceph_vinop(inode));
__ceph_touch_fmode(ci, mdsc, cap_fmode);
}
spin_unlock(&ci->i_ceph_lock);
ceph_fscache_register_inode_cookie(inode);
if (fill_inline)
ceph_fill_inline_data(inode, locked_page,
iinfo->inline_data, iinfo->inline_len);
if (wake)
wake_up_all(&ci->i_cap_wq);
/* queue truncate if we saw i_size decrease */ if (queue_trunc)
ceph_queue_vmtruncate(inode);
/* populate frag tree */ if (S_ISDIR(inode->i_mode))
ceph_fill_fragtree(inode, &info->fragtree, dirinfo);
/* update delegation info? */ if (dirinfo)
ceph_fill_dirfrag(inode, dirinfo);
spin_lock(&dentry->d_lock); /* make sure dentry's name matches target */ if (dentry->d_name.len != dname_len ||
memcmp(dentry->d_name.name, dname, dname_len)) goto out_unlock;
dir = d_inode(dentry->d_parent); /* make sure parent matches dvino */ if (!ceph_ino_compare(dir, pdvino)) goto out_unlock;
/* make sure dentry's inode matches target. NULL ptvino means that
* we expect a negative dentry */ if (ptvino) { if (d_really_is_negative(dentry)) goto out_unlock; if (!ceph_ino_compare(d_inode(dentry), ptvino)) goto out_unlock;
} else { if (d_really_is_positive(dentry)) goto out_unlock;
}
/* * splice a dentry to an inode. * caller must hold directory i_rwsem for this to be safe.
*/ staticint splice_dentry(struct dentry **pdn, struct inode *in)
{ struct ceph_client *cl = ceph_inode_to_client(in); struct dentry *dn = *pdn; struct dentry *realdn;
BUG_ON(d_inode(dn));
if (S_ISDIR(in->i_mode)) { /* If inode is directory, d_splice_alias() below will remove * 'realdn' from its origin parent. We need to ensure that * origin parent's readdir cache will not reference 'realdn'
*/
realdn = d_find_any_alias(in); if (realdn) { struct ceph_dentry_info *di = ceph_dentry(realdn);
spin_lock(&realdn->d_lock);
/* * Incorporate results into the local cache. This is either just * one inode, or a directory, dentry, and possibly linked-to inode (e.g., * after a lookup). * * A reply may contain * a directory inode along with a dentry. * and/or a target inode * * Called with snap_rwsem (read).
*/ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
{ struct ceph_mds_session *session = req->r_session; struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info; struct inode *in = NULL; struct ceph_vino tvino, dvino; struct ceph_fs_client *fsc = ceph_sb_to_fs_client(sb); struct ceph_client *cl = fsc->client; struct inode *parent_dir = NULL; int err = 0;
if (!rinfo->head->is_target && !rinfo->head->is_dentry) {
doutc(cl, "reply is empty!\n"); if (rinfo->head->result == 0 && req->r_parent)
ceph_invalidate_dir_request(req); return 0;
}
if (rinfo->head->is_dentry) { /* * r_parent may be stale, in cases when R_PARENT_LOCKED is not set, * so we need to get the correct inode
*/
parent_dir = ceph_get_reply_dir(sb, req->r_parent, rinfo); if (unlikely(IS_ERR(parent_dir))) {
err = PTR_ERR(parent_dir); goto done;
} if (parent_dir) {
err = ceph_fill_inode(parent_dir, NULL, &rinfo->diri,
rinfo->dirfrag, session, -1,
&req->r_caps_reservation); if (err < 0) goto done;
} else {
WARN_ON_ONCE(1);
}
/* do we have a lease on the whole dir? */
have_dir_cap =
(le32_to_cpu(rinfo->diri.in->cap.caps) &
CEPH_CAP_FILE_SHARED);
/* do we have a dn lease? */
have_lease = have_dir_cap ||
le32_to_cpu(rinfo->dlease->duration_ms); if (!have_lease)
doutc(cl, "no dentry lease or dir cap\n");
/* ensure target dentry is invalidated, despite
rehashing bug in vfs_rename_dir */
ceph_invalidate_dentry_lease(dn);
doutc(cl, "dn %p gets new offset %lld\n",
req->r_old_dentry,
ceph_dentry(req->r_old_dentry)->offset);
/* swap r_dentry and r_old_dentry in case that * splice_dentry() gets called later. This is safe
* because no other place will use them */
req->r_dentry = req->r_old_dentry;
req->r_old_dentry = dn;
dn = req->r_dentry;
}
/* null dentry? */ if (!rinfo->head->is_target) {
doutc(cl, "null dentry\n"); if (d_really_is_positive(dn)) {
doutc(cl, "d_delete %p\n", dn);
ceph_dir_clear_ordered(dir);
d_delete(dn);
} elseif (have_lease) { if (d_unhashed(dn))
d_add(dn, NULL);
}
if (rinfo->dir_dir &&
le32_to_cpu(rinfo->dir_dir->frag) != frag) {
doutc(cl, "got new frag %x -> %x\n", frag,
le32_to_cpu(rinfo->dir_dir->frag));
frag = le32_to_cpu(rinfo->dir_dir->frag); if (!rinfo->hash_order)
req->r_readdir_offset = 2;
}
if (le32_to_cpu(rinfo->head->op) == CEPH_MDS_OP_LSSNAP) {
doutc(cl, "%d items under SNAPDIR dn %p\n",
rinfo->dir_nr, parent);
} else {
doutc(cl, "%d items under dn %p\n", rinfo->dir_nr, parent); if (rinfo->dir_dir)
ceph_fill_dirfrag(d_inode(parent), rinfo->dir_dir);
if (ceph_frag_is_leftmost(frag) &&
req->r_readdir_offset == 2 &&
!(rinfo->hash_order && last_hash)) { /* note dir version at start of readdir so we can
* tell if any dentries get dropped */
req->r_dir_release_cnt =
atomic64_read(&ci->i_release_count);
req->r_dir_ordered_cnt =
atomic64_read(&ci->i_ordered_count);
req->r_readdir_cache_idx = 0;
}
}
spin_lock(&ci->i_ceph_lock); if (orig_gen == ci->i_rdcache_gen &&
orig_gen == ci->i_rdcache_revoking) {
doutc(cl, "%p %llx.%llx gen %d successful\n", inode,
ceph_vinop(inode), ci->i_rdcache_gen);
ci->i_rdcache_revoking--;
check = 1;
} else {
doutc(cl, "%p %llx.%llx gen %d raced, now %d revoking %d\n",
inode, ceph_vinop(inode), orig_gen, ci->i_rdcache_gen,
ci->i_rdcache_revoking); if (__ceph_caps_revoking_other(ci, NULL, CEPH_CAP_FILE_CACHE))
check = 1;
}
spin_unlock(&ci->i_ceph_lock);
mutex_unlock(&ci->i_truncate_mutex);
out: if (check)
ceph_check_caps(ci, 0);
}
/* * Make sure any pending truncation is applied before doing anything * that may depend on it.
*/ void __ceph_do_pending_vmtruncate(struct inode *inode)
{ struct ceph_client *cl = ceph_inode_to_client(inode); struct ceph_inode_info *ci = ceph_inode(inode);
u64 to; int wrbuffer_refs, finish = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.