- struct ll_inode_info *lli;
- struct ll_statahead_info *sai;
- struct dentry *parent;
- struct l_wait_info lwi = { 0 };
- int rc = 0;
- ENTRY;
-
- LASSERT(dir != NULL);
- lli = ll_i2info(dir);
- LASSERT(lli->lli_opendir_pid == cfs_curproc_pid());
- sai = lli->lli_sai;
-
- if (sai) {
- if (unlikely(sa_is_stopped(sai) &&
- cfs_list_empty(&sai->sai_entries_stated)))
- RETURN(-EBADFD);
-
- if ((*dentryp)->d_name.name[0] == '.') {
- if (likely(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(-ENOENT);
- }
- }
-
- if (!ll_sai_entry_stated(sai)) {
- /*
- * thread started already, avoid double-stat.
- */
- lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
- rc = l_wait_event(sai->sai_waitq,
- ll_sai_entry_stated(sai) ||
- sa_is_stopped(sai),
- &lwi);
- if (unlikely(rc == -EINTR))
- RETURN(rc);
- }
-
- if (ll_sai_entry_stated(sai)) {
- struct ll_sai_entry *entry;
-
- entry = cfs_list_entry(sai->sai_entries_stated.next,
- struct ll_sai_entry, se_list);
- /* This is for statahead lookup */
- if (entry->se_inode != NULL) {
- struct lookup_intent it = {.it_op = IT_LOOKUP};
- struct dentry *dchild = entry->se_dentry;
- struct inode *ichild = entry->se_inode;
- struct ll_dentry_data *lld = ll_d2d(dchild);
- struct ll_inode_info *sei = ll_i2info(ichild);
- struct dentry *save = dchild;
- int invalid = 0;
- __u32 bits = MDS_INODELOCK_LOOKUP |
- MDS_INODELOCK_UPDATE;
- int found = 0;
-
- LASSERT(dchild != *dentryp);
-
- if (!lookup)
- mutex_lock(&dir->i_mutex);
-
- /*
- * Make sure dentry is still valid.
- * For statahead lookup case, we need both
- * LOOKUP lock and UPDATE lock which obtained
- * by statahead thread originally.
- *
- * Consider following racer case:
- * 1. statahead thread on client1 get lock with
- * both LOOKUK and UPDATE bits for "aaa"
- * 2. rename thread on client2 cancel such lock
- * from client1, then rename "aaa" to "bbb"
- * 3. ls thread on client1 obtain LOOKUP lock
- * for "bbb" again
- * 4. here the dentry "aaa" created by statahead
- * thread should be invalid even related
- * LOOKUP lock valid for the same inode
- */
- rc = md_revalidate_lock(ll_i2mdexp(dir), &it,
- ll_inode2fid(ichild),
- &bits);
- cfs_spin_lock(&sei->lli_sa_lock);
- if (!cfs_list_empty(&lld->lld_sa_alias))
- cfs_list_del_init(&lld->lld_sa_alias);
- else
- invalid = 1;
- cfs_spin_unlock(&sei->lli_sa_lock);
- if (rc != 1)
- /* Someone has cancelled the original
- * lock before the real "revalidate"
- * using it. Drop it. */
- goto out_mutex;
-
- if (invalid) {
- /* Someone has cancelled the original
- * lock, and reobtained it, the dentry
- * maybe invalid anymore, Drop it. */
- ll_intent_drop_lock(&it);
- goto out_mutex;
- }
-
- ll_lookup_it_alias(&dchild, ichild, bits);
- found = is_same_dentry(*dentryp, dchild, lookup);
- ll_lookup_finish_locks(&it, dchild);
- if (dchild != save)
- dput(save);
- ichild = NULL;
-
-out_mutex:
- if (!lookup)
- mutex_unlock(&dir->i_mutex);
- /* Drop the inode reference count held by
- * interpreter. */
- if (ichild != NULL)
- iput(ichild);
-
- entry->se_dentry = NULL;
- entry->se_inode = NULL;
- if (found) {
- if (lookup) {
- LASSERT(*dentryp != dchild);
- /* VFS will drop the reference
- * count for dchild and *dentryp
- * by itself. */
- *dentryp = dchild;
- } else {
- LASSERT(*dentryp == dchild);
- /* Drop the dentry reference
- * count held by statahead. */
- dput(dchild);
- }
- RETURN(1);
- } else {
- /* Drop the dentry reference count held
- * by statahead. */
- dput(dchild);
- }
- }
- }
-
- if (lookup) {
- struct dentry *result;
-
- result = d_lookup((*dentryp)->d_parent,
- &(*dentryp)->d_name);
- if (result) {
- LASSERT(result != *dentryp);
- /* BUG 16303: do not drop reference count for
- * "*dentryp", VFS will do that by itself. */
- *dentryp = result;
- RETURN(1);
- }
- }
- /*
- * do nothing for revalidate.
- */
- RETURN(0);
- }
-
- /* I am the "lli_opendir_pid" owner, only me can set "lli_sai". */
- rc = is_first_dirent(dir, *dentryp);
- if (rc == LS_NONE_FIRST_DE)
- /* It is not "ls -{a}l" operation, no need statahead for it. */
- GOTO(out, rc = -EAGAIN);
-
- sai = ll_sai_alloc();
- if (sai == NULL)
- GOTO(out, rc = -ENOMEM);
-
- sai->sai_ls_all = (rc == LS_FIRST_DOT_DE);
- sai->sai_inode = igrab(dir);
- if (unlikely(sai->sai_inode == NULL)) {
- CWARN("Do not start stat ahead on dying inode "DFID"\n",
- PFID(&lli->lli_fid));
- OBD_FREE_PTR(sai);
- GOTO(out, rc = -ESTALE);
- }
-
- /* get parent reference count here, and put it in ll_statahead_thread */
- parent = dget((*dentryp)->d_parent);
- if (unlikely(sai->sai_inode != parent->d_inode)) {
- struct ll_inode_info *nlli = ll_i2info(parent->d_inode);
-
- CWARN("Race condition, someone changed %.*s just now: "
- "old parent "DFID", new parent "DFID"\n",
- (*dentryp)->d_name.len, (*dentryp)->d_name.name,
- PFID(&lli->lli_fid), PFID(&nlli->lli_fid));
- dput(parent);
- iput(sai->sai_inode);
- OBD_FREE_PTR(sai);
- RETURN(-EAGAIN);
- }
+ struct ll_inode_info *lli = ll_i2info(dir);
+ struct ll_statahead_info *sai = NULL;
+ struct dentry *parent = dentry->d_parent;
+ struct ptlrpc_thread *thread;
+ struct l_wait_info lwi = { 0 };
+ struct task_struct *task;
+ int rc;
+ ENTRY;
+
+ /* I am the "lli_opendir_pid" owner, only me can set "lli_sai". */
+ rc = is_first_dirent(dir, dentry);
+ if (rc == LS_NOT_FIRST_DE)
+ /* It is not "ls -{a}l" operation, no need statahead for it. */
+ GOTO(out, rc = -EFAULT);
+
+ sai = ll_sai_alloc(parent);
+ if (sai == NULL)
+ GOTO(out, rc = -ENOMEM);
+
+ sai->sai_ls_all = (rc == LS_FIRST_DOT_DE);
+
+ /* 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 != NULL ||
+ lli->lli_opendir_key == NULL ||
+ lli->lli_opendir_pid != current->pid)) {
+ spin_unlock(&lli->lli_sa_lock);
+ GOTO(out, rc = -EPERM);
+ }
+ lli->lli_sai = sai;
+ spin_unlock(&lli->lli_sa_lock);
+
+ atomic_inc(&ll_i2sbi(parent->d_inode)->ll_sa_running);
+
+ CDEBUG(D_READA, "start statahead thread: [pid %d] [parent %.*s]\n",
+ current_pid(), parent->d_name.len, parent->d_name.name);
+
+ task = kthread_run(ll_statahead_thread, parent, "ll_sa_%u",
+ lli->lli_opendir_pid);
+ thread = &sai->sai_thread;
+ if (IS_ERR(task)) {
+ rc = PTR_ERR(task);
+ CERROR("can't start ll_sa thread, rc: %d\n", rc);
+ GOTO(out, rc);
+ }
+
+ l_wait_event(thread->t_ctl_waitq,
+ thread_is_running(thread) || thread_is_stopped(thread),
+ &lwi);
+ ll_sai_put(sai);
+
+ /*
+ * We don't stat-ahead for the first dirent since we are already in
+ * lookup.
+ */
+ RETURN(-EAGAIN);