@@ -270,7 +270,9 @@ static int ll_revalidate_dentry(struct dentry *dentry,
if (lookup_flags & LOOKUP_RCU)
return -ECHILD;
- ll_statahead(dir, &dentry, !d_inode(dentry));
+ if (dentry_may_statahead(dir, dentry))
+ ll_revalidate_statahead(dir, &dentry, !d_inode(dentry));
+
return 1;
}
@@ -4555,23 +4555,66 @@ static int ll_merge_md_attr(struct inode *inode)
int ll_getattr(const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int flags)
{
- struct inode *inode = d_inode(path->dentry);
+ struct dentry *de = path->dentry;
+ struct inode *inode = d_inode(de);
struct ll_sb_info *sbi = ll_i2sbi(inode);
struct ll_inode_info *lli = ll_i2info(inode);
+ struct inode *dir = de->d_parent->d_inode;
+ bool need_glimpse = true;
ktime_t kstart = ktime_get();
int rc;
- rc = ll_inode_revalidate(path->dentry, IT_GETATTR);
+ /* The OST object(s) determine the file size, blocks and mtime. */
+ if (!(request_mask & STATX_SIZE || request_mask & STATX_BLOCKS ||
+ request_mask & STATX_MTIME))
+ need_glimpse = false;
+
+ if (dentry_may_statahead(dir, de))
+ ll_start_statahead(dir, de, need_glimpse &&
+ !(flags & AT_STATX_DONT_SYNC));
+
+ if (flags & AT_STATX_DONT_SYNC) {
+ rc = 0;
+ goto fill_attr;
+ }
+
+ rc = ll_inode_revalidate(de, IT_GETATTR);
if (rc < 0)
return rc;
if (S_ISREG(inode->i_mode)) {
bool cached;
- rc = pcc_inode_getattr(inode, &cached);
+ if (!need_glimpse)
+ goto fill_attr;
+
+ rc = pcc_inode_getattr(inode, request_mask, flags, &cached);
if (cached && rc < 0)
return rc;
+ if (cached)
+ goto fill_attr;
+
+ /*
+ * If the returned attr is masked with OBD_MD_FLSIZE &
+ * OBD_MD_FLBLOCKS & OBD_MD_FLMTIME, it means that the file size
+ * or blocks obtained from MDT is strictly correct, and the file
+ * is usually not being modified by clients, and the [a|m|c]time
+ * got from MDT is also strictly correct.
+ * Under this circumstance, it does not need to send glimpse
+ * RPCs to OSTs for file attributes such as the size and blocks.
+ */
+ if (lli->lli_attr_valid & OBD_MD_FLSIZE &&
+ lli->lli_attr_valid & OBD_MD_FLBLOCKS &&
+ lli->lli_attr_valid & OBD_MD_FLMTIME) {
+ inode->i_mtime.tv_sec = lli->lli_mtime;
+ if (lli->lli_attr_valid & OBD_MD_FLATIME)
+ inode->i_atime.tv_sec = lli->lli_atime;
+ if (lli->lli_attr_valid & OBD_MD_FLCTIME)
+ inode->i_ctime.tv_sec = lli->lli_ctime;
+ goto fill_attr;
+ }
+
/* In case of restore, the MDT has the right size and has
* already send it back without granting the layout lock,
* inode is up-to-date so glimpse is useless.
@@ -4579,8 +4622,7 @@ int ll_getattr(const struct path *path, struct kstat *stat,
* restore the MDT holds the layout lock so the glimpse will
* block up to the end of restore (getattr will block)
*/
- if (!cached && !test_bit(LLIF_FILE_RESTORING,
- &lli->lli_flags)) {
+ if (!test_bit(LLIF_FILE_RESTORING, &lli->lli_flags)) {
rc = ll_glimpse_size(inode);
if (rc < 0)
return rc;
@@ -4593,11 +4635,15 @@ int ll_getattr(const struct path *path, struct kstat *stat,
return rc;
}
- inode->i_atime.tv_sec = lli->lli_atime;
- inode->i_mtime.tv_sec = lli->lli_mtime;
- inode->i_ctime.tv_sec = lli->lli_ctime;
+ if (lli->lli_attr_valid & OBD_MD_FLATIME)
+ inode->i_atime.tv_sec = lli->lli_atime;
+ if (lli->lli_attr_valid & OBD_MD_FLMTIME)
+ inode->i_mtime.tv_sec = lli->lli_mtime;
+ if (lli->lli_attr_valid & OBD_MD_FLCTIME)
+ inode->i_ctime.tv_sec = lli->lli_ctime;
}
+fill_attr:
OBD_FAIL_TIMEOUT(OBD_FAIL_GETATTR_DELAY, 30);
stat->dev = inode->i_sb->s_dev;
@@ -4631,6 +4677,24 @@ int ll_getattr(const struct path *path, struct kstat *stat,
stat->size = i_size_read(inode);
stat->blocks = inode->i_blocks;
+ if (flags & AT_STATX_DONT_SYNC) {
+ if (stat->size == 0 &&
+ lli->lli_attr_valid & OBD_MD_FLLAZYSIZE)
+ stat->size = lli->lli_lazysize;
+ if (stat->blocks == 0 &&
+ lli->lli_attr_valid & OBD_MD_FLLAZYBLOCKS)
+ stat->blocks = lli->lli_lazyblocks;
+ }
+
+ if (lli->lli_attr_valid & OBD_MD_FLBTIME) {
+ stat->result_mask |= STATX_BTIME;
+ stat->btime.tv_sec = lli->lli_btime;
+ }
+
+ stat->attributes_mask = STATX_ATTR_IMMUTABLE | STATX_ATTR_APPEND;
+ stat->attributes |= ll_inode_to_ext_flags(inode->i_flags);
+ stat->result_mask &= request_mask;
+
ll_stats_ops_tally(sbi, LPROC_LL_GETATTR,
ktime_us_delta(ktime_get(), kstart));
@@ -143,6 +143,7 @@ struct ll_inode_info {
s64 lli_atime;
s64 lli_mtime;
s64 lli_ctime;
+ s64 lli_btime;
spinlock_t lli_agl_lock;
/* Try to make the d::member and f::member are aligned. Before using
@@ -233,6 +234,10 @@ struct ll_inode_info {
struct mutex lli_group_mutex;
u64 lli_group_users;
unsigned long lli_group_gid;
+
+ u64 lli_attr_valid;
+ u64 lli_lazysize;
+ u64 lli_lazyblocks;
};
};
@@ -1349,7 +1354,9 @@ struct ll_statahead_info {
atomic_t sai_cache_count; /* entry count in cache */
};
-int ll_statahead(struct inode *dir, struct dentry **dentry, bool unplug);
+int ll_revalidate_statahead(struct inode *dir, struct dentry **dentry,
+ bool unplug);
+int ll_start_statahead(struct inode *dir, struct dentry *dentry, bool agl);
void ll_authorize_statahead(struct inode *dir, void *key);
void ll_deauthorize_statahead(struct inode *dir, void *key);
@@ -1433,7 +1440,8 @@ static inline int ll_glimpse_size(struct inode *inode)
* 'lld_sa_generation == lli->lli_sa_generation'.
*/
ldd = ll_d2d(dentry);
- if (ldd->lld_sa_generation == lli->lli_sa_generation)
+ if (lli->lli_sa_generation &&
+ ldd->lld_sa_generation == lli->lli_sa_generation)
return false;
return true;
@@ -2049,6 +2049,9 @@ int ll_update_inode(struct inode *inode, struct lustre_md *md)
lli->lli_ctime = body->mbo_ctime;
}
+ if (body->mbo_valid & OBD_MD_FLBTIME)
+ lli->lli_btime = body->mbo_btime;
+
/* Clear i_flags to remove S_NOSEC before permissions are updated */
if (body->mbo_valid & OBD_MD_FLFLAGS)
ll_update_inode_flags(inode, body->mbo_flags);
@@ -2085,6 +2088,7 @@ int ll_update_inode(struct inode *inode, struct lustre_md *md)
LASSERT(fid_seq(&lli->lli_fid) != 0);
+ lli->lli_attr_valid = body->mbo_valid;
if (body->mbo_valid & OBD_MD_FLSIZE) {
i_size_write(inode, body->mbo_size);
@@ -2094,6 +2098,11 @@ int ll_update_inode(struct inode *inode, struct lustre_md *md)
if (body->mbo_valid & OBD_MD_FLBLOCKS)
inode->i_blocks = body->mbo_blocks;
+ } else {
+ if (body->mbo_valid & OBD_MD_FLLAZYSIZE)
+ lli->lli_lazysize = body->mbo_size;
+ if (body->mbo_valid & OBD_MD_FLLAZYBLOCKS)
+ lli->lli_lazyblocks = body->mbo_blocks;
}
if (body->mbo_valid & OBD_MD_TSTATE) {
@@ -751,7 +751,7 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
it = &lookup_it;
if (it->it_op == IT_GETATTR && dentry_may_statahead(parent, dentry)) {
- rc = ll_statahead(parent, &dentry, 0);
+ rc = ll_revalidate_statahead(parent, &dentry, 0);
if (rc == 1) {
if (dentry == save)
retval = NULL;
@@ -1673,7 +1673,8 @@ int pcc_inode_setattr(struct inode *inode, struct iattr *attr,
return rc;
}
-int pcc_inode_getattr(struct inode *inode, bool *cached)
+int pcc_inode_getattr(struct inode *inode, u32 request_mask,
+ unsigned int flags, bool *cached)
{
struct ll_inode_info *lli = ll_i2info(inode);
const struct cred *old_cred;
@@ -1694,7 +1695,7 @@ int pcc_inode_getattr(struct inode *inode, bool *cached)
old_cred = override_creds(pcc_super_cred(inode->i_sb));
rc = vfs_getattr(&ll_i2pcci(inode)->pcci_path, &stat,
- STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT);
+ request_mask, flags);
revert_creds(old_cred);
if (rc)
goto out;
@@ -238,7 +238,8 @@ ssize_t pcc_file_read_iter(struct kiocb *iocb, struct iov_iter *iter,
bool *cached);
ssize_t pcc_file_write_iter(struct kiocb *iocb, struct iov_iter *iter,
bool *cached);
-int pcc_inode_getattr(struct inode *inode, bool *cached);
+int pcc_inode_getattr(struct inode *inode, u32 request_mask,
+ unsigned int flags, bool *cached);
int pcc_inode_setattr(struct inode *inode, struct iattr *attr, bool *cached);
ssize_t pcc_file_splice_read(struct file *in_file, loff_t *ppos,
struct pipe_inode_info *pipe, size_t count,
@@ -965,10 +965,12 @@ static void ll_start_agl(struct dentry *parent, struct ll_statahead_info *sai)
plli->lli_opendir_pid);
if (IS_ERR(task)) {
CERROR("can't start ll_agl thread, rc: %ld\n", PTR_ERR(task));
+ sai->sai_agl_valid = 0;
return;
}
sai->sai_agl_task = task;
+ LASSERT(sai->sai_agl_valid == 1);
atomic_inc(&ll_i2sbi(d_inode(parent))->ll_agl_total);
spin_lock(&plli->lli_agl_lock);
sai->sai_agl_valid = 1;
@@ -1521,6 +1523,7 @@ static int revalidate_statahead_dentry(struct inode *dir,
* @dir: parent directory
* @dentry: dentry that triggers statahead, normally the first
* dirent under @dir
+ * @agl indicate whether AGL is needed
*
* Returns: -EAGAIN on success, because when this function is
* called, it's already in lookup call, so client should
@@ -1529,7 +1532,8 @@ static int revalidate_statahead_dentry(struct inode *dir,
*
* negative number upon error
*/
-static int start_statahead_thread(struct inode *dir, struct dentry *dentry)
+static int start_statahead_thread(struct inode *dir, struct dentry *dentry,
+ bool agl)
{
struct ll_inode_info *lli = ll_i2info(dir);
struct ll_statahead_info *sai = NULL;
@@ -1562,6 +1566,8 @@ static int start_statahead_thread(struct inode *dir, struct dentry *dentry)
}
sai->sai_ls_all = (first == LS_FIRST_DOT_DE);
+ sai->sai_agl_valid = agl;
+
/*
* if current lli_opendir_key was deauthorized, or dir re-opened by
* another process, don't start statahead, otherwise the newly spawned
@@ -1592,7 +1598,7 @@ static int start_statahead_thread(struct inode *dir, struct dentry *dentry)
goto out;
}
- if (ll_i2sbi(parent->d_inode)->ll_flags & LL_SBI_AGL_ENABLED)
+ if (ll_i2sbi(parent->d_inode)->ll_flags & LL_SBI_AGL_ENABLED && agl)
ll_start_agl(parent, sai);
atomic_inc(&ll_i2sbi(parent->d_inode)->ll_sa_total);
@@ -1615,6 +1621,7 @@ static int start_statahead_thread(struct inode *dir, struct dentry *dentry)
if (lli->lli_opendir_pid == current->pid)
lli->lli_sa_enabled = 0;
spin_unlock(&lli->lli_sa_lock);
+
if (sai)
ll_sai_free(sai);
if (first != LS_NOT_FIRST_DE)
@@ -1623,11 +1630,50 @@ static int start_statahead_thread(struct inode *dir, struct dentry *dentry)
return rc;
}
+/*
+ * Check whether statahead for @dir was started.
+ */
+static inline bool ll_statahead_started(struct inode *dir, bool agl)
+{
+ struct ll_inode_info *lli = ll_i2info(dir);
+ struct ll_statahead_info *sai;
+
+ spin_lock(&lli->lli_sa_lock);
+ sai = lli->lli_sai;
+ if (sai && sai->sai_agl_valid != agl)
+ CDEBUG(D_READA,
+ "%s: Statahead AGL hint changed from %d to %d\n",
+ ll_i2sbi(dir)->ll_fsname, sai->sai_agl_valid, agl);
+ spin_unlock(&lli->lli_sa_lock);
+
+ return !!sai;
+}
+
/**
* statahead entry function, this is called when client getattr on a file, it
* will start statahead thread if this is the first dir entry, else revalidate
* dentry from statahead cache.
*
+ * @dir parent directory
+ * @dentryp dentry to getattr
+ * @agl whether start the agl thread
+ *
+ * Return: 1 on success
+ * 0 revalidation from statahead cache failed, caller needs
+ * to getattr from server directly
+ * negative number on error, caller often ignores this and
+ * then getattr from server
+ */
+int ll_start_statahead(struct inode *dir, struct dentry *dentry, bool agl)
+{
+ if (!ll_statahead_started(dir, agl))
+ return start_statahead_thread(dir, dentry, agl);
+ return 0;
+}
+
+/**
+ * revalidate dentry from statahead cache.
+ *
* @dir: parent directory
* @dentryp: dentry to getattr
* @unplug: unplug statahead window only (normally for negative
@@ -1638,19 +1684,18 @@ static int start_statahead_thread(struct inode *dir, struct dentry *dentry)
* negative number on error, caller often ignores this and
* then getattr from server
*/
-int ll_statahead(struct inode *dir, struct dentry **dentryp, bool unplug)
+int ll_revalidate_statahead(struct inode *dir, struct dentry **dentryp,
+ bool unplug)
{
struct ll_statahead_info *sai;
+ int rc = 0;
sai = ll_sai_get(dir);
if (sai) {
- int rc;
-
rc = revalidate_statahead_dentry(dir, sai, dentryp, unplug);
CDEBUG(D_READA, "revalidate statahead %pd: %d.\n",
*dentryp, rc);
ll_sai_put(sai);
- return rc;
}
- return start_statahead_thread(dir, *dentryp);
+ return rc;
}