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: Greg Farnum <gregf@hq.newdream.net> Signed-off-by: Sage Weil <sage@newdream.net>
This commit is contained in:
parent
18a38193ef
commit
ac0b74d8a1
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
ceph_pagelist_unmap_tail(pl);
|
||||||
list_add_tail(&page->lru, &pl->head);
|
list_add_tail(&page->lru, &pl->head);
|
||||||
if (pl->mapped_tail)
|
|
||||||
ceph_pagelist_unmap_tail(pl);
|
|
||||||
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);
|
||||||
|
|
Reference in New Issue