0001
0002 #include <linux/module.h>
0003 #include <linux/gfp.h>
0004 #include <linux/slab.h>
0005 #include <linux/pagemap.h>
0006 #include <linux/highmem.h>
0007 #include <linux/ceph/pagelist.h>
0008
0009 struct ceph_pagelist *ceph_pagelist_alloc(gfp_t gfp_flags)
0010 {
0011 struct ceph_pagelist *pl;
0012
0013 pl = kmalloc(sizeof(*pl), gfp_flags);
0014 if (!pl)
0015 return NULL;
0016
0017 INIT_LIST_HEAD(&pl->head);
0018 pl->mapped_tail = NULL;
0019 pl->length = 0;
0020 pl->room = 0;
0021 INIT_LIST_HEAD(&pl->free_list);
0022 pl->num_pages_free = 0;
0023 refcount_set(&pl->refcnt, 1);
0024
0025 return pl;
0026 }
0027 EXPORT_SYMBOL(ceph_pagelist_alloc);
0028
0029 static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl)
0030 {
0031 if (pl->mapped_tail) {
0032 struct page *page = list_entry(pl->head.prev, struct page, lru);
0033 kunmap(page);
0034 pl->mapped_tail = NULL;
0035 }
0036 }
0037
0038 void ceph_pagelist_release(struct ceph_pagelist *pl)
0039 {
0040 if (!refcount_dec_and_test(&pl->refcnt))
0041 return;
0042 ceph_pagelist_unmap_tail(pl);
0043 while (!list_empty(&pl->head)) {
0044 struct page *page = list_first_entry(&pl->head, struct page,
0045 lru);
0046 list_del(&page->lru);
0047 __free_page(page);
0048 }
0049 ceph_pagelist_free_reserve(pl);
0050 kfree(pl);
0051 }
0052 EXPORT_SYMBOL(ceph_pagelist_release);
0053
0054 static int ceph_pagelist_addpage(struct ceph_pagelist *pl)
0055 {
0056 struct page *page;
0057
0058 if (!pl->num_pages_free) {
0059 page = __page_cache_alloc(GFP_NOFS);
0060 } else {
0061 page = list_first_entry(&pl->free_list, struct page, lru);
0062 list_del(&page->lru);
0063 --pl->num_pages_free;
0064 }
0065 if (!page)
0066 return -ENOMEM;
0067 pl->room += PAGE_SIZE;
0068 ceph_pagelist_unmap_tail(pl);
0069 list_add_tail(&page->lru, &pl->head);
0070 pl->mapped_tail = kmap(page);
0071 return 0;
0072 }
0073
0074 int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len)
0075 {
0076 while (pl->room < len) {
0077 size_t bit = pl->room;
0078 int ret;
0079
0080 memcpy(pl->mapped_tail + (pl->length & ~PAGE_MASK),
0081 buf, bit);
0082 pl->length += bit;
0083 pl->room -= bit;
0084 buf += bit;
0085 len -= bit;
0086 ret = ceph_pagelist_addpage(pl);
0087 if (ret)
0088 return ret;
0089 }
0090
0091 memcpy(pl->mapped_tail + (pl->length & ~PAGE_MASK), buf, len);
0092 pl->length += len;
0093 pl->room -= len;
0094 return 0;
0095 }
0096 EXPORT_SYMBOL(ceph_pagelist_append);
0097
0098
0099
0100
0101
0102 int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space)
0103 {
0104 if (space <= pl->room)
0105 return 0;
0106 space -= pl->room;
0107 space = (space + PAGE_SIZE - 1) >> PAGE_SHIFT;
0108
0109 while (space > pl->num_pages_free) {
0110 struct page *page = __page_cache_alloc(GFP_NOFS);
0111 if (!page)
0112 return -ENOMEM;
0113 list_add_tail(&page->lru, &pl->free_list);
0114 ++pl->num_pages_free;
0115 }
0116 return 0;
0117 }
0118 EXPORT_SYMBOL(ceph_pagelist_reserve);
0119
0120
0121 int ceph_pagelist_free_reserve(struct ceph_pagelist *pl)
0122 {
0123 while (!list_empty(&pl->free_list)) {
0124 struct page *page = list_first_entry(&pl->free_list,
0125 struct page, lru);
0126 list_del(&page->lru);
0127 __free_page(page);
0128 --pl->num_pages_free;
0129 }
0130 BUG_ON(pl->num_pages_free);
0131 return 0;
0132 }
0133 EXPORT_SYMBOL(ceph_pagelist_free_reserve);
0134
0135
0136 void ceph_pagelist_set_cursor(struct ceph_pagelist *pl,
0137 struct ceph_pagelist_cursor *c)
0138 {
0139 c->pl = pl;
0140 c->page_lru = pl->head.prev;
0141 c->room = pl->room;
0142 }
0143 EXPORT_SYMBOL(ceph_pagelist_set_cursor);
0144
0145
0146
0147
0148
0149
0150 int ceph_pagelist_truncate(struct ceph_pagelist *pl,
0151 struct ceph_pagelist_cursor *c)
0152 {
0153 struct page *page;
0154
0155 if (pl != c->pl)
0156 return -EINVAL;
0157 ceph_pagelist_unmap_tail(pl);
0158 while (pl->head.prev != c->page_lru) {
0159 page = list_entry(pl->head.prev, struct page, lru);
0160
0161 list_move_tail(&page->lru, &pl->free_list);
0162 ++pl->num_pages_free;
0163 }
0164 pl->room = c->room;
0165 if (!list_empty(&pl->head)) {
0166 page = list_entry(pl->head.prev, struct page, lru);
0167 pl->mapped_tail = kmap(page);
0168 }
0169 return 0;
0170 }
0171 EXPORT_SYMBOL(ceph_pagelist_truncate);