Viewing: statahead.c

// SPDX-License-Identifier: GPL-2.0

/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 * Use is subject to license terms.
 *
 * Copyright (c) 2011, 2017, Intel Corporation.
 */

/*
 * This file is part of Lustre, http://www.lustre.org/
 */

#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
#include <linux/delay.h>

#define DEBUG_SUBSYSTEM S_LLITE

#include <obd_support.h>
#include <lustre_dlm.h>
#include "llite_internal.h"

#define SA_OMITTED_ENTRY_MAX 8ULL

enum sa_entry_state {
	/** negative values are for error cases */
	SA_ENTRY_INIT = 0,      /** init entry */
	SA_ENTRY_SUCC = 1,      /** stat succeed */
	SA_ENTRY_INVA = 2,      /** invalid entry */
};

/*
 * sa_entry is not refcounted: statahead thread allocates it and do async stat,
 * and in async stat callback ll_statahead_interpret() will prepare the inode
 * and set lock data in the ptlrpcd context. Then the scanner process will be
 * woken up if this entry is the waiting one, can access and free it.
 */
struct sa_entry {
	/* link into sai_entries */
	struct list_head		 se_list;
	/* link into sai hash table locally */
	struct list_head		 se_hash;
	/* entry index in the sai */
	__u64				 se_index;
	/* low layer ldlm lock handle */
	__u64				 se_handle;
	/* entry status */
	enum sa_entry_state		 se_state;
	/* entry size, contains name */
	int				 se_size;
	/* pointer to the target inode */
	struct inode			*se_inode;
	/* pointer to @sai per process struct */
	struct ll_statahead_info	*se_sai;
	/* entry name */
	struct qstr			 se_qstr;
	/* entry fid */
	struct lu_fid			 se_fid;
};

static unsigned int sai_generation;
static DEFINE_SPINLOCK(sai_generation_lock);

static inline int sa_unhashed(struct sa_entry *entry)
{
	return list_empty(&entry->se_hash);
}

/* sa_entry is ready to use */
static inline int sa_ready(struct sa_entry *entry)
{
	/* Make sure sa_entry is updated and ready to use */
	smp_rmb();
	return (entry->se_state != SA_ENTRY_INIT);
}

/* hash value to put in sai_cache */
static inline int sa_hash(int val)
{
	return val & LL_SA_CACHE_MASK;
}

/* hash entry into sax_cache */
static inline void
sa_rehash(struct ll_statahead_context *ctx, struct sa_entry *entry)
{
	int i = sa_hash(entry->se_qstr.hash);

	spin_lock(&ctx->sax_cache_lock[i]);
	list_add_tail(&entry->se_hash, &ctx->sax_cache[i]);
	spin_unlock(&ctx->sax_cache_lock[i]);
}

/* unhash entry from sai_cache */
static inline int sa_unhash(struct ll_statahead_context *ctx,
			    struct sa_entry *entry, bool inuse_check)
{
	struct ll_statahead_info *sai = entry->se_sai;
	int i = sa_hash(entry->se_qstr.hash);
	int rc = 0;

	if (inuse_check && atomic_read(&sai->sai_inuse_count) > 0)
		return -EAGAIN;

	spin_lock(&ctx->sax_cache_lock[i]);
	if (inuse_check && atomic_read(&sai->sai_inuse_count) > 0)
		rc = -EAGAIN;
	else
		list_del_init(&entry->se_hash);
	spin_unlock(&ctx->sax_cache_lock[i]);

	return rc;
}

static inline int agl_should_run(struct ll_statahead_info *sai,
				 struct inode *inode)
{
	return inode && S_ISREG(inode->i_mode) && sai->sai_agl_task;
}

static inline struct ll_inode_info *
agl_first_entry(struct ll_statahead_info *sai)
{
	return list_first_entry(&sai->sai_agls, struct ll_inode_info,
				lli_agl_list);
}

/* statahead window is full */
static inline int sa_sent_full(struct ll_statahead_info *sai)
{
	return atomic_read(&sai->sai_cache_count) >= sai->sai_max;
}

/* Batch metadata handle */
static inline bool sa_has_batch_handle(struct ll_statahead_info *sai)
{
	return sai->sai_bh != NULL;
}

static inline void ll_statahead_flush_nowait(struct ll_statahead_info *sai)
{
	if (sa_has_batch_handle(sai)) {
		sai->sai_index_end = sai->sai_index - 1;
		(void) md_batch_flush(ll_i2mdexp(sai->sai_dentry->d_inode),
				      sai->sai_bh, false);
	}
}

static inline int agl_list_empty(struct ll_statahead_info *sai)
{
	return list_empty(&sai->sai_agls);
}

/**
 * sa_low_hit() - Return if hit ratio is low
 *
 * @sai: (ll_statahead_info struct) holds info about the SA process for a
 * directory
 *
 * Low hit ratio is defined as:
 * (1) hit ratio less than 80%
 * or
 * (2) consecutive miss more than 32, this means low hit.
 *
 * Returns:
 * * %0  Hit ratio is not low, else if !0 then hit ratio is low
 */
static inline int sa_low_hit(struct ll_statahead_info *sai)
{
	return ((sai->sai_hit > 32 && sai->sai_hit < 4 * sai->sai_miss) ||
		(sai->sai_consecutive_miss > 32));
}

/*
 * if the given index is behind of statahead window more than
 * SA_OMITTED_ENTRY_MAX, then it is old.
 */
static inline int is_omitted_entry(struct ll_statahead_info *sai, __u64 index)
{
	return ((__u64)sai->sai_max + index + SA_OMITTED_ENTRY_MAX <
		sai->sai_index);
}

/* allocate sa_entry and hash it to allow scanner process to find it */
static struct sa_entry *
sa_alloc(struct dentry *parent, struct ll_statahead_info *sai, __u64 index,
	 const char *name, int len, const struct lu_fid *fid)
{
	struct ll_inode_info *lli;
	struct sa_entry *entry;
	int entry_size;
	char *dname;

	ENTRY;

	entry_size = sizeof(struct sa_entry) +
		     round_up(len + 1 /* for trailing NUL */, 4);
	OBD_ALLOC(entry, entry_size);
	if (unlikely(!entry))
		RETURN(ERR_PTR(-ENOMEM));

	CDEBUG(D_READA, "alloc sa entry "DNAME"(%p) index %llu\n",
	       encode_fn_dname(len, name), entry, index);

	entry->se_index = index;
	entry->se_sai = sai;

	entry->se_state = SA_ENTRY_INIT;
	entry->se_size = entry_size;
	dname = (char *)entry + sizeof(struct sa_entry);
	memcpy(dname, name, len);
	dname[len] = 0;
	entry->se_qstr.hash = full_name_hash(parent, name, len);
	entry->se_qstr.len = len;
	entry->se_qstr.name = dname;

	if (fid)
		entry->se_fid = *fid;

	lli = ll_i2info(sai->sai_dentry->d_inode);
	spin_lock(&lli->lli_sa_lock);
	INIT_LIST_HEAD(&entry->se_list);
	sa_rehash(lli->lli_sax, entry);
	spin_unlock(&lli->lli_sa_lock);

	atomic_inc(&sai->sai_cache_count);

	RETURN(entry);
}

/* free sa_entry, which should have been unhashed and not in any list */
static void sa_free(struct ll_statahead_context *ctx, struct sa_entry *entry)
{
	CDEBUG(D_READA, "free sa entry "DNAME"(%p) index %llu\n",
	       encode_fn_qstr(entry->se_qstr), entry,
	       entry->se_index);

	LASSERT(list_empty(&entry->se_list));
	LASSERT(sa_unhashed(entry));

	OBD_FREE(entry, entry->se_size);
}

/*
 * Find sa_entry by name, used by directory scanner. If @sai_pid is not the PID
 * of the scanner (which means it may do statahead wrongly, return -EINVAL
 * immediately.
 */
static struct sa_entry *sa_get(struct ll_statahead_context *ctx,
			       const struct qstr *qstr,
			       struct ll_statahead_info **info)
{
	struct sa_entry *entry;
	int i = sa_hash(qstr->hash);

	spin_lock(&ctx->sax_cache_lock[i]);
	list_for_each_entry(entry, &ctx->sax_cache[i], se_hash) {
		if (entry->se_qstr.hash == qstr->hash &&
		    entry->se_qstr.len == qstr->len &&
		    memcmp(entry->se_qstr.name, qstr->name, qstr->len) == 0) {
			struct ll_statahead_info *sai = entry->se_sai;

			if (sai->sai_pid != current->pid) {
				CDEBUG(D_CACHE,
				       "%s: wrong pid=%d:%d for entry %.*s\n",
				       ll_i2sbi(ctx->sax_inode)->ll_fsname,
				       sai->sai_pid, current->pid,
				       entry->se_qstr.len, entry->se_qstr.name);
				entry = ERR_PTR(-EINVAL);
				*info = sai;
			}

			atomic_inc(&sai->sai_inuse_count);
			spin_unlock(&ctx->sax_cache_lock[i]);
			return entry;
		}
	}
	spin_unlock(&ctx->sax_cache_lock[i]);
	return NULL;
}

/* unhash and unlink sa_entry, and then free it */
static inline int sa_kill(struct ll_statahead_info *sai, struct sa_entry *entry,
			  bool locked, bool inuse_check)
{
	struct inode *dir = sai->sai_dentry->d_inode;
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_statahead_context *ctx = lli->lli_sax;
	int rc;

	LASSERT(!list_empty(&entry->se_list));
	LASSERT(sa_ready(entry));

	rc = sa_unhash(ctx, entry, inuse_check);
	if (rc)
		return rc;

	if (!locked)
		spin_lock(&lli->lli_sa_lock);
	list_del_init(&entry->se_list);
	spin_unlock(&lli->lli_sa_lock);

	iput(entry->se_inode);
	atomic_dec(&sai->sai_cache_count);

	sa_free(ctx, entry);
	if (locked)
		spin_lock(&lli->lli_sa_lock);

	return 0;
}

static inline int sa_kill_try(struct ll_statahead_info *sai,
			      struct sa_entry *entry, bool locked)
{
	return sa_kill(sai, entry, locked, true);
}

/* called by scanner after use, sa_entry will be killed */
static void sa_put(struct inode *dir, struct ll_statahead_info *sai,
		   struct sa_entry *entry, bool inuse)
{
	struct ll_inode_info *lli = ll_i2info(dir);
	struct sa_entry *tmp;
	bool wakeup = false;

	if (entry && entry->se_state == SA_ENTRY_SUCC) {
		struct ll_sb_info *sbi = ll_i2sbi(sai->sai_dentry->d_inode);

		sai->sai_hit++;
		sai->sai_consecutive_miss = 0;
		if (sai->sai_max < sbi->ll_sa_max) {
			sai->sai_max = min(2 * sai->sai_max, sbi->ll_sa_max);
			wakeup = true;
		} else if (sai->sai_max_batch_count > 0) {
			if (sai->sai_max >= sai->sai_max_batch_count &&
			   (sai->sai_index_end - entry->se_index) %
			   sai->sai_max_batch_count == 0) {
				wakeup = true;
			} else if (entry->se_index == sai->sai_index_end) {
				wakeup = true;
			}
		} else {
			wakeup = true;
		}
	} else if (sai) {
		sai->sai_miss++;
		sai->sai_consecutive_miss++;
		wakeup = true;
	}

	if (entry) {
		inuse = true;
		sa_kill(sai, entry, false, false);
		CFS_FAIL_TIMEOUT(OBD_FAIL_LLITE_STATAHEAD_PAUSE, cfs_fail_val);
	}

	spin_lock(&lli->lli_sa_lock);
	if (inuse) {
		/*
		 * kill old completed entries. Maybe kicking old entries can
		 * be ignored?
		 */
		while ((tmp = list_first_entry_or_null(&sai->sai_entries,
				struct sa_entry, se_list))) {
			if (!is_omitted_entry(sai, tmp->se_index))
				break;

			/* ll_sa_lock is dropped by sa_kill(), restart list */
			sa_kill(sai, tmp, true, false);
		}
	}
	if (wakeup && sai->sai_task)
		wake_up_process(sai->sai_task);
	if (inuse)
		atomic_dec(&sai->sai_inuse_count);
	spin_unlock(&lli->lli_sa_lock);
}

/*
 * update state and sort add entry to sai_entries by index, return true if
 * scanner is waiting on this entry.
 */
static bool
__sa_make_ready(struct ll_statahead_info *sai, struct sa_entry *entry, int ret)
{
	struct sa_entry *se;
	struct list_head *pos = &sai->sai_entries;
	__u64 index = entry->se_index;

	LASSERT(!sa_ready(entry));
	LASSERT(list_empty(&entry->se_list));

	list_for_each_entry_reverse(se, &sai->sai_entries, se_list) {
		if (se->se_index < entry->se_index) {
			pos = &se->se_list;
			break;
		}
	}
	list_add(&entry->se_list, pos);
	/*
	 * LU-9210: ll_statahead_interpet must be able to see this before
	 * we wake it up
	 */
	smp_store_release(&entry->se_state,
			  ret < 0 ? SA_ENTRY_INVA : SA_ENTRY_SUCC);

	return (index == sai->sai_index_wait);
}

/* finish async stat RPC arguments */
static void sa_fini_data(struct md_op_item *item)
{
	struct md_op_data *op_data = &item->mop_data;

	if (op_data->op_flags & MF_OPNAME_KMALLOCED)
		/* allocated via ll_setup_filename called from sa_prep_data */
		kfree(op_data->op_name);
	ll_unlock_md_op_lsm(&item->mop_data);
	iput(item->mop_dir);
	if (item->mop_subpill_allocated)
		OBD_FREE_PTR(item->mop_pill);
	OBD_FREE_PTR(item);
}

static int ll_statahead_interpret(struct md_op_item *item, int rc);

/*
 * prepare arguments for async stat RPC.
 */
static struct md_op_item *
sa_prep_data(struct inode *dir, struct inode *child, struct sa_entry *entry)
{
	struct md_op_item *item;
	struct ldlm_enqueue_info *einfo;
	struct md_op_data *op_data;

	OBD_ALLOC_PTR(item);
	if (!item)
		return ERR_PTR(-ENOMEM);

	op_data = ll_prep_md_op_data(&item->mop_data, dir, child,
				     entry->se_qstr.name, entry->se_qstr.len, 0,
				     LUSTRE_OPC_ANY, NULL);
	if (IS_ERR(op_data)) {
		OBD_FREE_PTR(item);
		return (struct md_op_item *)op_data;
	}

	if (!child)
		op_data->op_fid2 = entry->se_fid;

	item->mop_opc = MD_OP_GETATTR;
	item->mop_it.it_op = IT_GETATTR;
	item->mop_dir = igrab(dir);
	item->mop_cb = ll_statahead_interpret;
	item->mop_cbdata = entry;

	einfo = &item->mop_einfo;
	einfo->ei_type = LDLM_IBITS;
	einfo->ei_mode = it_to_lock_mode(&item->mop_it);
	einfo->ei_cb_bl = ll_md_blocking_ast;
	einfo->ei_cb_cp = ldlm_completion_ast;
	einfo->ei_cb_gl = NULL;
	einfo->ei_cbdata = NULL;
	einfo->ei_req_slot = 1;

	return item;
}

/*
 * release resources used in async stat RPC, update entry state and wakeup if
 * scanner process it waiting on this entry.
 */
static void
sa_make_ready(struct ll_statahead_info *sai, struct sa_entry *entry, int ret)
{
	struct ll_inode_info *lli = ll_i2info(sai->sai_dentry->d_inode);
	bool wakeup;

	spin_lock(&lli->lli_sa_lock);
	wakeup = __sa_make_ready(sai, entry, ret);
	spin_unlock(&lli->lli_sa_lock);

	if (wakeup)
		wake_up(&sai->sai_waitq);
}

/* insert inode into the list of sai_agls */
static void ll_agl_add(struct ll_statahead_info *sai,
		       struct inode *inode, int index)
{
	struct ll_inode_info *child  = ll_i2info(inode);
	struct ll_inode_info *parent = ll_i2info(sai->sai_dentry->d_inode);

	spin_lock(&child->lli_agl_lock);
	if (child->lli_agl_index == 0) {
		child->lli_agl_index = index;
		spin_unlock(&child->lli_agl_lock);

		LASSERT(list_empty(&child->lli_agl_list));

		spin_lock(&parent->lli_agl_lock);
		/* Re-check under the lock */
		if (agl_should_run(sai, inode)) {
			if (agl_list_empty(sai))
				wake_up_process(sai->sai_agl_task);
			igrab(inode);
			list_add_tail(&child->lli_agl_list, &sai->sai_agls);
		} else
			child->lli_agl_index = 0;
		spin_unlock(&parent->lli_agl_lock);
	} else {
		spin_unlock(&child->lli_agl_lock);
	}
}

/* Allocate sax */
static struct ll_statahead_context *ll_sax_alloc(struct inode *dir)
{
	struct ll_statahead_context *ctx;
	int i;

	ENTRY;

	OBD_ALLOC_PTR(ctx);
	if (ctx == NULL)
		RETURN(NULL);

	ctx->sax_inode = igrab(dir);
	atomic_set(&ctx->sax_refcount, 1);
	INIT_LIST_HEAD(&ctx->sax_sai_list);
	for (i = 0; i < LL_SA_CACHE_SIZE; i++) {
		INIT_LIST_HEAD(&ctx->sax_cache[i]);
		spin_lock_init(&ctx->sax_cache_lock[i]);
	}

	atomic_inc(&ll_i2sbi(dir)->ll_sa_refcnt);
	RETURN(ctx);
}

static inline void ll_sax_free(struct ll_statahead_context *ctx)
{
	struct ll_sb_info *sbi;

	LASSERT(ctx->sax_inode != NULL);
	sbi = ll_i2sbi(ctx->sax_inode);
	iput(ctx->sax_inode);
	atomic_dec(&sbi->ll_sa_refcnt);
	OBD_FREE_PTR(ctx);
}

static inline void __ll_sax_get(struct ll_statahead_context *ctx)
{
	atomic_inc(&ctx->sax_refcount);
}

static inline struct ll_statahead_context *ll_sax_get(struct inode *dir)
{
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_statahead_context *ctx = NULL;

	spin_lock(&lli->lli_sa_lock);
	ctx = lli->lli_sax;
	if (ctx)
		__ll_sax_get(ctx);
	spin_unlock(&lli->lli_sa_lock);

	return ctx;
}

static inline void ll_sax_put( struct ll_statahead_context *ctx)
{
	struct ll_inode_info *lli = ll_i2info(ctx->sax_inode);

	if (atomic_dec_and_lock(&ctx->sax_refcount, &lli->lli_sa_lock)) {
		LASSERT(list_empty(&ctx->sax_sai_list));
		lli->lli_sai = NULL;
		lli->lli_sax = NULL;
		if (lli->lli_sa_pattern & (LSA_PATTERN_ADVISE |
					   LSA_PATTERN_FNAME)) {
			lli->lli_opendir_key = NULL;
			lli->lli_stat_pid = 0;
			lli->lli_sa_enabled = 0;
		}
		lli->lli_sa_pattern = LSA_PATTERN_NONE;
		spin_unlock(&lli->lli_sa_lock);

		ll_sax_free(ctx);
	}
}

/* allocate sai */
static struct ll_statahead_info *ll_sai_alloc(struct dentry *dentry)
{
	struct ll_statahead_info *sai;
	struct ll_inode_info *lli = ll_i2info(dentry->d_inode);

	ENTRY;

	OBD_ALLOC_PTR(sai);
	if (!sai)
		RETURN(NULL);

	sai->sai_dentry = dget(dentry);
	atomic_set(&sai->sai_refcount, 1);
	sai->sai_max = ll_i2sbi(dentry->d_inode)->ll_sa_min;
	sai->sai_index = 1;
	init_waitqueue_head(&sai->sai_waitq);

	INIT_LIST_HEAD(&sai->sai_item);
	INIT_LIST_HEAD(&sai->sai_entries);
	INIT_LIST_HEAD(&sai->sai_agls);

	atomic_set(&sai->sai_cache_count, 0);
	atomic_set(&sai->sai_inuse_count, 0);
	spin_lock(&sai_generation_lock);
	lli->lli_sa_generation = ++sai_generation;
	if (unlikely(sai_generation == 0))
		lli->lli_sa_generation = ++sai_generation;
	spin_unlock(&sai_generation_lock);

	RETURN(sai);
}

/* free sai */
static inline void ll_sai_free(struct ll_statahead_info *sai)
{
	LASSERT(sai->sai_dentry != NULL);
	dput(sai->sai_dentry);
	OBD_FREE_PTR(sai);
}

static inline struct ll_statahead_info *
__ll_sai_get(struct ll_statahead_info *sai)
{
	atomic_inc(&sai->sai_refcount);
	return sai;
}

/*
 * put sai refcount after use, if refcount reaches zero, free sai and sa_entries
 * attached to it.
 */
static void ll_sai_put(struct ll_statahead_info *sai)
{
	struct ll_inode_info *lli = ll_i2info(sai->sai_dentry->d_inode);

	if (atomic_dec_and_lock(&sai->sai_refcount, &lli->lli_sa_lock)) {
		struct ll_sb_info *sbi = ll_i2sbi(sai->sai_dentry->d_inode);

		lli->lli_sai = NULL;
		list_del_init(&sai->sai_item);
		spin_unlock(&lli->lli_sa_lock);

		LASSERT(!sai->sai_task);
		LASSERT(!sai->sai_agl_task);
		LASSERT(sai->sai_sent == sai->sai_replied);

		LASSERT(atomic_read(&sai->sai_cache_count) == 0);
		LASSERT(agl_list_empty(sai));

		ll_sai_free(sai);
		atomic_dec(&sbi->ll_sa_running);
	}
}

/* Do NOT forget to drop inode refcount when into sai_agls. */
static void ll_agl_trigger(struct inode *inode, struct ll_statahead_info *sai)
{
	struct ll_inode_info *lli = ll_i2info(inode);
	u64 index = lli->lli_agl_index;
	ktime_t expire;
	int rc;

	ENTRY;

	LASSERT(list_empty(&lli->lli_agl_list));

	/* AGL maybe fall behind statahead with one entry */
	if (is_omitted_entry(sai, index + 1)) {
		lli->lli_agl_index = 0;
		iput(inode);
		RETURN_EXIT;
	}

	/*
	 * In case of restore, the MDT has the right size and has already
	 * sent it back without granting the layout lock, inode is up-to-date.
	 * Then AGL (async glimpse lock) is useless.
	 * Also to glimpse we need the layout, in case of a runninh restore
	 * the MDT holds the layout lock so the glimpse will block up to the
	 * end of restore (statahead/agl will block)
	 */
	if (test_bit(LLIF_FILE_RESTORING, &lli->lli_flags)) {
		lli->lli_agl_index = 0;
		iput(inode);
		RETURN_EXIT;
	}

	/* Someone is in glimpse (sync or async), do nothing. */
	rc = down_write_trylock(&lli->lli_glimpse_sem);
	if (rc == 0) {
		lli->lli_agl_index = 0;
		iput(inode);
		RETURN_EXIT;
	}

	/*
	 * Someone triggered glimpse within 1 sec before.
	 * 1) The former glimpse succeeded with glimpse lock granted by OST, and
	 *    if the lock is still cached on client, AGL needs to do nothing. If
	 *    it is cancelled by other client, AGL maybe cannot obtaion new lock
	 *    for no glimpse callback triggered by AGL.
	 * 2) The former glimpse succeeded, but OST did not grant glimpse lock.
	 *    Under such case, it is quite possible that the OST will not grant
	 *    glimpse lock for AGL also.
	 * 3) The former glimpse failed, compared with other two cases, it is
	 *    relative rare. AGL can ignore such case, and it will not muchly
	 *    affect the performance.
	 */
	expire = ktime_sub_ns(ktime_get(), NSEC_PER_SEC);
	if (ktime_to_ns(lli->lli_glimpse_time) &&
	    ktime_before(expire, lli->lli_glimpse_time)) {
		up_write(&lli->lli_glimpse_sem);
		lli->lli_agl_index = 0;
		iput(inode);
		RETURN_EXIT;
	}

	CDEBUG(D_READA,
	       "Handling (init) async glimpse: inode = " DFID", idx = %llu\n",
	       PFID(&lli->lli_fid), index);

	cl_agl(inode);
	lli->lli_agl_index = 0;
	lli->lli_glimpse_time = ktime_get();
	up_write(&lli->lli_glimpse_sem);

	CDEBUG(D_READA,
	       "Handled (init) async glimpse: inode= " DFID", idx = %llu, rc = %d\n",
	       PFID(&lli->lli_fid), index, rc);

	iput(inode);

	EXIT;
}

static void ll_statahead_interpret_fini(struct ll_inode_info *lli,
					struct ll_statahead_info *sai,
					struct md_op_item *item,
					struct sa_entry *entry,
					struct ptlrpc_request *req,
					int rc)
{
	/*
	 * First it will drop ldlm ibits lock refcount by calling
	 * ll_intent_drop_lock() in spite of failures. Do not worry about
	 * calling ll_intent_drop_lock() more than once.
	 */
	ll_intent_release(&item->mop_it);
	sa_fini_data(item);
	if (req)
		ptlrpc_req_put(req);
	sa_make_ready(sai, entry, rc);

	spin_lock(&lli->lli_sa_lock);
	sai->sai_replied++;
	spin_unlock(&lli->lli_sa_lock);
}

static void ll_statahead_interpret_work(struct work_struct *work)
{
	struct md_op_item *item = container_of(work, struct md_op_item,
					       mop_work);
	struct req_capsule *pill = item->mop_pill;
	struct inode *dir = item->mop_dir;
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_statahead_info *sai;
	struct lookup_intent *it;
	struct sa_entry *entry;
	struct mdt_body *body;
	struct inode *child;
	int rc;

	ENTRY;

	entry = (struct sa_entry *)item->mop_cbdata;
	LASSERT(entry->se_handle != 0);

	sai = entry->se_sai;
	it = &item->mop_it;
	body = req_capsule_server_get(pill, &RMF_MDT_BODY);
	if (!body)
		GOTO(out, rc = -EFAULT);

	child = entry->se_inode;
	/* revalidate; unlinked and re-created with the same name */
	if (unlikely(!fid_is_zero(&item->mop_data.op_fid2) &&
		     !lu_fid_eq(&item->mop_data.op_fid2, &body->mbo_fid1))) {
		if (child) {
			entry->se_inode = NULL;
			iput(child);
		}
		/* The mdt_body is invalid. Skip this entry */
		GOTO(out, rc = -EAGAIN);
	}

	it->it_lock_handle = entry->se_handle;
	rc = md_revalidate_lock(ll_i2mdexp(dir), it, ll_inode2fid(dir), NULL);
	if (rc != 1)
		GOTO(out, rc = -EAGAIN);

	rc = ll_prep_inode(&child, pill, dir->i_sb, it);
	if (rc) {
		CERROR("%s: failed to prep %.*s "DFID" inode@%pK: rc = %d\n",
		       ll_i2sbi(dir)->ll_fsname, entry->se_qstr.len,
		       entry->se_qstr.name, PFID(&entry->se_fid),
		       entry->se_inode, rc);
		if (entry->se_inode) {
			iput(entry->se_inode);
			entry->se_inode = NULL;
		}
		GOTO(out, rc);
	}

	/* If encryption context was returned by MDT, put it in
	 * inode now to save an extra getxattr.
	 */
	if (body->mbo_valid & OBD_MD_ENCCTX) {
		void *encctx = req_capsule_server_get(pill, &RMF_FILE_ENCCTX);
		__u32 encctxlen = req_capsule_get_size(pill, &RMF_FILE_ENCCTX,
						       RCL_SERVER);

		if (encctxlen) {
			CDEBUG(D_SEC,
			       "server returned encryption ctx for "DFID"\n",
			       PFID(ll_inode2fid(child)));
			rc = ll_xattr_cache_insert(child,
						   xattr_for_enc(child),
						   encctx, encctxlen);
			if (rc)
				CWARN("%s: cannot set enc ctx for "DFID": rc = %d\n",
				      ll_i2sbi(child)->ll_fsname,
				      PFID(ll_inode2fid(child)), rc);
		}
	}

	CDEBUG(D_READA, "%s: setting "DNAME""DFID" l_data to inode %p\n",
	       ll_i2sbi(dir)->ll_fsname, encode_fn_qstr(entry->se_qstr),
	       PFID(ll_inode2fid(child)), child);
	ll_set_lock_data(ll_i2sbi(dir)->ll_md_exp, child, it, NULL);

	entry->se_inode = child;

	if (agl_should_run(sai, child))
		ll_agl_add(sai, child, entry->se_index);
out:
	ll_statahead_interpret_fini(lli, sai, item, entry, pill->rc_req, rc);
}

/*
 * Callback for async stat RPC, this is called in ptlrpcd context. It prepares
 * the inode and set lock data directly in the ptlrpcd context. It will wake up
 * the directory listing process if the dentry is the waiting one.
 */
static int ll_statahead_interpret(struct md_op_item *item, int rc)
{
	struct req_capsule *pill = item->mop_pill;
	struct lookup_intent *it = &item->mop_it;
	struct inode *dir = item->mop_dir;
	struct ll_inode_info *lli = ll_i2info(dir);
	struct sa_entry *entry = (struct sa_entry *)item->mop_cbdata;
	struct work_struct *work = &item->mop_work;
	struct ll_statahead_info *sai;
	struct mdt_body *body;
	struct inode *child;

	ENTRY;

	if (it_disposition(it, DISP_LOOKUP_NEG))
		rc = -ENOENT;

	/*
	 * because statahead thread will wait for all inflight RPC to finish,
	 * sai should be always valid, no need to refcount
	 */
	LASSERT(entry != NULL);
	sai = entry->se_sai;
	LASSERT(sai != NULL);

	CDEBUG(D_READA, "sa_entry "DNAME" rc %d\n",
	       encode_fn_qstr(entry->se_qstr), rc);

	if (rc != 0)
		GOTO(out, rc);

	body = req_capsule_server_get(pill, &RMF_MDT_BODY);
	if (!body)
		GOTO(out, rc = -EFAULT);

	child = entry->se_inode;
	/*
	 * revalidate; unlinked and re-created with the same name.
	 * exclude the case where FID is zero as it was from statahead with
	 * regularized file name pattern and had no idea for the FID of the
	 * children file.
	 */
	if (unlikely(!fid_is_zero(&item->mop_data.op_fid2) &&
		     !lu_fid_eq(&item->mop_data.op_fid2, &body->mbo_fid1))) {
		if (child) {
			entry->se_inode = NULL;
			iput(child);
		}
		/* The mdt_body is invalid. Skip this entry */
		GOTO(out, rc = -EAGAIN);
	}

	entry->se_handle = it->it_lock_handle;
	/*
	 * In ptlrpcd context, it is not allowed to generate new RPCs
	 * especially for striped directories or regular files with layout
	 * change.
	 */
	/*
	 * release ibits lock ASAP to avoid deadlock when statahead
	 * thread enqueues lock on parent in readdir and another
	 * process enqueues lock on child with parent lock held, eg.
	 * unlink.
	 */
	ll_intent_drop_lock(it);
	ll_unlock_md_op_lsm(&item->mop_data);

	/*
	 * If the statahead entry is a striped directory or regular file with
	 * layout change, it will generate a new RPC and long wait in the
	 * ptlrpcd context.
	 * However, it is dangerous of blocking in ptlrpcd thread.
	 * Here we use work queue or the separate statahead thread to handle
	 * the extra RPC and long wait:
	 *	(@ll_prep_inode->@lmv_revalidate_slaves);
	 *	(@ll_prep_inode->@lov_layout_change->osc_cache_wait_range);
	 */
	INIT_WORK(work, ll_statahead_interpret_work);
	ptlrpc_request_addref(pill->rc_req);
	schedule_work(work);
	RETURN(0);
out:
	ll_statahead_interpret_fini(lli, sai, item, entry, NULL, rc);
	RETURN(rc);
}

static inline int sa_getattr(struct ll_statahead_info *sai, struct inode *dir,
			     struct md_op_item *item)
{
	int rc;

	if (sa_has_batch_handle(sai))
		rc = md_batch_add(ll_i2mdexp(dir), sai->sai_bh, item);
	else
		rc = md_intent_getattr_async(ll_i2mdexp(dir), item);

	return rc;
}

/* async stat for file not found in dcache */
static int sa_lookup(struct inode *dir, struct sa_entry *entry)
{
	struct md_op_item *item;
	int rc;

	ENTRY;

	item = sa_prep_data(dir, NULL, entry);
	if (IS_ERR(item))
		RETURN(PTR_ERR(item));

	rc = sa_getattr(entry->se_sai, dir, item);
	if (rc < 0)
		sa_fini_data(item);

	RETURN(rc);
}

/**
 * sa_revalidate() - async stat for file found in dcache (directory cache),
 * similar to .revalidate
 *
 * @dir: inode struct for the @dir that contains file to be revalidated
 * @entry: sa_entry structure (SA file state)
 * @dentry: dentry struct for the @dentry that contains file to be revalidated
 *
 * Returns:
 * * %1  dentry valid, no RPC sent
 * * %0  dentry invalid, will send async stat RPC
 * * %-ERRNO negative number upon error
 */
static int sa_revalidate(struct inode *dir, struct sa_entry *entry,
			 struct dentry *dentry)
{
	struct inode *inode = dentry->d_inode;
	struct lookup_intent it = { .it_op = IT_GETATTR,
				    .it_lock_handle = 0 };
	struct md_op_item *item;
	int rc;

	ENTRY;

	if (unlikely(!inode))
		RETURN(1);

	if (d_mountpoint(dentry))
		RETURN(1);

	item = sa_prep_data(dir, inode, entry);
	if (IS_ERR(item))
		RETURN(PTR_ERR(item));

	entry->se_inode = igrab(inode);
	rc = md_revalidate_lock(ll_i2mdexp(dir), &it, ll_inode2fid(inode),
				NULL);
	if (rc == 1) {
		entry->se_handle = it.it_lock_handle;
		ll_intent_release(&it);
		sa_fini_data(item);
		RETURN(1);
	}

	rc = sa_getattr(entry->se_sai, dir, item);
	if (rc < 0) {
		entry->se_inode = NULL;
		iput(inode);
		sa_fini_data(item);
	}

	RETURN(rc);
}

/* async stat for file with @name */
static void sa_statahead(struct ll_statahead_info *sai, struct dentry *parent,
			 const char *name, int len, const struct lu_fid *fid)
{
	struct inode *dir = parent->d_inode;
	struct dentry *dentry = NULL;
	struct sa_entry *entry;
	int rc;

	ENTRY;

	entry = sa_alloc(parent, sai, sai->sai_index, name, len, fid);
	if (IS_ERR(entry))
		RETURN_EXIT;

	dentry = d_lookup(parent, &entry->se_qstr);
	if (!dentry) {
		rc = sa_lookup(dir, entry);
	} else {
		rc = sa_revalidate(dir, entry, dentry);
		if (rc == 1 && agl_should_run(sai, dentry->d_inode))
			ll_agl_add(sai, dentry->d_inode, entry->se_index);
	}

	if (dentry)
		dput(dentry);

	if (rc != 0)
		sa_make_ready(sai, entry, rc);
	else
		sai->sai_sent++;

	sai->sai_index++;

	if (sa_sent_full(sai))
		ll_statahead_flush_nowait(sai);

	EXIT;
}

/* async glimpse (agl) thread main function */
static int ll_agl_thread(void *arg)
{
	/*
	 * We already own this reference, so it is safe to take it
	 * without a lock.
	 */
	struct ll_statahead_info *sai = (struct ll_statahead_info *)arg;
	struct dentry *parent = sai->sai_dentry;
	struct inode *dir = parent->d_inode;
	struct ll_inode_info *plli = ll_i2info(dir);
	struct ll_inode_info *clli;

	ENTRY;

	CDEBUG(D_READA, "agl thread started: sai %p, parent %pd\n",
	       sai, parent);

	while (({set_current_state(TASK_IDLE);
		 !kthread_should_stop(); })) {
		spin_lock(&plli->lli_agl_lock);
		clli = list_first_entry_or_null(&sai->sai_agls,
						struct ll_inode_info,
						lli_agl_list);
		if (clli) {
			__set_current_state(TASK_RUNNING);
			list_del_init(&clli->lli_agl_list);
			spin_unlock(&plli->lli_agl_lock);
			ll_agl_trigger(&clli->lli_vfs_inode, sai);
			cond_resched();
		} else {
			spin_unlock(&plli->lli_agl_lock);
			schedule();
		}
	}
	__set_current_state(TASK_RUNNING);
	RETURN(0);
}

static void ll_stop_agl(struct ll_statahead_info *sai)
{
	struct dentry *parent = sai->sai_dentry;
	struct ll_inode_info *plli = ll_i2info(parent->d_inode);
	struct ll_inode_info *clli;
	struct task_struct *agl_task;

	spin_lock(&plli->lli_agl_lock);
	agl_task = sai->sai_agl_task;
	sai->sai_agl_task = NULL;
	spin_unlock(&plli->lli_agl_lock);
	if (!agl_task)
		return;

	CDEBUG(D_READA, "stop agl thread: sai %p pid %u\n",
	       sai, (unsigned int)agl_task->pid);
	kthread_stop(agl_task);

	spin_lock(&plli->lli_agl_lock);
	while ((clli = list_first_entry_or_null(&sai->sai_agls,
						struct ll_inode_info,
						lli_agl_list)) != NULL) {
		list_del_init(&clli->lli_agl_list);
		spin_unlock(&plli->lli_agl_lock);
		clli->lli_agl_index = 0;
		iput(&clli->lli_vfs_inode);
		spin_lock(&plli->lli_agl_lock);
	}
	spin_unlock(&plli->lli_agl_lock);
	CDEBUG(D_READA, "agl thread stopped: sai %p, parent %pd\n",
	       sai, parent);
	ll_sai_put(sai);
}

/* start agl thread */
static void ll_start_agl(struct dentry *parent, struct ll_statahead_info *sai)
{
	int node = cfs_cpt_spread_node(cfs_cpt_tab, CFS_CPT_ANY);
	struct task_struct *task;

	ENTRY;

	CDEBUG(D_READA, "start agl thread: sai %p, parent %pd\n",
	       sai, parent);

	task = kthread_create_on_node(ll_agl_thread, sai, node, "ll_agl_%d",
				      sai->sai_pid);
	if (IS_ERR(task)) {
		CERROR("%s: cannot start ll_agl_%u thread for '%pd': rc=%ld\n",
		       ll_i2sbi(d_inode(parent))->ll_fsname, sai->sai_pid,
		       sai->sai_dentry, PTR_ERR(task));
		RETURN_EXIT;
	}
	sai->sai_agl_task = task;
	atomic_inc(&ll_i2sbi(d_inode(parent))->ll_agl_total);
	/* Get an extra reference that the thread holds */
	__ll_sai_get(sai);

	wake_up_process(task);

	EXIT;
}

static int ll_statahead_by_list(struct ll_statahead_info *sai,
				struct dentry *parent)
{
	struct inode *dir = parent->d_inode;
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_sb_info *sbi = ll_i2sbi(dir);
	struct md_op_data *op_data;
	struct folio *folio = NULL;
	bool is_hash64 = test_bit(LL_SBI_64BIT_HASH, sbi->ll_flags);
	__u64 pos = 0;
	int first = 0;
	int rc = 0;

	ENTRY;

	LASSERTF(sai != NULL, "Pattern %#X\n", lli->lli_sa_pattern);
	CDEBUG(D_READA, "statahead thread starting: sai %p, parent %pd\n",
	       sai, parent);

	OBD_ALLOC_PTR(op_data);
	if (!op_data)
		RETURN(-ENOMEM);

	while (pos != MDS_DIR_END_OFF &&
	       /* matches smp_store_release() in ll_deauthorize_statahead() */
	       smp_load_acquire(&sai->sai_task) &&
	       lli->lli_sa_enabled) {
		struct lu_dirpage *dp;
		struct lu_dirent *ent;
		struct md_op_data *ret;
		void *kaddr = NULL;
		u32 flags;

		ret = ll_prep_md_op_data(op_data, dir, dir, NULL, 0, 0,
					 LUSTRE_OPC_ANY, dir);
		if (IS_ERR(ret)) {
			rc = PTR_ERR(ret);
			break;
		}

		folio = ll_get_dir_folio(dir, op_data, pos, is_hash64, NULL);
		ll_unlock_md_op_lsm(op_data);
		if (IS_ERR(folio)) {
			rc = PTR_ERR(folio);
			CDEBUG(D_READA,
			       "error reading dir "DFID" at %llu /%llu stat_pid = %u: rc = %d\n",
			       PFID(ll_inode2fid(dir)), pos, sai->sai_index,
			       lli->lli_stat_pid, rc);
			break;
		}
		kaddr = ll_kmap_local_folio(folio, 0);
		dp = kaddr;
		for (ent = lu_dirent_start(dp);
		     /* matches smp_store_release() in ll_deauthorize_statahead() */
		     ent != NULL && smp_load_acquire(&sai->sai_task) &&
		     !sa_low_hit(sai) && lli->lli_sa_enabled;
		     ent = lu_dirent_next(ent)) {
			__u64 hash;
			int namelen;
			char *name;
			struct lu_fid fid;
			struct llcrypt_str lltr = LLTR_INIT(NULL, 0);

			hash = le64_to_cpu(ent->lde_hash);
			if (unlikely(hash < pos))
				/*
				 * Skip until we find target hash value.
				 */
				continue;

			namelen = le16_to_cpu(ent->lde_namelen);
			if (unlikely(namelen == 0))
				/*
				 * Skip dummy record.
				 */
				continue;

			name = ent->lde_name;
			if (name[0] == '.') {
				if (namelen == 1) {
					/*
					 * skip "."
					 */
					continue;
				} else if (name[1] == '.' && namelen == 2) {
					/*
					 * skip ".."
					 */
					continue;
				} else if (!sai->sai_ls_all) {
					/*
					 * skip hidden files.
					 */
					sai->sai_skip_hidden++;
					continue;
				}
			}

			/*
			 * don't stat-ahead first entry.
			 */
			if (unlikely(++first == 1))
				continue;

			fid_le_to_cpu(&fid, &ent->lde_fid);

			while (({set_current_state(TASK_IDLE);
				 /* matches smp_store_release() in
				  * ll_deauthorize_statahead()
				  */
				 smp_load_acquire(&sai->sai_task); })) {
				long timeout;

				spin_lock(&lli->lli_agl_lock);
				while (sa_sent_full(sai) &&
				       !agl_list_empty(sai)) {
					struct ll_inode_info *clli;

					__set_current_state(TASK_RUNNING);
					clli = agl_first_entry(sai);
					list_del_init(&clli->lli_agl_list);
					spin_unlock(&lli->lli_agl_lock);

					ll_agl_trigger(&clli->lli_vfs_inode,
						       sai);
					cond_resched();
					spin_lock(&lli->lli_agl_lock);
				}
				spin_unlock(&lli->lli_agl_lock);

				if (!sa_sent_full(sai))
					break;

				/*
				 * If the thread is not doing stat in
				 * @sbi->ll_sa_timeout (30s) then it probably
				 * does not care too much about performance,
				 * or is no longer using this directory.
				 * Stop the statahead thread in this case.
				 */
				timeout = schedule_timeout(
					cfs_time_seconds(sbi->ll_sa_timeout));
				if (timeout == 0) {
					lli->lli_sa_enabled = 0;
					break;
				}
			}
			__set_current_state(TASK_RUNNING);

			if (IS_ENCRYPTED(dir)) {
				struct llcrypt_str de_name =
					LLTR_INIT(ent->lde_name, namelen);
				struct lu_fid fid;

				rc = llcrypt_fname_alloc_buffer(dir, NAME_MAX,
								&lltr);
				if (rc < 0)
					continue;

				fid_le_to_cpu(&fid, &ent->lde_fid);
				if (ll_fname_disk_to_usr(dir, 0, 0, &de_name,
							 &lltr, &fid)) {
					llcrypt_fname_free_buffer(&lltr);
					continue;
				}

				name = lltr.name;
				namelen = lltr.len;
			}

			sa_statahead(sai, parent, name, namelen, &fid);
			llcrypt_fname_free_buffer(&lltr);
		}

		pos = le64_to_cpu(dp->ldp_hash_end);
		flags = le32_to_cpu(dp->ldp_flags);
		if (kaddr) {
			ll_kunmap_local(kaddr);
			kaddr = NULL;
		}
		ll_release_dir_folio(dir, folio, flags & LDF_COLLIDE);

		if (sa_low_hit(sai)) {
			rc = -EFAULT;
			atomic_inc(&sbi->ll_sa_wrong);
			CDEBUG(D_READA,
			       "Statahead for dir "DFID" hit ratio too low: hit/miss %llu/%llu, sent/replied %llu/%llu, stoppingstatahead thread: pid %d\n",
			       PFID(&lli->lli_fid), sai->sai_hit,
			       sai->sai_miss, sai->sai_sent,
			       sai->sai_replied, current->pid);
			break;
		}
	}
	ll_finish_md_op_data(op_data);

	RETURN(rc);
}

static void ll_statahead_handle(struct ll_statahead_info *sai,
				struct dentry *parent, const char *name,
				int len, const struct lu_fid *fid)
{
	struct inode *dir = parent->d_inode;
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_sb_info *sbi = ll_i2sbi(dir);
	long timeout;

	while (({set_current_state(TASK_IDLE);
		/* matches smp_store_release() in ll_deauthorize_statahead() */
		 smp_load_acquire(&sai->sai_task); })) {
		spin_lock(&lli->lli_agl_lock);
		while (sa_sent_full(sai) && !agl_list_empty(sai)) {
			struct ll_inode_info *clli;

			__set_current_state(TASK_RUNNING);
			clli = agl_first_entry(sai);
			list_del_init(&clli->lli_agl_list);
			spin_unlock(&lli->lli_agl_lock);

			ll_agl_trigger(&clli->lli_vfs_inode, sai);
			cond_resched();
			spin_lock(&lli->lli_agl_lock);
		}
		spin_unlock(&lli->lli_agl_lock);

		if (!sa_sent_full(sai))
			break;

		/*
		 * If the thread is not doing a stat in 30s then it probably
		 * does not care too much about performance, or is no longer
		 * using this directory. Stop the statahead thread in this case.
		 */
		timeout = schedule_timeout(
				cfs_time_seconds(sbi->ll_sa_timeout));
		if (timeout == 0) {
			lli->lli_sa_enabled = 0;
			break;
		}
	}
	__set_current_state(TASK_RUNNING);

	sa_statahead(sai, parent, name, len, fid);
}

static int ll_statahead_by_advise(struct ll_statahead_info *sai,
				  struct dentry *parent)
{
	struct inode *dir = parent->d_inode;
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_sb_info *sbi = ll_i2sbi(dir);
	size_t max_len;
	size_t len;
	char *fname;
	char *ptr;
	int rc = 0;
	__u64 i = 0;

	ENTRY;

	CDEBUG(D_READA, "%s: ADVISE statahead: parent %pd fname prefix %s\n",
	       sbi->ll_fsname, parent, sai->sai_fname);

	OBD_ALLOC(fname, NAME_MAX);
	if (fname == NULL)
		RETURN(-ENOMEM);

	len = strlen(sai->sai_fname);
	memcpy(fname, sai->sai_fname, len);
	max_len = sizeof(sai->sai_fname) - len;
	ptr = fname + len;

	/* matches smp_store_release() in ll_deauthorize_statahead() */
	while (smp_load_acquire(&sai->sai_task) && lli->lli_sa_enabled) {
		size_t numlen;

		numlen = snprintf(ptr, max_len, "%llu",
				  sai->sai_fstart + i);

		ll_statahead_handle(sai, parent, fname, len + numlen, NULL);
		if (++i >= sai->sai_fend)
			break;
	}

	OBD_FREE(fname, NAME_MAX);
	RETURN(rc);
}

static int ll_statahead_by_fname(struct ll_statahead_info *sai,
				 struct dentry *parent)
{
	struct inode *dir = parent->d_inode;
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_sb_info *sbi = ll_i2sbi(dir);
	size_t max_len;
	size_t len;
	char *fname;
	char *ptr;
	int rc = 0;

	ENTRY;

	CDEBUG(D_READA, "%s: FNAME statahead: parent %pd fname prefix %s\n",
	       sbi->ll_fsname, parent, sai->sai_fname);

	OBD_ALLOC(fname, NAME_MAX);
	if (fname == NULL)
		RETURN(-ENOMEM);

	len = strlen(sai->sai_fname);
	memcpy(fname, sai->sai_fname, len);
	max_len = sizeof(sai->sai_fname) - len;
	ptr = fname + len;

	/* matches smp_store_release() in ll_deauthorize_statahead() */
	while (smp_load_acquire(&sai->sai_task) && lli->lli_sa_enabled) {
		size_t numlen;

		if (sai->sai_fname_zeroed_len)
			numlen = snprintf(ptr, max_len, "%0*llu",
					  sai->sai_fname_zeroed_len,
					  ++sai->sai_fname_index);
		else
			numlen = snprintf(ptr, max_len, "%llu",
					  ++sai->sai_fname_index);

		ll_statahead_handle(sai, parent, fname, len + numlen, NULL);

		if (sa_low_hit(sai)) {
			rc = -EFAULT;
			atomic_inc(&sbi->ll_sa_wrong);
			CDEBUG(D_CACHE, "%s: low hit ratio for %pd "DFID": hit=%llu miss=%llu sent=%llu replied=%llu, stopping PID %d\n",
			       sbi->ll_fsname, parent, PFID(ll_inode2fid(dir)),
			       sai->sai_hit, sai->sai_miss, sai->sai_sent,
			       sai->sai_replied, current->pid);
			break;
		}
	}

	OBD_FREE(fname, NAME_MAX);
	RETURN(rc);
}

/* statahead thread main function */
static int ll_statahead_thread(void *arg)
{
	struct ll_statahead_info *sai = (struct ll_statahead_info *)arg;
	struct dentry *parent = sai->sai_dentry;
	struct inode *dir = parent->d_inode;
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_sb_info *sbi = ll_i2sbi(dir);
	struct ll_statahead_context *ctx;
	enum ll_sa_pattern pattern;
	struct lu_batch *bh = NULL;
	struct sa_entry *entry;
	int tries = 0;
	int rc = 0;

	ENTRY;

	CDEBUG(D_READA, "statahead thread starting: sai %p, parent %pd\n",
	       sai, parent);

	if (exp_connect_batch_rpc(sbi->ll_md_exp))
		sai->sai_max_batch_count = sbi->ll_sa_batch_max;
	else
		sai->sai_max_batch_count = 0;

	if (sai->sai_max_batch_count) {
		bh = md_batch_create(ll_i2mdexp(dir), BATCH_FL_RDONLY,
				     sai->sai_max_batch_count);
		if (IS_ERR(bh))
			GOTO(out_stop_agl, rc = PTR_ERR(bh));
	}

	sai->sai_bh = bh;
	pattern = lli->lli_sa_pattern & LSA_PATTERN_MASK;
	switch (pattern) {
	case LSA_PATTERN_LIST:
		rc = ll_statahead_by_list(sai, parent);
		break;
	case LSA_PATTERN_ADVISE:
		rc = ll_statahead_by_advise(sai, parent);
		break;
	case LSA_PATTERN_FNAME:
		rc = ll_statahead_by_fname(sai, parent);
		break;
	default:
		rc = -EFAULT;
		break;
	}

	if (rc < 0) {
		spin_lock(&lli->lli_sa_lock);
		sai->sai_task = NULL;
		spin_unlock(&lli->lli_sa_lock);
	}

	ll_statahead_flush_nowait(sai);

	/*
	 * statahead is finished, but statahead entries need to be cached, wait
	 * for file release closedir() call to stop me.
	 */
	while (({set_current_state(TASK_IDLE);
		/* matches smp_store_release() in ll_deauthorize_statahead() */
		smp_load_acquire(&sai->sai_task) && lli->lli_sa_enabled; })) {
		long timeout;

		timeout = schedule_timeout(
				cfs_time_seconds(sbi->ll_sa_timeout));
		if (timeout == 0 && pattern == LSA_PATTERN_LIST &&
		    lli->lli_sai == NULL)
			CERROR("%s: ll_sa_%u LIST => FNAME no wakeup.\n",
			       sbi->ll_fsname, sai->sai_pid);
	}
	__set_current_state(TASK_RUNNING);

	EXIT;

	if (bh) {
		rc = md_batch_stop(ll_i2mdexp(dir), sai->sai_bh);
		sai->sai_bh = NULL;
	}

out_stop_agl:
	ll_stop_agl(sai);

	/*
	 * wait for inflight statahead RPCs to finish, and then we can free sai
	 * safely because statahead RPC will access sai data
	 */
	while (sai->sai_sent != sai->sai_replied)
		/* in case we're not woken up, timeout wait */
		msleep(125);

	CDEBUG(D_READA, "%s: statahead thread stopped: sai %p, parent %pd hit %llu miss %llu\n",
	       sbi->ll_fsname, sai, parent, sai->sai_hit, sai->sai_miss);

	spin_lock(&lli->lli_sa_lock);
	sai->sai_task = NULL;
	spin_unlock(&lli->lli_sa_lock);
	wake_up(&sai->sai_waitq);

	atomic_add(sai->sai_hit, &sbi->ll_sa_hit_total);
	atomic_add(sai->sai_miss, &sbi->ll_sa_miss_total);

	/* Kill all local cached entry. */
	spin_lock(&lli->lli_sa_lock);
	while ((entry = list_first_entry_or_null(&sai->sai_entries,
						 struct sa_entry, se_list))) {
		/*
		 * If the entry is being used by the user process, wait for
		 * inuse entry finished and restart to kill local cached
		 * entries.
		 */
		if (sa_kill_try(sai, entry, true)) {
			spin_unlock(&lli->lli_sa_lock);
			msleep(125);
			if (++tries % 1024 == 0) {
				CWARN("%s: statahead thread waited %lums for inuse entry "DFID" to be finished\n",
				      sbi->ll_fsname, tries * 125/MSEC_PER_SEC,
				      PFID(&entry->se_fid));
			}
			spin_lock(&lli->lli_sa_lock);
		}
	}
	spin_unlock(&lli->lli_sa_lock);

	/*
	 * ll_sai_put() may free sai (via dput of sai_dentry) and lead to
	 * lli being freed as well and so we need to save the context
	 * before that.
	 */
	ctx = lli->lli_sax;
	ll_sai_put(sai);
	if (ctx) // We lost the race to deauthorize statahead?
		ll_sax_put(ctx);

	return rc;
}

/* authorize opened dir handle @key to statahead */
void ll_authorize_statahead(struct inode *dir, void *key)
{
	struct ll_inode_info *lli = ll_i2info(dir);

	spin_lock(&lli->lli_sa_lock);
	if (!lli->lli_opendir_key && !lli->lli_sai) {
		/*
		 * if lli_sai is not NULL, it means previous statahead is not
		 * finished yet, we'd better not start a new statahead for now.
		 */
		lli->lli_opendir_key = key;
		lli->lli_stat_pid = current->pid;
		lli->lli_sa_enabled = 1;
		lli->lli_sa_pattern |= LSA_PATTERN_OPENDIR;
	}
	spin_unlock(&lli->lli_sa_lock);
}

static void ll_deauthorize_statahead_advise(struct inode *dir, void *key)
{
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_file_data *lfd = (struct ll_file_data *)key;
	struct ll_statahead_info *sai = lfd->fd_sai;

	if (sai == NULL)
		return;

	spin_lock(&lli->lli_sa_lock);
	if (sai->sai_task) {
		struct task_struct *task = sai->sai_task;

		/* matches smp_load_acquire() in ll_statahead_thread() */
		smp_store_release(&sai->sai_task, NULL);
		wake_up_process(task);
	}
	lfd->fd_sai = NULL;
	spin_unlock(&lli->lli_sa_lock);
	ll_sai_put(sai);
	LASSERT(lli->lli_sax != NULL);
	ll_sax_put(lli->lli_sax);
}

/*
 * deauthorize opened dir handle @key to statahead, and notify statahead thread
 * to quit if it's running.
 */
void ll_deauthorize_statahead(struct inode *dir, void *key)
{
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_statahead_info *sai;

	CDEBUG(D_READA, "deauthorize statahead for "DFID"\n",
	       PFID(&lli->lli_fid));

	if (lli->lli_sa_pattern & LSA_PATTERN_ADVISE) {
		ll_deauthorize_statahead_advise(dir, key);
		return;
	}

	LASSERT(lli->lli_stat_pid != 0);
	LASSERT(lli->lli_opendir_key == key);
	spin_lock(&lli->lli_sa_lock);
	lli->lli_opendir_key = NULL;
	lli->lli_stat_pid = 0;
	lli->lli_sa_enabled = 0;
	lli->lli_sa_pattern = LSA_PATTERN_NONE;
	lli->lli_sa_fname_index = 0;
	lli->lli_sa_match_count = 0;
	sai = lli->lli_sai;
	if (sai && sai->sai_task) {
		/*
		 * statahead thread may not have quit yet because it needs to
		 * cache entries, now it's time to tell it to quit.
		 *
		 * wake_up_process() provides the necessary barriers
		 * to pair with set_current_state().
		 */
		struct task_struct *task = sai->sai_task;

		/* matches smp_load_acquire() in ll_statahead_thread() */
		smp_store_release(&sai->sai_task, NULL);
		wake_up_process(task);
	}
	spin_unlock(&lli->lli_sa_lock);
}

enum {
	/**
	 * not first dirent, or is "."
	 */
	LS_NOT_FIRST_DE = 0,
	/**
	 * the first non-hidden dirent
	 */
	LS_FIRST_DE,
	/**
	 * the first hidden dirent, that is "."
	 */
	LS_FIRST_DOT_DE
};

/* file is first dirent under @dir */
static int is_first_dirent(struct inode *dir, struct dentry *dentry)
{
	const struct qstr *target = &dentry->d_name;
	struct md_op_data *op_data;
	int dot_de;
	struct folio *folio = NULL;
	int rc = LS_NOT_FIRST_DE;
	__u64 pos = 0;
	struct ll_sb_info *sbi = ll_i2sbi(dir);
	bool is_hash64 = test_bit(LL_SBI_64BIT_HASH, sbi->ll_flags);
	struct llcrypt_str lltr = LLTR_INIT(NULL, 0);

	ENTRY;

	op_data = ll_prep_md_op_data(NULL, dir, dir, NULL, 0, 0,
				     LUSTRE_OPC_ANY, dir);
	if (IS_ERR(op_data))
		RETURN(PTR_ERR(op_data));

	if (IS_ENCRYPTED(dir)) {
		int rc2 = llcrypt_fname_alloc_buffer(dir, NAME_MAX, &lltr);

		if (rc2 < 0)
			RETURN(rc2);
	}

	/**
	 *FIXME choose the start offset of the readdir
	 */

	folio = ll_get_dir_folio(dir, op_data, 0, is_hash64, NULL);

	while (1) {
		struct lu_dirpage *dp;
		struct lu_dirent  *ent;

		if (IS_ERR(folio)) {
			struct ll_inode_info *lli = ll_i2info(dir);

			rc = PTR_ERR(folio);
			CERROR("%s: reading dir "DFID" at %llu stat_pid = %u : rc = %d\n",
			       ll_i2sbi(dir)->ll_fsname,
			       PFID(ll_inode2fid(dir)), pos,
			       lli->lli_stat_pid, rc);
			break;
		}

		dp = kmap_local_folio(folio, 0);
		for (ent = lu_dirent_start(dp); ent != NULL;
		     ent = lu_dirent_next(ent)) {
			__u64 hash;
			int namelen;
			char *name;

			hash = le64_to_cpu(ent->lde_hash);
			/*
			 * The ll_get_dir_folio() can return any page containing
			 * the given hash which may be not the start hash.
			 */
			if (unlikely(hash < pos))
				continue;

			namelen = le16_to_cpu(ent->lde_namelen);
			if (unlikely(namelen == 0))
				/*
				 * skip dummy record.
				 */
				continue;

			name = ent->lde_name;
			if (name[0] == '.') {
				if (namelen == 1)
					/*
					 * skip "."
					 */
					continue;
				else if (name[1] == '.' && namelen == 2)
					/*
					 * skip ".."
					 */
					continue;
				else
					dot_de = 1;
			} else {
				dot_de = 0;
			}

			if (dot_de && target->name[0] != '.') {
				CDEBUG(D_READA, DNAME" skip hidden file "DNAME"\n",
				       encode_fn_dentry(dentry),
				       encode_fn_dname(namelen, name));
				continue;
			}

			if (IS_ENCRYPTED(dir)) {
				struct llcrypt_str de_name =
					LLTR_INIT(ent->lde_name, namelen);
				struct lu_fid fid;

				fid_le_to_cpu(&fid, &ent->lde_fid);
				if (ll_fname_disk_to_usr(dir, 0, 0, &de_name,
							 &lltr, &fid))
					continue;
				name = lltr.name;
				namelen = lltr.len;
			}

			if (target->len != namelen ||
			    memcmp(target->name, name, namelen) != 0)
				rc = LS_NOT_FIRST_DE;
			else if (!dot_de)
				rc = LS_FIRST_DE;
			else
				rc = LS_FIRST_DOT_DE;

			kunmap_local(dp);
			ll_release_dir_folio(dir, folio, false);
			GOTO(out, rc);
		}
		pos = le64_to_cpu(dp->ldp_hash_end);
		if (pos == MDS_DIR_END_OFF) {
			/*
			 * End of directory reached.
			 */
			kunmap_local(dp);
			ll_release_dir_folio(dir, folio, false);
			GOTO(out, rc);
		} else {
			u32 flags = le32_to_cpu(dp->ldp_flags);

			/*
			 * chain is exhausted
			 * Normal case: continue to the next page.
			 */
			kunmap_local(dp);
			ll_release_dir_folio(dir, folio, flags & LDF_COLLIDE);
			folio = ll_get_dir_folio(dir, op_data, pos, is_hash64,
						 NULL);
		}
	}
	EXIT;
out:
	llcrypt_fname_free_buffer(&lltr);
	ll_finish_md_op_data(op_data);

	return rc;
}

static struct ll_statahead_info *
ll_find_sai_locked(struct ll_statahead_context *ctx, pid_t pid)
{
	struct ll_statahead_info *sai;

	list_for_each_entry(sai, &ctx->sax_sai_list, sai_item) {
		if (sai->sai_pid == pid)
			return sai;
	}
	return NULL;
}

static int start_statahead_thread(struct inode *dir, struct dentry *dentry,
				  bool agl);

static int ll_shared_statahead_check(struct inode *dir, struct dentry *dentry,
				     struct ll_statahead_context *ctx)
{
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_statahead_info *sai;
	int rc;

	ENTRY;

	spin_lock(&lli->lli_sa_lock);
	if (!(lli->lli_sa_pattern & LSA_PATTERN_FNAME))
		GOTO(out_unlock, rc = 0);

	sai = lli->lli_sai;
	if (sai) {
		if (sai->sai_pid == current->pid)
			GOTO(out_unlock, rc = 0);

		lli->lli_sai = NULL;
		lli->lli_sa_pattern |= LSA_PATTERN_FN_SHARED;
	}

	sai = ll_find_sai_locked(ctx, current->pid);
	if (sai)
		GOTO(out_unlock, rc = -EEXIST);

	lli->lli_sa_pattern |= LSA_PATTERN_FN_SHARED;
	spin_unlock(&lli->lli_sa_lock);

	RETURN(start_statahead_thread(dir, dentry, true));

out_unlock:
	spin_unlock(&lli->lli_sa_lock);
	RETURN(rc);
}

/**
 * revalidate_statahead_dentry() - revalidate @dentryp from statahead cache
 *
 * @dir: parent directory
 * @sai: sai structure
 * @ctx: statahead context structure (cache info)
 * @dentryp: pointer to dentry which will be revalidated
 * @unplug: unplug statahead window only (normally for negative dentry)
 *
 * Returns:
 * * %1  on success, dentry is saved in @dentryp
 * * %0  if revalidation failed (no proper lock on client)
 * * %-ERRNO upon error
 */
static int revalidate_statahead_dentry(struct inode *dir,
				       struct ll_statahead_info *sai,
				       struct ll_statahead_context *ctx,
				       struct dentry **dentryp,
				       bool unplug)
{
	struct sa_entry *entry = NULL;
	struct ll_dentry_data *lld;
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_statahead_info *info = NULL;
	bool inuse = false;
	int rc = 0;

	ENTRY;

	if (sai && (*dentryp)->d_name.name[0] == '.') {
		if (sai->sai_ls_all ||
		    sai->sai_miss_hidden >= sai->sai_skip_hidden) {
			/*
			 * Hidden dentry is the first one, or statahead
			 * thread does not skip so many hidden dentries
			 * before "sai_ls_all" enabled as below.
			 */
		} else {
			if (!sai->sai_ls_all)
				/*
				 * It maybe because hidden dentry is not
				 * the first one, "sai_ls_all" was not
				 * set, then "ls -al" missed. Enable
				 * "sai_ls_all" for such case.
				 */
				sai->sai_ls_all = 1;

			/*
			 * Such "getattr" has been skipped before
			 * "sai_ls_all" enabled as above.
			 */
			sai->sai_miss_hidden++;
			RETURN(-EAGAIN);
		}
	}

	if (unplug)
		GOTO(out, rc = 1);

	entry = sa_get(ctx, &(*dentryp)->d_name, &info);
	if (entry == ERR_PTR(-EINVAL)) {
		sai = info;
		spin_lock(&lli->lli_sa_lock);
		if (sai->sai_task) {
			struct task_struct *task = sai->sai_task;

			/*
			 * matches smp_load_acquire() in
			 * ll_statahead_thread().
			 * Notify to stop statahead thread immediately.
			 */
			smp_store_release(&sai->sai_task, NULL);
			wake_up_process(task);
		}
		atomic_dec(&sai->sai_inuse_count);
		spin_unlock(&lli->lli_sa_lock);
		RETURN(-EINVAL);
	} else if (entry == NULL) {
		if (lli->lli_sa_pattern & LSA_PATTERN_FNAME)
			rc = ll_shared_statahead_check(dir, *dentryp, ctx);
		GOTO(out, rc = rc == 0 ? -EAGAIN : rc);
	}

	if (lli->lli_sa_pattern & LSA_PATTERN_LIST)
		LASSERT(sai == entry->se_sai);
	else if (lli->lli_sa_pattern & LSA_PATTERN_FNAME ||
		 lli->lli_sa_pattern == LSA_PATTERN_ADVISE)
		sai = entry->se_sai;
	else
		sai = entry->se_sai;

	LASSERTF(sai != NULL, "pattern %#X entry %p se_sai %p %pd lli %p\n",
		 lli->lli_sa_pattern, entry, entry->se_sai, *dentryp, lli);

	inuse = true;
	if (!sa_ready(entry)) {
		spin_lock(&lli->lli_sa_lock);
		sai->sai_index_wait = entry->se_index;
		spin_unlock(&lli->lli_sa_lock);
		rc = wait_event_idle_timeout(sai->sai_waitq, sa_ready(entry),
					     cfs_time_seconds(30));
		if (rc == 0) {
			/*
			 * entry may not be ready, so it may be used by inflight
			 * statahead RPC, don't free it.
			 */
			entry = NULL;
			GOTO(out, rc = -EAGAIN);
		}
	}

	/*
	 * We need to see the value that was set immediately before we
	 * were woken up.
	 */
	if (smp_load_acquire(&entry->se_state) == SA_ENTRY_SUCC &&
	    entry->se_inode) {
		struct inode *inode = entry->se_inode;
		struct lookup_intent it = { .it_op = IT_GETATTR,
					    .it_lock_handle =
						entry->se_handle };
		enum mds_ibits_locks bits;

		rc = md_revalidate_lock(ll_i2mdexp(dir), &it,
					ll_inode2fid(inode), &bits);
		if (rc == 1) {
			if (!(*dentryp)->d_inode) {
				struct dentry *alias;

				alias = ll_splice_alias(inode, *dentryp);
				if (IS_ERR(alias)) {
					ll_intent_release(&it);
					GOTO(out, rc = PTR_ERR(alias));
				}
				*dentryp = alias;
				/*
				 * statahead prepared this inode, transfer inode
				 * refcount from sa_entry to dentry
				 */
				entry->se_inode = NULL;
			} else if ((*dentryp)->d_inode != inode) {
				/* revalidate, but inode is recreated */
				CDEBUG(D_READA,
				       "%s: stale dentry %pd inode " DFID", statahead inode "DFID "\n",
				       ll_i2sbi(inode)->ll_fsname, *dentryp,
				       PFID(ll_inode2fid((*dentryp)->d_inode)),
				       PFID(ll_inode2fid(inode)));
				ll_intent_release(&it);
				GOTO(out, rc = -ESTALE);
			}

			if (bits & MDS_INODELOCK_LOOKUP) {
				d_lustre_revalidate(*dentryp);
				if (S_ISDIR(inode->i_mode))
					ll_update_dir_depth_dmv(dir, *dentryp);
			}

			ll_intent_release(&it);
		}
	}
out:
	/*
	 * statahead cached sa_entry can be used only once, and will be killed
	 * right after use, so if lookup/revalidate accessed statahead cache,
	 * set dentry ldd_sa_generation to parent lli_sa_generation, later if we
	 * stat this file again, we know we've done statahead before, see
	 * dentry_may_statahead().
	 */
	rcu_read_lock();
	lld = ll_d2d(*dentryp);
	if (lld)
		lld->lld_sa_generation = lli->lli_sa_generation;
	rcu_read_unlock();
	sa_put(dir, sai, entry, inuse);

	RETURN(rc);
}

static inline bool
sa_pattern_list_detect(struct inode *dir, struct dentry *dchild, int *first)
{
	struct ll_inode_info *lli = ll_i2info(dir);

	if (lli->lli_stat_pid == 0)
		return false;

	/* Directory listing needs to call opendir()/readdir()/stat(). */
	if (!(lli->lli_sa_pattern & LSA_PATTERN_OPENDIR))
		return false;

	if (lli->lli_sa_enabled == 0)
		return false;

	if (lli->lli_sa_pattern & LSA_PATTERN_LS_NOT_FIRST_DE)
		return false;

	*first = is_first_dirent(dir, dchild);
	if (*first == LS_NOT_FIRST_DE) {
		/*
		 * It is not "ls -{a}l" operation, no need statahead for it.
		 * Disable statahead so that subsequent stat() won't waste
		 * time to try it.
		 */
		spin_lock(&lli->lli_sa_lock);
		if (lli->lli_stat_pid == current->pid) {
			lli->lli_sa_enabled = 0;
			lli->lli_sa_pattern |= LSA_PATTERN_LS_NOT_FIRST_DE;
		}
		spin_unlock(&lli->lli_sa_lock);
		return false;
	}

	spin_lock(&lli->lli_sa_lock);
	lli->lli_sa_pattern |= LSA_PATTERN_LIST;
	spin_unlock(&lli->lli_sa_lock);
	return true;
}

static inline bool
sa_pattern_fname_detect(struct inode *dir, struct dentry *dchild)
{
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_sb_info *sbi = ll_i2sbi(dir);
	const struct qstr *dname = &dchild->d_name;
	const unsigned char *name = dname->name;
	bool rc = false;
	int i;

	if (sbi->ll_enable_statahead_fname == 0)
		return false;
	if (lli->lli_sa_pattern & LSA_PATTERN_FN_SHARED)
		return true;

	/*
	 * Parse the format of the file name to determine whether it matches
	 * the supported file name pattern for statahead (i.e. mdtest.$rank.$i).
	 */
	i = dname->len - 1;
	if (isdigit(name[i])) {
		long num;
		int ret;

		if (lli->lli_stat_pid == 0) {
			lli->lli_stat_pid = current->pid;
		} else if (lli->lli_stat_pid != current->pid) {
			/*
			 * More than two processes (MPI ranks) doing stat()
			 * calls under this directory, consider it as a mdtest
			 * shared dir stat() workload.
			 */
			spin_lock(&lli->lli_sa_lock);
			lli->lli_stat_pid = current->pid;
			if (lli->lli_sa_pattern & LSA_PATTERN_FNAME) {
				lli->lli_sai = NULL;
				rc = false;
			} else {
				lli->lli_sa_pattern |= LSA_PATTERN_FNAME;
				rc = true;
			}
			lli->lli_sa_pattern |= LSA_PATTERN_FN_SHARED;
			spin_unlock(&lli->lli_sa_lock);
			return rc;
		}

		while (--i >= 0 && isdigit(name[i]))
			; /* do nothing */
		i++;
		ret = kstrtol(&name[i], 0, &num);
		if (ret)
			GOTO(out, rc);

		/*
		 * The traversing program do multiple stat() calls on the same
		 * children entry. i.e. ls $dir*.
		 */
		if (lli->lli_sa_fname_index == num)
			return false;

		if (lli->lli_sa_match_count == 0 ||
		    num == lli->lli_sa_fname_index + 1) {
			lli->lli_sa_match_count++;
			lli->lli_sa_fname_index = num;

			if (lli->lli_sa_match_count >
			    sbi->ll_sa_fname_match_hit)
				GOTO(out, rc = true);

			return false;
		}
	}
out:
	spin_lock(&lli->lli_sa_lock);
	if (rc) {
		lli->lli_sa_pattern |= LSA_PATTERN_FNAME;
	} else {
		lli->lli_sa_pattern = LSA_PATTERN_NONE;
		lli->lli_sa_match_count = 0;
		lli->lli_sa_fname_index = 0;
		lli->lli_sa_enabled = 0;
	}
	spin_unlock(&lli->lli_sa_lock);

	return rc;
}

/* detect the statahead pattern. */
static inline bool
sa_pattern_detect(struct inode *dir, struct dentry *dchild, int *first)
{
	return sa_pattern_list_detect(dir, dchild, first) ||
	       sa_pattern_fname_detect(dir, dchild);
}

static inline int ll_sax_add_sai(struct ll_statahead_context *ctx,
				 struct ll_statahead_info *sai)
{
	if (ll_find_sai_locked(ctx, sai->sai_pid) != NULL)
		return -EEXIST;

	list_add_tail(&sai->sai_item, &ctx->sax_sai_list);
	return 0;
}

/* Check whether it is shared FNAME statahead pattern. */
static inline bool sa_pattern_shared_fname(struct ll_inode_info *lli)
{
	return (lli->lli_sa_pattern & LSA_PATTERN_SFNAME) == LSA_PATTERN_SFNAME;
}

/**
 * start_statahead_thread() - start statahead thread
 *
 * @dir: parent directory
 * @dentry: dentry that triggers statahead, normally the first dirent under @dir
 * @agl: indicate whether AGL(Asynchronous Glimpse Lock) is needed
 *
 * Returns:
 * * %-EAGAIN success, because when this function is called, it's already
 * in lookup call, so client should do it itself instead of waiting for
 * statahead thread to do it asynchronously.
 * * %-ERRNO  (other than -EAGAIN) on error
 */
static int start_statahead_thread(struct inode *dir, struct dentry *dentry,
				  bool agl)
{
	int node = cfs_cpt_spread_node(cfs_cpt_tab, CFS_CPT_ANY);
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_statahead_info *sai = NULL;
	struct ll_statahead_context *ctx = NULL;
	struct dentry *parent;
	struct task_struct *task;
	struct ll_sb_info *sbi;
	int first = LS_FIRST_DE;
	bool fail_free = true;
	int rc = 0;

	ENTRY;

	if (sa_pattern_detect(dir, dentry, &first) == false)
		RETURN(0);

	parent = dget_parent(dentry);
	sbi = ll_i2sbi(d_inode(parent));
	if (unlikely(atomic_inc_return(&sbi->ll_sa_running) >
				       sbi->ll_sa_running_max)) {
		CDEBUG(D_READA,
		       "Too many concurrent statahead instances, avoid new statahead instance temporarily.\n");
		dput(parent);
		GOTO(out, rc = -EMFILE);
	}

	/* on success ll_sai_alloc holds a ref on parent */
	sai = ll_sai_alloc(parent);
	dput(parent);
	if (!sai)
		GOTO(out, rc = -ENOMEM);

	sai->sai_ls_all = (first == LS_FIRST_DOT_DE);
	sai->sai_pid = current->pid;

	if (lli->lli_sa_pattern & LSA_PATTERN_FNAME) {
		const struct qstr *dname = &dentry->d_name;
		const unsigned char *name = dname->name;
		long num;
		int i;

		if (dname->len >= sizeof(sai->sai_fname))
			GOTO(out, rc = -ERANGE);

		i = dname->len;
		while (--i >= 0 && isdigit(name[i]))
			; /* do nothing */
		i++;
		rc = kstrtol(&name[i], 0, &num);
		if (rc)
			GOTO(out, rc);

		memcpy(sai->sai_fname, dname->name, i);
		sai->sai_fname[i] = '\0';
		sai->sai_fname_index = num;
		/* The front part of the file name is zeroed padding. */
		if (name[i] == '0')
			sai->sai_fname_zeroed_len = dname->len - i;
	}

	/* The workload like directory listing or mdtest unique dir stat() */
	if (lli->lli_sa_pattern & LSA_PATTERN_LIST ||
	    (lli->lli_sa_pattern & LSA_PATTERN_SFNAME) == LSA_PATTERN_FNAME) {
		ctx = ll_sax_alloc(dir);
		if (!ctx)
			GOTO(out, rc = -ENOMEM);

		/*
		 * if current lli_opendir_key was deauthorized, or dir
		 * re-opened by another process, don't start statahead,
		 * otherwise the newly spawned statahead thread won't be
		 * notified to quit.
		 */
		spin_lock(&lli->lli_sa_lock);
		if (unlikely(lli->lli_sai || lli->lli_sax ||
			     ((lli->lli_sa_pattern & LSA_PATTERN_LIST) &&
			      (!lli->lli_opendir_key ||
			       lli->lli_stat_pid != current->pid)))) {
			spin_unlock(&lli->lli_sa_lock);
			GOTO(out, rc = -EPERM);
		}
		rc = ll_sax_add_sai(ctx, sai);
		if (rc) {
			spin_unlock(&lli->lli_sa_lock);
			GOTO(out, rc);
		}
		lli->lli_sai = sai;
		lli->lli_sax = ctx;
		spin_unlock(&lli->lli_sa_lock);
	} else if (sa_pattern_shared_fname(lli)) {
		/* For mdtest shared dir stat() workload */
		ctx = ll_sax_get(dir);
		if (ctx == NULL) {
			ctx = ll_sax_alloc(dir);
			if (ctx == NULL)
				GOTO(out, rc = -ENOMEM);

			spin_lock(&lli->lli_sa_lock);
			if (!sa_pattern_shared_fname(lli)) {
				spin_unlock(&lli->lli_sa_lock);
				CERROR("%s: invalid pattern %#X.\n",
				       sbi->ll_fsname, lli->lli_sa_pattern);
				GOTO(out, rc = -EINVAL);
			}

			if (lli->lli_sax) {
				struct ll_statahead_context *tmp = ctx;

				ctx = lli->lli_sax;
				__ll_sax_get(ctx);
				rc = ll_sax_add_sai(ctx, sai);

				spin_unlock(&lli->lli_sa_lock);
				ll_sax_free(tmp);
				if (rc) {
					CERROR("%s: failed to add sai: rc=%d\n",
					       sbi->ll_fsname, rc);
					ll_sax_put(ctx);
					ctx = NULL;
					GOTO(out, rc);
				}
			} else {
				lli->lli_sax = ctx;
				rc = ll_sax_add_sai(ctx, sai);
				if (rc)
					lli->lli_sax = NULL;
				spin_unlock(&lli->lli_sa_lock);
			}
		} else {
			spin_lock(&lli->lli_sa_lock);
			if (sa_pattern_shared_fname(lli))
				rc = ll_sax_add_sai(ctx, sai);
			else
				rc = -EINVAL;
			spin_unlock(&lli->lli_sa_lock);
			if (rc) {
				CERROR("%s: failed to add sai: pattern %#X pid=%d rc=%d\n",
				       sbi->ll_fsname, lli->lli_sa_pattern,
				       sai->sai_pid, rc);
				ll_sax_put(ctx);
				ctx = NULL;
				GOTO(out, rc);
			}
		}

		if (rc)
			GOTO(out, rc);
	} else {
		CERROR("%s: unsupported statahead pattern %#X.\n",
		       sbi->ll_fsname, lli->lli_sa_pattern);
		GOTO(out, rc = -EOPNOTSUPP);
	}

	CDEBUG(D_READA, "start statahead thread: [pid %d] [parent %pd]\n",
	       current->pid, parent);

	task = kthread_create_on_node(ll_statahead_thread, sai, node,
				      "ll_sa_%u", sai->sai_pid);
	if (IS_ERR(task)) {
		spin_lock(&lli->lli_sa_lock);
		lli->lli_sai = NULL;
		spin_unlock(&lli->lli_sa_lock);
		rc = PTR_ERR(task);
		CERROR("%s: cannot start ll_sa_%u thread for '%pd': rc = %d\n",
		       sbi->ll_fsname, current->pid, sai->sai_dentry, rc);
		/* Use @ll_sai/sax_put to release @sai and @ctx. */
		fail_free = false;
		GOTO(out, rc);
	}

	if (test_bit(LL_SBI_AGL_ENABLED, sbi->ll_flags) && agl)
		ll_start_agl(parent, sai);

	atomic_inc(&sbi->ll_sa_total);
	if (lli->lli_sa_pattern & LSA_PATTERN_LIST)
		atomic_inc(&sbi->ll_sa_list_total);
	else if (lli->lli_sa_pattern & LSA_PATTERN_FNAME)
		atomic_inc(&sbi->ll_sa_fname_total);

	sai->sai_task = task;
	wake_up_process(task);
	/*
	 * We don't stat-ahead for the first dirent since we are already in
	 * lookup.
	 */
	RETURN(-EAGAIN);

out:
	/*
	 * once we start statahead thread failed, disable statahead so that
	 * subsequent stat won't waste time to try it.
	 */
	spin_lock(&lli->lli_sa_lock);
	if (lli->lli_stat_pid == current->pid)
		lli->lli_sa_enabled = 0;
	spin_unlock(&lli->lli_sa_lock);

	/*
	 * @ll_sai_free() and @ll_sax_free() will not set @lli_sai and @lli_sax
	 * with NULL. When these two values are not set by this process upon
	 * failure, we should free @sai and @ctx directly by using these two
	 * functions.
	 * If using ll_sax/sai_put(), it will change @lli_sai and @lli_sax into
	 * NULL and other statahead threads will use/access these two data
	 * structures (NULL) wrongly.
	 */
	if (fail_free) {
		if (sai)
			ll_sai_free(sai);
		if (ctx)
			ll_sax_free(ctx);
	} else {
		if (sai)
			ll_sai_put(sai);
		if (ctx)
			ll_sax_put(ctx);
	}

	if (rc)
		atomic_dec(&sbi->ll_sa_running);
	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_context *ctx;
	struct ll_statahead_info *sai;

	spin_lock(&lli->lli_sa_lock);
	ctx = lli->lli_sax;
	sai = lli->lli_sai;
	if (sai && (sai->sai_agl_task != NULL) != agl)
		CDEBUG(D_READA,
		       "%s: Statahead AGL hint changed from %d to %d\n",
		       ll_i2sbi(dir)->ll_fsname,
		       sai->sai_agl_task != NULL, agl);
	spin_unlock(&lli->lli_sa_lock);

	return !!ctx;
}

/**
 * ll_start_statahead() - statahead entry function, this is called when client
 *
 * @dir: parent directory
 * @dentry: dentry to getattr
 * @agl: whether start the agl thread
 *
 * 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.
 *
 * Returns:
 * * %1   success
 * * %0   revalidation from statahead cache failed, caller needs to getattr
 * from server directly
 * * <0   on error, caller often ignores this and then getattrfrom 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;
}

/**
 * ll_revalidate_statahead() - revalidate dentry from statahead cache.
 *
 * @dir: parent directory
 * @dentryp: dentry to getattr
 * @unplug: unplug statahead window only (normally for negative dentry)
 *
 * Returns:
 * * %1  success
 * * %0  revalidation from statahead cache failed, caller needs to getattr
 *       from server directly
 * * <0  negative number on error, caller often ignores this and then getattr
 *       from server
 */
int ll_revalidate_statahead(struct inode *dir, struct dentry **dentryp,
			    bool unplug)
{
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_statahead_context *ctx;
	struct ll_statahead_info *sai = NULL;
	int rc = 0;

	spin_lock(&lli->lli_sa_lock);
	ctx = lli->lli_sax;
	if (ctx) {
		sai = lli->lli_sai;
		if (sai) {
			atomic_inc(&sai->sai_refcount);
		} else if (lli->lli_sa_pattern & LSA_PATTERN_LIST) {
			spin_unlock(&lli->lli_sa_lock);
			return 0;
		}
		__ll_sax_get(ctx);
	}
	spin_unlock(&lli->lli_sa_lock);
	if (ctx) {
		rc = revalidate_statahead_dentry(dir, sai, ctx,
						 dentryp, unplug);
		CDEBUG(D_READA, "revalidate statahead %pd: rc = %d.\n",
		       *dentryp, rc);
		if (sai)
			ll_sai_put(sai);
		ll_sax_put(ctx);
	}
	return rc;
}

int ll_ioctl_ahead(struct file *file, struct llapi_lu_ladvise2 *ladvise)
{
	int node = cfs_cpt_spread_node(cfs_cpt_tab, CFS_CPT_ANY);
	struct ll_file_data *lfd = file->private_data;
	struct dentry *dentry = file_dentry(file);
	struct inode *dir = dentry->d_inode;
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_sb_info *sbi = ll_i2sbi(dir);
	struct ll_statahead_info *sai = NULL;
	struct ll_statahead_context *ctx = NULL;
	struct task_struct *task;
	bool agl = true;
	int rc;

	ENTRY;

	if (sbi->ll_sa_max == 0)
		RETURN(0);

	if (!S_ISDIR(dir->i_mode))
		RETURN(-EINVAL);

	if (lfd->fd_sai) {
		rc = -EALREADY;
		CWARN("%s: already set statahead hint for dir %pd: rc = %d\n",
		      sbi->ll_fsname, dentry, rc);
		RETURN(rc);
	}

	if (unlikely(atomic_inc_return(&sbi->ll_sa_running) >
				       sbi->ll_sa_running_max)) {
		CDEBUG(D_READA,
		       "Too many concurrent statahead instances, avoid new statahead instance temporarily.\n");
		GOTO(out, rc = -EMFILE);
	}

	sai = ll_sai_alloc(dentry);
	if (sai == NULL)
		GOTO(out, rc = -ENOMEM);

	sai->sai_fstart = ladvise->lla_start;
	sai->sai_fend = ladvise->lla_end;
	sai->sai_ls_all = 0;
	sai->sai_max = sbi->ll_sa_max;
	strncpy(sai->sai_fname, ladvise->lla_fname, sizeof(sai->sai_fname));
	sai->sai_pid = current->pid;

	ctx = ll_sax_get(dir);
	if (ctx == NULL) {
		ctx = ll_sax_alloc(dir);
		if (ctx == NULL)
			GOTO(out, rc = -ENOMEM);

		spin_lock(&lli->lli_sa_lock);
		if (unlikely(lli->lli_sax)) {
			struct ll_statahead_context *tmp = ctx;

			if (lli->lli_sa_pattern == LSA_PATTERN_NONE ||
			    lli->lli_sa_pattern == LSA_PATTERN_ADVISE) {
				lli->lli_sa_pattern = LSA_PATTERN_ADVISE;
				ctx = lli->lli_sax;
				__ll_sax_get(ctx);
				lfd->fd_sai = __ll_sai_get(sai);
				rc = 0;
			} else {
				rc = -EINVAL;
				CWARN("%s: pattern %X is not ADVISE: rc = %d\n",
				      sbi->ll_fsname, lli->lli_sa_pattern, rc);
			}

			spin_unlock(&lli->lli_sa_lock);
			ll_sax_free(tmp);
			if (rc)
				GOTO(out, rc);
		} else {
			lli->lli_sa_pattern = LSA_PATTERN_ADVISE;
			lli->lli_sax = ctx;
			lfd->fd_sai = __ll_sai_get(sai);
			spin_unlock(&lli->lli_sa_lock);
		}
	} else {
		spin_lock(&lli->lli_sa_lock);
		if (!(lli->lli_sa_pattern == LSA_PATTERN_ADVISE ||
		      lli->lli_sa_pattern == LSA_PATTERN_NONE)) {
			spin_unlock(&lli->lli_sa_lock);
			GOTO(out, rc = -EINVAL);
		}

		lli->lli_sa_pattern = LSA_PATTERN_ADVISE;
		lfd->fd_sai = __ll_sai_get(sai);
		spin_unlock(&lli->lli_sa_lock);
	}

	__ll_sax_get(ctx);
	CDEBUG(D_READA,
	       "start statahead thread: [pid %d] [parent %pd] sai %p ctx %p\n",
	       current->pid, dentry, sai, ctx);

	task = kthread_create_on_node(ll_statahead_thread, sai, node,
				      "ll_sa_%u", current->pid);
	if (IS_ERR(task)) {
		rc = PTR_ERR(task);
		CERROR("%s: cannot start ll_sa_%u thread for '%pd': rc = %d\n",
		       sbi->ll_fsname, sai->sai_pid, sai->sai_dentry, rc);
		GOTO(out, rc);
	}

	if (test_bit(LL_SBI_AGL_ENABLED, sbi->ll_flags) && agl)
		ll_start_agl(dentry, sai);

	atomic_inc(&sbi->ll_sa_total);
	sai->sai_task = task;
	wake_up_process(task);

	RETURN(0);
out:
	if (lfd->fd_sai) {
		ll_sai_put(sai);
		ll_sax_put(ctx);
		lfd->fd_sai = NULL;
	}

	if (sai)
		ll_sai_put(sai);

	if (ctx)
		ll_sax_put(ctx);

	atomic_dec(&sbi->ll_sa_running);
	RETURN(rc);
}

/*
 * This function is called in each stat() system call to do statahead check.
 * When the files' naming of stat() call sequence under a directory follows
 * a certain name rule roughly, this directory is considered as an condicant
 * to do statahead.
 * For an example, the file naming rule is mdtest.$rank.$i, the suffix of
 * the stat() dentry name is number and do stat() for dentries with name
 * ending with number more than @LSA_FN_PREDICT_HIT, then the corresponding
 * directory is met the requrirement for statahead.
 */
void ll_statahead_enter(struct inode *dir, struct dentry *dchild)
{
	struct ll_inode_info *lli = ll_i2info(dir);
	struct ll_sb_info *sbi = ll_i2sbi(dir);
	const struct qstr *dname = &dchild->d_name;

	if (sbi->ll_sa_max == 0)
		return;

	if (sbi->ll_enable_statahead_fname == 0)
		return;

	if (lli->lli_sa_enabled)
		return;

	if (lli->lli_sa_pattern & (LSA_PATTERN_FN_PREDICT | LSA_PATTERN_LIST))
		return;

	/*
	 * Now support number indexing regularized statahead pattern only.
	 * Quick check whether the last character is digit.
	 */
	if (!isdigit(dname->name[dname->len - 1])) {
		lli->lli_sa_pattern &= ~LSA_PATTERN_FN_PREDICT;
		lli->lli_sa_match_count = 0;
		return;
	}

	lli->lli_sa_match_count++;
	if (lli->lli_sa_match_count > sbi->ll_sa_fname_predict_hit) {
		spin_lock(&lli->lli_sa_lock);
		lli->lli_sa_pattern |= LSA_PATTERN_FN_PREDICT;
		spin_unlock(&lli->lli_sa_lock);
		lli->lli_sa_enabled = 1;
		lli->lli_sa_match_count = 0;
	}
}