Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ac0b74d8 authored by Greg Farnum's avatar Greg Farnum Committed by Sage Weil
Browse files

ceph: add pagelist_reserve, pagelist_truncate, pagelist_set_cursor



These facilitate preallocation of pages so that we can encode into the pagelist
in an atomic context.

Signed-off-by: default avatarGreg Farnum <gregf@hq.newdream.net>
Signed-off-by: default avatarSage Weil <sage@newdream.net>
parent 18a38193
Loading
Loading
Loading
Loading
+21 −0
Original line number Original line Diff line number Diff line
@@ -8,6 +8,14 @@ struct ceph_pagelist {
	void *mapped_tail;
	void *mapped_tail;
	size_t length;
	size_t length;
	size_t room;
	size_t room;
	struct list_head free_list;
	size_t num_pages_free;
};

struct ceph_pagelist_cursor {
	struct ceph_pagelist *pl;   /* pagelist, for error checking */
	struct list_head *page_lru; /* page in list */
	size_t room;		    /* room remaining to reset to */
};
};


static inline void ceph_pagelist_init(struct ceph_pagelist *pl)
static inline void ceph_pagelist_init(struct ceph_pagelist *pl)
@@ -16,11 +24,24 @@ static inline void ceph_pagelist_init(struct ceph_pagelist *pl)
	pl->mapped_tail = NULL;
	pl->mapped_tail = NULL;
	pl->length = 0;
	pl->length = 0;
	pl->room = 0;
	pl->room = 0;
	INIT_LIST_HEAD(&pl->free_list);
	pl->num_pages_free = 0;
}
}

extern int ceph_pagelist_release(struct ceph_pagelist *pl);
extern int ceph_pagelist_release(struct ceph_pagelist *pl);


extern int ceph_pagelist_append(struct ceph_pagelist *pl, const void *d, size_t l);
extern int ceph_pagelist_append(struct ceph_pagelist *pl, const void *d, size_t l);


extern int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space);

extern int ceph_pagelist_free_reserve(struct ceph_pagelist *pl);

extern void ceph_pagelist_set_cursor(struct ceph_pagelist *pl,
				     struct ceph_pagelist_cursor *c);

extern int ceph_pagelist_truncate(struct ceph_pagelist *pl,
				  struct ceph_pagelist_cursor *c);

static inline int ceph_pagelist_encode_64(struct ceph_pagelist *pl, u64 v)
static inline int ceph_pagelist_encode_64(struct ceph_pagelist *pl, u64 v)
{
{
	__le64 ev = cpu_to_le64(v);
	__le64 ev = cpu_to_le64(v);
+97 −9
Original line number Original line Diff line number Diff line
@@ -7,35 +7,42 @@


static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl)
static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl)
{
{
	struct page *page = list_entry(pl->head.prev, struct page,
	if (pl->mapped_tail) {
				       lru);
		struct page *page = list_entry(pl->head.prev, struct page, lru);
		kunmap(page);
		kunmap(page);
		pl->mapped_tail = NULL;
	}
}
}


int ceph_pagelist_release(struct ceph_pagelist *pl)
int ceph_pagelist_release(struct ceph_pagelist *pl)
{
{
	if (pl->mapped_tail)
	ceph_pagelist_unmap_tail(pl);
	ceph_pagelist_unmap_tail(pl);

	while (!list_empty(&pl->head)) {
	while (!list_empty(&pl->head)) {
		struct page *page = list_first_entry(&pl->head, struct page,
		struct page *page = list_first_entry(&pl->head, struct page,
						     lru);
						     lru);
		list_del(&page->lru);
		list_del(&page->lru);
		__free_page(page);
		__free_page(page);
	}
	}
	ceph_pagelist_free_reserve(pl);
	return 0;
	return 0;
}
}
EXPORT_SYMBOL(ceph_pagelist_release);
EXPORT_SYMBOL(ceph_pagelist_release);


static int ceph_pagelist_addpage(struct ceph_pagelist *pl)
static int ceph_pagelist_addpage(struct ceph_pagelist *pl)
{
{
	struct page *page = __page_cache_alloc(GFP_NOFS);
	struct page *page;

	if (!pl->num_pages_free) {
		page = __page_cache_alloc(GFP_NOFS);
	} else {
		page = list_first_entry(&pl->free_list, struct page, lru);
		list_del(&page->lru);
	}
	if (!page)
	if (!page)
		return -ENOMEM;
		return -ENOMEM;
	pl->room += PAGE_SIZE;
	pl->room += PAGE_SIZE;
	list_add_tail(&page->lru, &pl->head);
	if (pl->mapped_tail)
	ceph_pagelist_unmap_tail(pl);
	ceph_pagelist_unmap_tail(pl);
	list_add_tail(&page->lru, &pl->head);
	pl->mapped_tail = kmap(page);
	pl->mapped_tail = kmap(page);
	return 0;
	return 0;
}
}
@@ -63,3 +70,84 @@ int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len)
	return 0;
	return 0;
}
}
EXPORT_SYMBOL(ceph_pagelist_append);
EXPORT_SYMBOL(ceph_pagelist_append);

/**
 * Allocate enough pages for a pagelist to append the given amount
 * of data without without allocating.
 * Returns: 0 on success, -ENOMEM on error.
 */
int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space)
{
	if (space <= pl->room)
		return 0;
	space -= pl->room;
	space = (space + PAGE_SIZE - 1) >> PAGE_SHIFT;   /* conv to num pages */

	while (space > pl->num_pages_free) {
		struct page *page = __page_cache_alloc(GFP_NOFS);
		if (!page)
			return -ENOMEM;
		list_add_tail(&page->lru, &pl->free_list);
		++pl->num_pages_free;
	}
	return 0;
}
EXPORT_SYMBOL(ceph_pagelist_reserve);

/**
 * Free any pages that have been preallocated.
 */
int ceph_pagelist_free_reserve(struct ceph_pagelist *pl)
{
	while (!list_empty(&pl->free_list)) {
		struct page *page = list_first_entry(&pl->free_list,
						     struct page, lru);
		list_del(&page->lru);
		__free_page(page);
		--pl->num_pages_free;
	}
	BUG_ON(pl->num_pages_free);
	return 0;
}
EXPORT_SYMBOL(ceph_pagelist_free_reserve);

/**
 * Create a truncation point.
 */
void ceph_pagelist_set_cursor(struct ceph_pagelist *pl,
			      struct ceph_pagelist_cursor *c)
{
	c->pl = pl;
	c->page_lru = pl->head.prev;
	c->room = pl->room;
}
EXPORT_SYMBOL(ceph_pagelist_set_cursor);

/**
 * Truncate a pagelist to the given point. Move extra pages to reserve.
 * This won't sleep.
 * Returns: 0 on success,
 *          -EINVAL if the pagelist doesn't match the trunc point pagelist
 */
int ceph_pagelist_truncate(struct ceph_pagelist *pl,
			   struct ceph_pagelist_cursor *c)
{
	struct page *page;

	if (pl != c->pl)
		return -EINVAL;
	ceph_pagelist_unmap_tail(pl);
	while (pl->head.prev != c->page_lru) {
		page = list_entry(pl->head.prev, struct page, lru);
		list_del(&page->lru);                /* remove from pagelist */
		list_add_tail(&page->lru, &pl->free_list); /* add to reserve */
		++pl->num_pages_free;
	}
	pl->room = c->room;
	if (!list_empty(&pl->head)) {
		page = list_entry(pl->head.prev, struct page, lru);
		pl->mapped_tail = kmap(page);
	}
	return 0;
}
EXPORT_SYMBOL(ceph_pagelist_truncate);