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

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
......@@ -8,6 +8,14 @@ struct ceph_pagelist {
void *mapped_tail;
size_t length;
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)
......@@ -16,11 +24,24 @@ static inline void ceph_pagelist_init(struct ceph_pagelist *pl)
pl->mapped_tail = NULL;
pl->length = 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_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)
{
__le64 ev = cpu_to_le64(v);
......
......@@ -7,35 +7,42 @@
static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl)
{
struct page *page = list_entry(pl->head.prev, struct page,
lru);
kunmap(page);
if (pl->mapped_tail) {
struct page *page = list_entry(pl->head.prev, struct page, lru);
kunmap(page);
pl->mapped_tail = NULL;
}
}
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)) {
struct page *page = list_first_entry(&pl->head, struct page,
lru);
list_del(&page->lru);
__free_page(page);
}
ceph_pagelist_free_reserve(pl);
return 0;
}
EXPORT_SYMBOL(ceph_pagelist_release);
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)
return -ENOMEM;
pl->room += PAGE_SIZE;
ceph_pagelist_unmap_tail(pl);
list_add_tail(&page->lru, &pl->head);
if (pl->mapped_tail)
ceph_pagelist_unmap_tail(pl);
pl->mapped_tail = kmap(page);
return 0;
}
......@@ -63,3 +70,84 @@ int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len)
return 0;
}
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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment