Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pagelist.c
Go to the documentation of this file.
1 #include <linux/module.h>
2 #include <linux/gfp.h>
3 #include <linux/pagemap.h>
4 #include <linux/highmem.h>
5 #include <linux/ceph/pagelist.h>
6 
7 static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl)
8 {
9  if (pl->mapped_tail) {
10  struct page *page = list_entry(pl->head.prev, struct page, lru);
11  kunmap(page);
12  pl->mapped_tail = NULL;
13  }
14 }
15 
17 {
18  ceph_pagelist_unmap_tail(pl);
19  while (!list_empty(&pl->head)) {
20  struct page *page = list_first_entry(&pl->head, struct page,
21  lru);
22  list_del(&page->lru);
23  __free_page(page);
24  }
26  return 0;
27 }
29 
30 static int ceph_pagelist_addpage(struct ceph_pagelist *pl)
31 {
32  struct page *page;
33 
34  if (!pl->num_pages_free) {
35  page = __page_cache_alloc(GFP_NOFS);
36  } else {
37  page = list_first_entry(&pl->free_list, struct page, lru);
38  list_del(&page->lru);
39  --pl->num_pages_free;
40  }
41  if (!page)
42  return -ENOMEM;
43  pl->room += PAGE_SIZE;
44  ceph_pagelist_unmap_tail(pl);
45  list_add_tail(&page->lru, &pl->head);
46  pl->mapped_tail = kmap(page);
47  return 0;
48 }
49 
50 int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len)
51 {
52  while (pl->room < len) {
53  size_t bit = pl->room;
54  int ret;
55 
57  buf, bit);
58  pl->length += bit;
59  pl->room -= bit;
60  buf += bit;
61  len -= bit;
62  ret = ceph_pagelist_addpage(pl);
63  if (ret)
64  return ret;
65  }
66 
67  memcpy(pl->mapped_tail + (pl->length & ~PAGE_CACHE_MASK), buf, len);
68  pl->length += len;
69  pl->room -= len;
70  return 0;
71 }
73 
74 /* Allocate enough pages for a pagelist to append the given amount
75  * of data without without allocating.
76  * Returns: 0 on success, -ENOMEM on error.
77  */
78 int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space)
79 {
80  if (space <= pl->room)
81  return 0;
82  space -= pl->room;
83  space = (space + PAGE_SIZE - 1) >> PAGE_SHIFT; /* conv to num pages */
84 
85  while (space > pl->num_pages_free) {
86  struct page *page = __page_cache_alloc(GFP_NOFS);
87  if (!page)
88  return -ENOMEM;
89  list_add_tail(&page->lru, &pl->free_list);
90  ++pl->num_pages_free;
91  }
92  return 0;
93 }
95 
96 /* Free any pages that have been preallocated. */
98 {
99  while (!list_empty(&pl->free_list)) {
100  struct page *page = list_first_entry(&pl->free_list,
101  struct page, lru);
102  list_del(&page->lru);
103  __free_page(page);
104  --pl->num_pages_free;
105  }
106  BUG_ON(pl->num_pages_free);
107  return 0;
108 }
110 
111 /* Create a truncation point. */
113  struct ceph_pagelist_cursor *c)
114 {
115  c->pl = pl;
116  c->page_lru = pl->head.prev;
117  c->room = pl->room;
118 }
120 
121 /* Truncate a pagelist to the given point. Move extra pages to reserve.
122  * This won't sleep.
123  * Returns: 0 on success,
124  * -EINVAL if the pagelist doesn't match the trunc point pagelist
125  */
127  struct ceph_pagelist_cursor *c)
128 {
129  struct page *page;
130 
131  if (pl != c->pl)
132  return -EINVAL;
133  ceph_pagelist_unmap_tail(pl);
134  while (pl->head.prev != c->page_lru) {
135  page = list_entry(pl->head.prev, struct page, lru);
136  /* move from pagelist to reserve */
137  list_move_tail(&page->lru, &pl->free_list);
138  ++pl->num_pages_free;
139  }
140  pl->room = c->room;
141  if (!list_empty(&pl->head)) {
142  page = list_entry(pl->head.prev, struct page, lru);
143  pl->mapped_tail = kmap(page);
144  }
145  return 0;
146 }