Commit ec108d3c authored by Anna Schumaker's avatar Anna Schumaker
Browse files

NFS: Convert readdir page array functions to use a folio

parent 61f02e0a
Loading
Loading
Loading
Loading
+99 −102
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ static int nfs_closedir(struct inode *, struct file *);
static int nfs_readdir(struct file *, struct dir_context *);
static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
static loff_t nfs_llseek_dir(struct file *, loff_t, int);
static void nfs_readdir_free_folio(struct folio *);
static void nfs_readdir_clear_array(struct folio *);

const struct file_operations nfs_dir_operations = {
	.llseek		= nfs_llseek_dir,
@@ -67,7 +67,7 @@ const struct file_operations nfs_dir_operations = {
};

const struct address_space_operations nfs_dir_aops = {
	.free_folio = nfs_readdir_free_folio,
	.free_folio = nfs_readdir_clear_array,
};

#define NFS_INIT_DTSIZE PAGE_SIZE
@@ -146,8 +146,8 @@ struct nfs_cache_array {
	u64 change_attr;
	u64 last_cookie;
	unsigned int size;
	unsigned char page_full : 1,
		      page_is_eof : 1,
	unsigned char folio_full : 1,
		      folio_is_eof : 1,
		      cookies_are_ordered : 1;
	struct nfs_cache_array_entry array[];
};
@@ -198,17 +198,17 @@ static void nfs_grow_dtsize(struct nfs_readdir_descriptor *desc)
	nfs_set_dtsize(desc, desc->dtsize << 1);
}

static void nfs_readdir_page_init_array(struct page *page, u64 last_cookie,
static void nfs_readdir_folio_init_array(struct folio *folio, u64 last_cookie,
					 u64 change_attr)
{
	struct nfs_cache_array *array;

	array = kmap_local_page(page);
	array = kmap_local_folio(folio, 0);
	array->change_attr = change_attr;
	array->last_cookie = last_cookie;
	array->size = 0;
	array->page_full = 0;
	array->page_is_eof = 0;
	array->folio_full = 0;
	array->folio_is_eof = 0;
	array->cookies_are_ordered = 1;
	kunmap_local(array);
}
@@ -216,44 +216,39 @@ static void nfs_readdir_page_init_array(struct page *page, u64 last_cookie,
/*
 * we are freeing strings created by nfs_add_to_readdir_array()
 */
static void nfs_readdir_clear_array(struct page *page)
static void nfs_readdir_clear_array(struct folio *folio)
{
	struct nfs_cache_array *array;
	unsigned int i;

	array = kmap_local_page(page);
	array = kmap_local_folio(folio, 0);
	for (i = 0; i < array->size; i++)
		kfree(array->array[i].name);
	array->size = 0;
	kunmap_local(array);
}

static void nfs_readdir_free_folio(struct folio *folio)
{
	nfs_readdir_clear_array(&folio->page);
}

static void nfs_readdir_page_reinit_array(struct page *page, u64 last_cookie,
static void nfs_readdir_folio_reinit_array(struct folio *folio, u64 last_cookie,
					   u64 change_attr)
{
	nfs_readdir_clear_array(page);
	nfs_readdir_page_init_array(page, last_cookie, change_attr);
	nfs_readdir_clear_array(folio);
	nfs_readdir_folio_init_array(folio, last_cookie, change_attr);
}

static struct page *
nfs_readdir_page_array_alloc(u64 last_cookie, gfp_t gfp_flags)
static struct folio *
nfs_readdir_folio_array_alloc(u64 last_cookie, gfp_t gfp_flags)
{
	struct page *page = alloc_page(gfp_flags);
	if (page)
		nfs_readdir_page_init_array(page, last_cookie, 0);
	return page;
	struct folio *folio = folio_alloc(gfp_flags, 0);
	if (folio)
		nfs_readdir_folio_init_array(folio, last_cookie, 0);
	return folio;
}

static void nfs_readdir_page_array_free(struct page *page)
static void nfs_readdir_folio_array_free(struct folio *folio)
{
	if (page) {
		nfs_readdir_clear_array(page);
		put_page(page);
	if (folio) {
		nfs_readdir_clear_array(folio);
		folio_put(folio);
	}
}

@@ -264,13 +259,13 @@ static u64 nfs_readdir_array_index_cookie(struct nfs_cache_array *array)

static void nfs_readdir_array_set_eof(struct nfs_cache_array *array)
{
	array->page_is_eof = 1;
	array->page_full = 1;
	array->folio_is_eof = 1;
	array->folio_full = 1;
}

static bool nfs_readdir_array_is_full(struct nfs_cache_array *array)
{
	return array->page_full;
	return array->folio_full;
}

/*
@@ -302,16 +297,16 @@ static size_t nfs_readdir_array_maxentries(void)
 */
static int nfs_readdir_array_can_expand(struct nfs_cache_array *array)
{
	if (array->page_full)
	if (array->folio_full)
		return -ENOSPC;
	if (array->size == nfs_readdir_array_maxentries()) {
		array->page_full = 1;
		array->folio_full = 1;
		return -ENOSPC;
	}
	return 0;
}

static int nfs_readdir_page_array_append(struct page *page,
static int nfs_readdir_folio_array_append(struct folio *folio,
					  const struct nfs_entry *entry,
					  u64 *cookie)
{
@@ -322,7 +317,7 @@ static int nfs_readdir_page_array_append(struct page *page,

	name = nfs_readdir_copy_name(entry->name, entry->len);

	array = kmap_atomic(page);
	array = kmap_atomic(folio_page(folio, 0));
	if (!name)
		goto out;
	ret = nfs_readdir_array_can_expand(array);
@@ -361,17 +356,17 @@ static int nfs_readdir_page_array_append(struct page *page,
 * 127 readdir entries for a typical 64-bit system, that works out to a
 * cache of ~ 33 million entries per directory.
 */
static pgoff_t nfs_readdir_page_cookie_hash(u64 cookie)
static pgoff_t nfs_readdir_folio_cookie_hash(u64 cookie)
{
	if (cookie == 0)
		return 0;
	return hash_64(cookie, 18);
}

static bool nfs_readdir_page_validate(struct page *page, u64 last_cookie,
static bool nfs_readdir_folio_validate(struct folio *folio, u64 last_cookie,
				       u64 change_attr)
{
	struct nfs_cache_array *array = kmap_local_page(page);
	struct nfs_cache_array *array = kmap_local_folio(folio, 0);
	int ret = true;

	if (array->change_attr != change_attr)
@@ -382,81 +377,83 @@ static bool nfs_readdir_page_validate(struct page *page, u64 last_cookie,
	return ret;
}

static void nfs_readdir_page_unlock_and_put(struct page *page)
static void nfs_readdir_folio_unlock_and_put(struct folio *folio)
{
	unlock_page(page);
	put_page(page);
	folio_unlock(folio);
	folio_put(folio);
}

static void nfs_readdir_page_init_and_validate(struct page *page, u64 cookie,
static void nfs_readdir_folio_init_and_validate(struct folio *folio, u64 cookie,
						u64 change_attr)
{
	if (PageUptodate(page)) {
		if (nfs_readdir_page_validate(page, cookie, change_attr))
	if (folio_test_uptodate(folio)) {
		if (nfs_readdir_folio_validate(folio, cookie, change_attr))
			return;
		nfs_readdir_clear_array(page);
		nfs_readdir_clear_array(folio);
	}
	nfs_readdir_page_init_array(page, cookie, change_attr);
	SetPageUptodate(page);
	nfs_readdir_folio_init_array(folio, cookie, change_attr);
	folio_mark_uptodate(folio);
}

static struct page *nfs_readdir_page_get_locked(struct address_space *mapping,
static struct folio *nfs_readdir_folio_get_locked(struct address_space *mapping,
						  u64 cookie, u64 change_attr)
{
	pgoff_t index = nfs_readdir_page_cookie_hash(cookie);
	struct page *page;
	pgoff_t index = nfs_readdir_folio_cookie_hash(cookie);
	struct folio *folio;

	page = grab_cache_page(mapping, index);
	if (!page)
	folio = filemap_grab_folio(mapping, index);
	if (!folio)
		return NULL;
	nfs_readdir_page_init_and_validate(page, cookie, change_attr);
	return page;
	nfs_readdir_folio_init_and_validate(folio, cookie, change_attr);
	return folio;
}

static u64 nfs_readdir_page_last_cookie(struct page *page)
static u64 nfs_readdir_folio_last_cookie(struct folio *folio)
{
	struct nfs_cache_array *array;
	u64 ret;

	array = kmap_local_page(page);
	array = kmap_local_folio(folio, 0);
	ret = array->last_cookie;
	kunmap_local(array);
	return ret;
}

static bool nfs_readdir_page_needs_filling(struct page *page)
static bool nfs_readdir_folio_needs_filling(struct folio *folio)
{
	struct nfs_cache_array *array;
	bool ret;

	array = kmap_local_page(page);
	array = kmap_local_folio(folio, 0);
	ret = !nfs_readdir_array_is_full(array);
	kunmap_local(array);
	return ret;
}

static void nfs_readdir_page_set_eof(struct page *page)
static void nfs_readdir_folio_set_eof(struct folio *folio)
{
	struct nfs_cache_array *array;

	array = kmap_local_page(page);
	array = kmap_local_folio(folio, 0);
	nfs_readdir_array_set_eof(array);
	kunmap_local(array);
}

static struct page *nfs_readdir_page_get_next(struct address_space *mapping,
static struct folio *nfs_readdir_folio_get_next(struct address_space *mapping,
						u64 cookie, u64 change_attr)
{
	pgoff_t index = nfs_readdir_page_cookie_hash(cookie);
	struct page *page;
	pgoff_t index = nfs_readdir_folio_cookie_hash(cookie);
	struct folio *folio;

	page = grab_cache_page_nowait(mapping, index);
	if (!page)
	folio = __filemap_get_folio(mapping, index,
			FGP_LOCK|FGP_CREAT|FGP_NOFS|FGP_NOWAIT,
			mapping_gfp_mask(mapping));
	if (!folio)
		return NULL;
	nfs_readdir_page_init_and_validate(page, cookie, change_attr);
	if (nfs_readdir_page_last_cookie(page) != cookie)
		nfs_readdir_page_reinit_array(page, cookie, change_attr);
	return page;
	nfs_readdir_folio_init_and_validate(folio, cookie, change_attr);
	if (nfs_readdir_folio_last_cookie(folio) != cookie)
		nfs_readdir_folio_reinit_array(folio, cookie, change_attr);
	return folio;
}

static inline
@@ -481,7 +478,7 @@ bool nfs_readdir_use_cookie(const struct file *filp)
static void nfs_readdir_seek_next_array(struct nfs_cache_array *array,
					struct nfs_readdir_descriptor *desc)
{
	if (array->page_full) {
	if (array->folio_full) {
		desc->last_cookie = array->last_cookie;
		desc->current_index += array->size;
		desc->cache_entry_index = 0;
@@ -506,7 +503,7 @@ static int nfs_readdir_search_for_pos(struct nfs_cache_array *array,
	if (diff < 0)
		goto out_eof;
	if (diff >= array->size) {
		if (array->page_is_eof)
		if (array->folio_is_eof)
			goto out_eof;
		nfs_readdir_seek_next_array(array, desc);
		return -EAGAIN;
@@ -554,7 +551,7 @@ static int nfs_readdir_search_for_cookie(struct nfs_cache_array *array,
		}
	}
check_eof:
	if (array->page_is_eof) {
	if (array->folio_is_eof) {
		status = -EBADCOOKIE;
		if (desc->dir_cookie == array->last_cookie)
			desc->eof = true;
@@ -826,9 +823,9 @@ static int nfs_readdir_folio_filler(struct nfs_readdir_descriptor *desc,
				    u64 change_attr)
{
	struct address_space *mapping = desc->file->f_mapping;
	struct folio *folio = *arrays;
	struct folio *new, *folio = *arrays;
	struct xdr_stream stream;
	struct page *scratch, *new;
	struct page *scratch;
	struct xdr_buf buf;
	u64 cookie;
	int status;
@@ -845,36 +842,36 @@ static int nfs_readdir_folio_filler(struct nfs_readdir_descriptor *desc,
		if (status != 0)
			break;

		status = nfs_readdir_page_array_append(folio_page(folio, 0), entry, &cookie);
		status = nfs_readdir_folio_array_append(folio, entry, &cookie);
		if (status != -ENOSPC)
			continue;

		if (folio->mapping != mapping) {
			if (!--narrays)
				break;
			new = nfs_readdir_page_array_alloc(cookie, GFP_KERNEL);
			new = nfs_readdir_folio_array_alloc(cookie, GFP_KERNEL);
			if (!new)
				break;
			arrays++;
			*arrays = folio = page_folio(new);
			*arrays = folio = new;
		} else {
			new = nfs_readdir_page_get_next(mapping, cookie,
			new = nfs_readdir_folio_get_next(mapping, cookie,
							 change_attr);
			if (!new)
				break;
			if (folio != *arrays)
				nfs_readdir_page_unlock_and_put(folio_page(folio, 0));
			folio = page_folio(new);
				nfs_readdir_folio_unlock_and_put(folio);
			folio = new;
		}
		desc->folio_index_max++;
		status = nfs_readdir_page_array_append(folio_page(folio, 0), entry, &cookie);
		status = nfs_readdir_folio_array_append(folio, entry, &cookie);
	} while (!status && !entry->eof);

	switch (status) {
	case -EBADCOOKIE:
		if (!entry->eof)
			break;
		nfs_readdir_page_set_eof(folio_page(folio, 0));
		nfs_readdir_folio_set_eof(folio);
		fallthrough;
	case -EAGAIN:
		status = 0;
@@ -888,7 +885,7 @@ static int nfs_readdir_folio_filler(struct nfs_readdir_descriptor *desc,
	}

	if (folio != *arrays)
		nfs_readdir_page_unlock_and_put(folio_page(folio, 0));
		nfs_readdir_folio_unlock_and_put(folio);

	put_page(scratch);
	return status;
@@ -943,7 +940,7 @@ static int nfs_readdir_xdr_to_array(struct nfs_readdir_descriptor *desc,
	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
	if (!entry)
		return -ENOMEM;
	entry->cookie = nfs_readdir_page_last_cookie(folio_page(folio, 0));
	entry->cookie = nfs_readdir_folio_last_cookie(folio);
	entry->fh = nfs_alloc_fhandle();
	entry->fattr = nfs_alloc_fattr_with_label(NFS_SERVER(inode));
	entry->server = NFS_SERVER(inode);
@@ -966,7 +963,7 @@ static int nfs_readdir_xdr_to_array(struct nfs_readdir_descriptor *desc,
		status = nfs_readdir_folio_filler(desc, entry, pages, pglen,
						  arrays, narrays, change_attr);
	else
		nfs_readdir_page_set_eof(folio_page(folio, 0));
		nfs_readdir_folio_set_eof(folio);
	desc->buffer_fills++;

free_pages:
@@ -997,14 +994,14 @@ nfs_readdir_folio_get_cached(struct nfs_readdir_descriptor *desc)
	struct address_space *mapping = desc->file->f_mapping;
	u64 change_attr = inode_peek_iversion_raw(mapping->host);
	u64 cookie = desc->last_cookie;
	struct page *page;
	struct folio *folio;

	page = nfs_readdir_page_get_locked(mapping, cookie, change_attr);
	if (!page)
	folio = nfs_readdir_folio_get_locked(mapping, cookie, change_attr);
	if (!folio)
		return NULL;
	if (desc->clear_cache && !nfs_readdir_page_needs_filling(page))
		nfs_readdir_page_reinit_array(page, cookie, change_attr);
	return page_folio(page);
	if (desc->clear_cache && !nfs_readdir_folio_needs_filling(folio))
		nfs_readdir_folio_reinit_array(folio, cookie, change_attr);
	return folio;
}

/*
@@ -1021,7 +1018,7 @@ static int find_and_lock_cache_page(struct nfs_readdir_descriptor *desc)
	desc->folio = nfs_readdir_folio_get_cached(desc);
	if (!desc->folio)
		return -ENOMEM;
	if (nfs_readdir_page_needs_filling(folio_page(desc->folio, 0))) {
	if (nfs_readdir_folio_needs_filling(desc->folio)) {
		/* Grow the dtsize if we had to go back for more pages */
		if (desc->folio_index == desc->folio_index_max)
			nfs_grow_dtsize(desc);
@@ -1115,7 +1112,7 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
			break;
		}
	}
	if (array->page_is_eof)
	if (array->folio_is_eof)
		desc->eof = !desc->eob;

	kunmap_local(array);
@@ -1148,7 +1145,7 @@ static int uncached_readdir(struct nfs_readdir_descriptor *desc)
	arrays = kcalloc(sz, sizeof(*arrays), GFP_KERNEL);
	if (!arrays)
		goto out;
	arrays[0] = page_folio(nfs_readdir_page_array_alloc(desc->dir_cookie, GFP_KERNEL));
	arrays[0] = nfs_readdir_folio_array_alloc(desc->dir_cookie, GFP_KERNEL);
	if (!arrays[0])
		goto out;

@@ -1185,7 +1182,7 @@ static int uncached_readdir(struct nfs_readdir_descriptor *desc)
	}
out_free:
	for (i = 0; i < sz && arrays[i]; i++)
		nfs_readdir_page_array_free(folio_page(arrays[i], 0));
		nfs_readdir_folio_array_free(arrays[i]);
out:
	if (!nfs_readdir_use_cookie(desc->file))
		nfs_readdir_rewind_search(desc);