1 #include <linux/module.h>
3 #include <linux/pagemap.h>
4 #include <linux/highmem.h>
5 #include <linux/ceph/pagelist.h>
7 static void ceph_pagelist_unmap_tail(struct ceph_pagelist
*pl
)
10 struct page
*page
= list_entry(pl
->head
.prev
, struct page
, lru
);
12 pl
->mapped_tail
= NULL
;
16 int ceph_pagelist_release(struct ceph_pagelist
*pl
)
18 ceph_pagelist_unmap_tail(pl
);
19 while (!list_empty(&pl
->head
)) {
20 struct page
*page
= list_first_entry(&pl
->head
, struct page
,
25 ceph_pagelist_free_reserve(pl
);
28 EXPORT_SYMBOL(ceph_pagelist_release
);
30 static int ceph_pagelist_addpage(struct ceph_pagelist
*pl
)
34 if (!pl
->num_pages_free
) {
35 page
= __page_cache_alloc(GFP_NOFS
);
37 page
= list_first_entry(&pl
->free_list
, struct page
, lru
);
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
);
50 int ceph_pagelist_append(struct ceph_pagelist
*pl
, const void *buf
, size_t len
)
52 while (pl
->room
< len
) {
53 size_t bit
= pl
->room
;
56 memcpy(pl
->mapped_tail
+ (pl
->length
& ~PAGE_CACHE_MASK
),
62 ret
= ceph_pagelist_addpage(pl
);
67 memcpy(pl
->mapped_tail
+ (pl
->length
& ~PAGE_CACHE_MASK
), buf
, len
);
72 EXPORT_SYMBOL(ceph_pagelist_append
);
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.
78 int ceph_pagelist_reserve(struct ceph_pagelist
*pl
, size_t space
)
80 if (space
<= pl
->room
)
83 space
= (space
+ PAGE_SIZE
- 1) >> PAGE_SHIFT
; /* conv to num pages */
85 while (space
> pl
->num_pages_free
) {
86 struct page
*page
= __page_cache_alloc(GFP_NOFS
);
89 list_add_tail(&page
->lru
, &pl
->free_list
);
94 EXPORT_SYMBOL(ceph_pagelist_reserve
);
96 /* Free any pages that have been preallocated. */
97 int ceph_pagelist_free_reserve(struct ceph_pagelist
*pl
)
99 while (!list_empty(&pl
->free_list
)) {
100 struct page
*page
= list_first_entry(&pl
->free_list
,
102 list_del(&page
->lru
);
104 --pl
->num_pages_free
;
106 BUG_ON(pl
->num_pages_free
);
109 EXPORT_SYMBOL(ceph_pagelist_free_reserve
);
111 /* Create a truncation point. */
112 void ceph_pagelist_set_cursor(struct ceph_pagelist
*pl
,
113 struct ceph_pagelist_cursor
*c
)
116 c
->page_lru
= pl
->head
.prev
;
119 EXPORT_SYMBOL(ceph_pagelist_set_cursor
);
121 /* Truncate a pagelist to the given point. Move extra pages to reserve.
123 * Returns: 0 on success,
124 * -EINVAL if the pagelist doesn't match the trunc point pagelist
126 int ceph_pagelist_truncate(struct ceph_pagelist
*pl
,
127 struct ceph_pagelist_cursor
*c
)
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
;
141 if (!list_empty(&pl
->head
)) {
142 page
= list_entry(pl
->head
.prev
, struct page
, lru
);
143 pl
->mapped_tail
= kmap(page
);
147 EXPORT_SYMBOL(ceph_pagelist_truncate
);