13d14c5d2SYehuda Sadeh #include <linux/module.h> 23d14c5d2SYehuda Sadeh #include <linux/gfp.h> 33d14c5d2SYehuda Sadeh #include <linux/pagemap.h> 43d14c5d2SYehuda Sadeh #include <linux/highmem.h> 53d14c5d2SYehuda Sadeh #include <linux/ceph/pagelist.h> 63d14c5d2SYehuda Sadeh 73d14c5d2SYehuda Sadeh static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl) 83d14c5d2SYehuda Sadeh { 9ac0b74d8SGreg Farnum if (pl->mapped_tail) { 10ac0b74d8SGreg Farnum struct page *page = list_entry(pl->head.prev, struct page, lru); 113d14c5d2SYehuda Sadeh kunmap(page); 12ac0b74d8SGreg Farnum pl->mapped_tail = NULL; 13ac0b74d8SGreg Farnum } 143d14c5d2SYehuda Sadeh } 153d14c5d2SYehuda Sadeh 163d14c5d2SYehuda Sadeh int ceph_pagelist_release(struct ceph_pagelist *pl) 173d14c5d2SYehuda Sadeh { 183d14c5d2SYehuda Sadeh ceph_pagelist_unmap_tail(pl); 193d14c5d2SYehuda Sadeh while (!list_empty(&pl->head)) { 203d14c5d2SYehuda Sadeh struct page *page = list_first_entry(&pl->head, struct page, 213d14c5d2SYehuda Sadeh lru); 223d14c5d2SYehuda Sadeh list_del(&page->lru); 233d14c5d2SYehuda Sadeh __free_page(page); 243d14c5d2SYehuda Sadeh } 25ac0b74d8SGreg Farnum ceph_pagelist_free_reserve(pl); 263d14c5d2SYehuda Sadeh return 0; 273d14c5d2SYehuda Sadeh } 283d14c5d2SYehuda Sadeh EXPORT_SYMBOL(ceph_pagelist_release); 293d14c5d2SYehuda Sadeh 303d14c5d2SYehuda Sadeh static int ceph_pagelist_addpage(struct ceph_pagelist *pl) 313d14c5d2SYehuda Sadeh { 32ac0b74d8SGreg Farnum struct page *page; 33ac0b74d8SGreg Farnum 34ac0b74d8SGreg Farnum if (!pl->num_pages_free) { 35ac0b74d8SGreg Farnum page = __page_cache_alloc(GFP_NOFS); 36ac0b74d8SGreg Farnum } else { 37ac0b74d8SGreg Farnum page = list_first_entry(&pl->free_list, struct page, lru); 38ac0b74d8SGreg Farnum list_del(&page->lru); 39240634e9SSage Weil --pl->num_pages_free; 40ac0b74d8SGreg Farnum } 413d14c5d2SYehuda Sadeh if (!page) 423d14c5d2SYehuda Sadeh return -ENOMEM; 433d14c5d2SYehuda Sadeh pl->room += PAGE_SIZE; 443d14c5d2SYehuda Sadeh ceph_pagelist_unmap_tail(pl); 45ac0b74d8SGreg Farnum list_add_tail(&page->lru, &pl->head); 463d14c5d2SYehuda Sadeh pl->mapped_tail = kmap(page); 473d14c5d2SYehuda Sadeh return 0; 483d14c5d2SYehuda Sadeh } 493d14c5d2SYehuda Sadeh 503d14c5d2SYehuda Sadeh int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len) 513d14c5d2SYehuda Sadeh { 523d14c5d2SYehuda Sadeh while (pl->room < len) { 533d14c5d2SYehuda Sadeh size_t bit = pl->room; 543d14c5d2SYehuda Sadeh int ret; 553d14c5d2SYehuda Sadeh 563d14c5d2SYehuda Sadeh memcpy(pl->mapped_tail + (pl->length & ~PAGE_CACHE_MASK), 573d14c5d2SYehuda Sadeh buf, bit); 583d14c5d2SYehuda Sadeh pl->length += bit; 593d14c5d2SYehuda Sadeh pl->room -= bit; 603d14c5d2SYehuda Sadeh buf += bit; 613d14c5d2SYehuda Sadeh len -= bit; 623d14c5d2SYehuda Sadeh ret = ceph_pagelist_addpage(pl); 633d14c5d2SYehuda Sadeh if (ret) 643d14c5d2SYehuda Sadeh return ret; 653d14c5d2SYehuda Sadeh } 663d14c5d2SYehuda Sadeh 673d14c5d2SYehuda Sadeh memcpy(pl->mapped_tail + (pl->length & ~PAGE_CACHE_MASK), buf, len); 683d14c5d2SYehuda Sadeh pl->length += len; 693d14c5d2SYehuda Sadeh pl->room -= len; 703d14c5d2SYehuda Sadeh return 0; 713d14c5d2SYehuda Sadeh } 723d14c5d2SYehuda Sadeh EXPORT_SYMBOL(ceph_pagelist_append); 73ac0b74d8SGreg Farnum 74ae86b9e3SBen Hutchings /* Allocate enough pages for a pagelist to append the given amount 75ac0b74d8SGreg Farnum * of data without without allocating. 76ac0b74d8SGreg Farnum * Returns: 0 on success, -ENOMEM on error. 77ac0b74d8SGreg Farnum */ 78ac0b74d8SGreg Farnum int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space) 79ac0b74d8SGreg Farnum { 80ac0b74d8SGreg Farnum if (space <= pl->room) 81ac0b74d8SGreg Farnum return 0; 82ac0b74d8SGreg Farnum space -= pl->room; 83ac0b74d8SGreg Farnum space = (space + PAGE_SIZE - 1) >> PAGE_SHIFT; /* conv to num pages */ 84ac0b74d8SGreg Farnum 85ac0b74d8SGreg Farnum while (space > pl->num_pages_free) { 86ac0b74d8SGreg Farnum struct page *page = __page_cache_alloc(GFP_NOFS); 87ac0b74d8SGreg Farnum if (!page) 88ac0b74d8SGreg Farnum return -ENOMEM; 89ac0b74d8SGreg Farnum list_add_tail(&page->lru, &pl->free_list); 90ac0b74d8SGreg Farnum ++pl->num_pages_free; 91ac0b74d8SGreg Farnum } 92ac0b74d8SGreg Farnum return 0; 93ac0b74d8SGreg Farnum } 94ac0b74d8SGreg Farnum EXPORT_SYMBOL(ceph_pagelist_reserve); 95ac0b74d8SGreg Farnum 96ae86b9e3SBen Hutchings /* Free any pages that have been preallocated. */ 97ac0b74d8SGreg Farnum int ceph_pagelist_free_reserve(struct ceph_pagelist *pl) 98ac0b74d8SGreg Farnum { 99ac0b74d8SGreg Farnum while (!list_empty(&pl->free_list)) { 100ac0b74d8SGreg Farnum struct page *page = list_first_entry(&pl->free_list, 101ac0b74d8SGreg Farnum struct page, lru); 102ac0b74d8SGreg Farnum list_del(&page->lru); 103ac0b74d8SGreg Farnum __free_page(page); 104ac0b74d8SGreg Farnum --pl->num_pages_free; 105ac0b74d8SGreg Farnum } 106ac0b74d8SGreg Farnum BUG_ON(pl->num_pages_free); 107ac0b74d8SGreg Farnum return 0; 108ac0b74d8SGreg Farnum } 109ac0b74d8SGreg Farnum EXPORT_SYMBOL(ceph_pagelist_free_reserve); 110ac0b74d8SGreg Farnum 111ae86b9e3SBen Hutchings /* Create a truncation point. */ 112ac0b74d8SGreg Farnum void ceph_pagelist_set_cursor(struct ceph_pagelist *pl, 113ac0b74d8SGreg Farnum struct ceph_pagelist_cursor *c) 114ac0b74d8SGreg Farnum { 115ac0b74d8SGreg Farnum c->pl = pl; 116ac0b74d8SGreg Farnum c->page_lru = pl->head.prev; 117ac0b74d8SGreg Farnum c->room = pl->room; 118ac0b74d8SGreg Farnum } 119ac0b74d8SGreg Farnum EXPORT_SYMBOL(ceph_pagelist_set_cursor); 120ac0b74d8SGreg Farnum 121ae86b9e3SBen Hutchings /* Truncate a pagelist to the given point. Move extra pages to reserve. 122ac0b74d8SGreg Farnum * This won't sleep. 123ac0b74d8SGreg Farnum * Returns: 0 on success, 124ac0b74d8SGreg Farnum * -EINVAL if the pagelist doesn't match the trunc point pagelist 125ac0b74d8SGreg Farnum */ 126ac0b74d8SGreg Farnum int ceph_pagelist_truncate(struct ceph_pagelist *pl, 127ac0b74d8SGreg Farnum struct ceph_pagelist_cursor *c) 128ac0b74d8SGreg Farnum { 129ac0b74d8SGreg Farnum struct page *page; 130ac0b74d8SGreg Farnum 131ac0b74d8SGreg Farnum if (pl != c->pl) 132ac0b74d8SGreg Farnum return -EINVAL; 133ac0b74d8SGreg Farnum ceph_pagelist_unmap_tail(pl); 134ac0b74d8SGreg Farnum while (pl->head.prev != c->page_lru) { 135ac0b74d8SGreg Farnum page = list_entry(pl->head.prev, struct page, lru); 136*cc4829e5SWei Yongjun /* move from pagelist to reserve */ 137*cc4829e5SWei Yongjun list_move_tail(&page->lru, &pl->free_list); 138ac0b74d8SGreg Farnum ++pl->num_pages_free; 139ac0b74d8SGreg Farnum } 140ac0b74d8SGreg Farnum pl->room = c->room; 141ac0b74d8SGreg Farnum if (!list_empty(&pl->head)) { 142ac0b74d8SGreg Farnum page = list_entry(pl->head.prev, struct page, lru); 143ac0b74d8SGreg Farnum pl->mapped_tail = kmap(page); 144ac0b74d8SGreg Farnum } 145ac0b74d8SGreg Farnum return 0; 146ac0b74d8SGreg Farnum } 147ac0b74d8SGreg Farnum EXPORT_SYMBOL(ceph_pagelist_truncate); 148