Subject: linux-next: manual merge of the vfs tree with the ceph tree

From: Stephen Rothwell <[email-masked]>
Date: Fri, 26 Jun 2015 11:17:55 +1000

Hi Al,

Today's linux-next merge of the vfs tree got a conflict in:

  fs/ceph/dir.c

between commit:

  fdd4e15838e5 ("ceph: rework dcache readdir")

from the ceph tree and commit:

  dc3f4198eac1 ("make simple_positive() public")

from the vfs tree.

I fixed it up (I think - see below) and can carry the fix as necessary (no action
is required).

Sage: that ceph tree commit has no Signed-off-by from its
committer :-(  The same is true for almost all the commits in the ceph
tree ... and the committer (Ilya) is not listed as a maintainer of that
tree ... and its on github :-(
-- 
Cheers,
Stephen Rothwell                    [email-masked]

diff --cc fs/ceph/dir.c
index 9314b4ea2375,edbb8da02a6a..000000000000
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@@ -144,110 -123,101 +144,110 @@@ static int __dcache_readdir(struct fil
  	struct ceph_file_info *fi = file->private_data;
  	struct dentry *parent = file->f_path.dentry;
  	struct inode *dir = d_inode(parent);
 -	struct list_head *p;
 -	struct dentry *dentry, *last;
 +	struct dentry *dentry, *last = NULL;
  	struct ceph_dentry_info *di;
 +	unsigned nsize = PAGE_CACHE_SIZE / sizeof(struct dentry *);
  	int err = 0;
 +	loff_t ptr_pos = 0;
 +	struct ceph_readdir_cache_control cache_ctl = {};
  
 -	/* claim ref on last dentry we returned */
 -	last = fi->dentry;
 -	fi->dentry = NULL;
 -
 -	dout("__dcache_readdir %p v%u at %llu (last %p)\n",
 -	     dir, shared_gen, ctx->pos, last);
 +	dout("__dcache_readdir %p v%u at %llu\n", dir, shared_gen, ctx->pos);
  
 -	spin_lock(&parent->d_lock);
 -
 -	/* start at beginning? */
 -	if (ctx->pos == 2 || last == NULL ||
 -	    fpos_cmp(ctx->pos, ceph_dentry(last)->offset) < 0) {
 -		if (list_empty(&parent->d_subdirs))
 -			goto out_unlock;
 -		p = parent->d_subdirs.prev;
 -		dout(" initial p %p/%p\n", p->prev, p->next);
 -	} else {
 -		p = last->d_child.prev;
 +	/* we can calculate cache index for the first dirfrag */
 +	if (ceph_frag_is_leftmost(fpos_frag(ctx->pos))) {
 +		cache_ctl.index = fpos_off(ctx->pos) - 2;
 +		BUG_ON(cache_ctl.index < 0);
 +		ptr_pos = cache_ctl.index * sizeof(struct dentry *);
  	}
  
 -more:
 -	dentry = list_entry(p, struct dentry, d_child);
 -	di = ceph_dentry(dentry);
 -	while (1) {
 -		dout(" p %p/%p %s d_subdirs %p/%p\n", p->prev, p->next,
 -		     d_unhashed(dentry) ? "!hashed" : "hashed",
 -		     parent->d_subdirs.prev, parent->d_subdirs.next);
 -		if (p == &parent->d_subdirs) {
 +	while (true) {
 +		pgoff_t pgoff;
 +		bool emit_dentry;
 +
 +		if (ptr_pos >= i_size_read(dir)) {
  			fi->flags |= CEPH_F_ATEND;
 -			goto out_unlock;
 +			err = 0;
 +			break;
 +		}
 +
 +		err = -EAGAIN;
 +		pgoff = ptr_pos >> PAGE_CACHE_SHIFT;
 +		if (!cache_ctl.page || pgoff != page_index(cache_ctl.page)) {
 +			ceph_readdir_cache_release(&cache_ctl);
 +			cache_ctl.page = find_lock_page(&dir->i_data, pgoff);
 +			if (!cache_ctl.page) {
 +				dout(" page %lu not found\n", pgoff);
 +				break;
 +			}
 +			/* reading/filling the cache are serialized by
 +			 * i_mutex, no need to use page lock */
 +			unlock_page(cache_ctl.page);
 +			cache_ctl.dentries = kmap(cache_ctl.page);
  		}
 -		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
 +
 +		rcu_read_lock();
 +		spin_lock(&parent->d_lock);
 +		/* check i_size again here, because empty directory can be
 +		 * marked as complete while not holding the i_mutex. */
 +		if (ceph_dir_is_complete_ordered(dir) &&
 +		    ptr_pos < i_size_read(dir))
 +			dentry = cache_ctl.dentries[cache_ctl.index % nsize];
 +		else
 +			dentry = NULL;
 +		spin_unlock(&parent->d_lock);
 +		if (dentry && !lockref_get_not_dead(&dentry->d_lockref))
 +			dentry = NULL;
 +		rcu_read_unlock();
 +		if (!dentry)
 +			break;
 +
 +		emit_dentry = false;
 +		di = ceph_dentry(dentry);
 +		spin_lock(&dentry->d_lock);
  		if (di->lease_shared_gen == shared_gen &&
- 		    d_really_is_positive(dentry) &&
+ 		    simple_positive(dentry) &&
  		    ceph_snap(d_inode(dentry)) != CEPH_SNAPDIR &&
  		    ceph_ino(d_inode(dentry)) != CEPH_INO_CEPH &&
 -		    fpos_cmp(ctx->pos, di->offset) <= 0)
 -			break;
 -		dout(" skipping %p %pd at %llu (%llu)%s%s\n", dentry,
 -		     dentry, di->offset,
 -		     ctx->pos, d_unhashed(dentry) ? " unhashed" : "",
 -		     !d_inode(dentry) ? " null" : "");
 +		    fpos_cmp(ctx->pos, di->offset) <= 0) {
 +			emit_dentry = true;
 +		}
  		spin_unlock(&dentry->d_lock);
 -		p = p->prev;
 -		dentry = list_entry(p, struct dentry, d_child);
 -		di = ceph_dentry(dentry);
 -	}
 -
 -	dget_dlock(dentry);
 -	spin_unlock(&dentry->d_lock);
 -	spin_unlock(&parent->d_lock);
  
 -	/* make sure a dentry wasn't dropped while we didn't have parent lock */
 -	if (!ceph_dir_is_complete_ordered(dir)) {
 -		dout(" lost dir complete on %p; falling back to mds\n", dir);
 -		dput(dentry);
 -		err = -EAGAIN;
 -		goto out;
 -	}
 +		if (emit_dentry) {
 +			dout(" %llu (%llu) dentry %p %pd %p\n", di->offset, ctx->pos,
 +			     dentry, dentry, d_inode(dentry));
 +			ctx->pos = di->offset;
 +			if (!dir_emit(ctx, dentry->d_name.name,
 +				      dentry->d_name.len,
 +				      ceph_translate_ino(dentry->d_sb,
 +							 d_inode(dentry)->i_ino),
 +				      d_inode(dentry)->i_mode >> 12)) {
 +				dput(dentry);
 +				err = 0;
 +				break;
 +			}
 +			ctx->pos++;
  
 -	dout(" %llu (%llu) dentry %p %pd %p\n", di->offset, ctx->pos,
 -	     dentry, dentry, d_inode(dentry));
 -	if (!dir_emit(ctx, dentry->d_name.name,
 -		      dentry->d_name.len,
 -		      ceph_translate_ino(dentry->d_sb, d_inode(dentry)->i_ino),
 -		      d_inode(dentry)->i_mode >> 12)) {
 -		if (last) {
 -			/* remember our position */
 -			fi->dentry = last;
 -			fi->next_offset = fpos_off(di->offset);
 +			if (last)
 +				dput(last);
 +			last = dentry;
 +		} else {
 +			dput(dentry);
  		}
 -		dput(dentry);
 -		return 0;
 -	}
 -
 -	ctx->pos = di->offset + 1;
  
 -	if (last)
 -		dput(last);
 -	last = dentry;
 -
 -	spin_lock(&parent->d_lock);
 -	p = p->prev;	/* advance to next dentry */
 -	goto more;
 -
 -out_unlock:
 -	spin_unlock(&parent->d_lock);
 -out:
 -	if (last)
 +		cache_ctl.index++;
 +		ptr_pos += sizeof(struct dentry *);
 +	}
 +	ceph_readdir_cache_release(&cache_ctl);
 +	if (last) {
 +		int ret;
 +		di = ceph_dentry(last);
 +		ret = note_last_dentry(fi, last->d_name.name, last->d_name.len,
 +				       fpos_off(di->offset) + 1);
 +		if (ret < 0)
 +			err = ret;
  		dput(last);
 +	}
  	return err;
  }