1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/module.h>
4 #include <linux/slab.h>
5 #include <linux/pagemap.h>
6 #include <linux/highmem.h>
7 #include <linux/ceph/pagelist.h>
9 struct ceph_pagelist
*ceph_pagelist_alloc(gfp_t gfp_flags
)
11 struct ceph_pagelist
*pl
;
13 pl
= kmalloc(sizeof(*pl
), gfp_flags
);
17 INIT_LIST_HEAD(&pl
->head
);
18 pl
->mapped_tail
= NULL
;
21 INIT_LIST_HEAD(&pl
->free_list
);
22 pl
->num_pages_free
= 0;
23 refcount_set(&pl
->refcnt
, 1);
27 EXPORT_SYMBOL(ceph_pagelist_alloc
);
29 static void ceph_pagelist_unmap_tail(struct ceph_pagelist
*pl
)
31 if (pl
->mapped_tail
) {
32 struct page
*page
= list_entry(pl
->head
.prev
, struct page
, lru
);
34 pl
->mapped_tail
= NULL
;
38 void ceph_pagelist_release(struct ceph_pagelist
*pl
)
40 if (!refcount_dec_and_test(&pl
->refcnt
))
42 ceph_pagelist_unmap_tail(pl
);
43 while (!list_empty(&pl
->head
)) {
44 struct page
*page
= list_first_entry(&pl
->head
, struct page
,
49 ceph_pagelist_free_reserve(pl
);
52 EXPORT_SYMBOL(ceph_pagelist_release
);
54 static int ceph_pagelist_addpage(struct ceph_pagelist
*pl
)
58 if (!pl
->num_pages_free
) {
59 page
= __page_cache_alloc(GFP_NOFS
);
61 page
= list_first_entry(&pl
->free_list
, struct page
, lru
);
67 pl
->room
+= PAGE_SIZE
;
68 ceph_pagelist_unmap_tail(pl
);
69 list_add_tail(&page
->lru
, &pl
->head
);
70 pl
->mapped_tail
= kmap(page
);
74 int ceph_pagelist_append(struct ceph_pagelist
*pl
, const void *buf
, size_t len
)
76 while (pl
->room
< len
) {
77 size_t bit
= pl
->room
;
80 memcpy(pl
->mapped_tail
+ (pl
->length
& ~PAGE_MASK
),
86 ret
= ceph_pagelist_addpage(pl
);
91 memcpy(pl
->mapped_tail
+ (pl
->length
& ~PAGE_MASK
), buf
, len
);
96 EXPORT_SYMBOL(ceph_pagelist_append
);
98 /* Allocate enough pages for a pagelist to append the given amount
99 * of data without without allocating.
100 * Returns: 0 on success, -ENOMEM on error.
102 int ceph_pagelist_reserve(struct ceph_pagelist
*pl
, size_t space
)
104 if (space
<= pl
->room
)
107 space
= (space
+ PAGE_SIZE
- 1) >> PAGE_SHIFT
; /* conv to num pages */
109 while (space
> pl
->num_pages_free
) {
110 struct page
*page
= __page_cache_alloc(GFP_NOFS
);
113 list_add_tail(&page
->lru
, &pl
->free_list
);
114 ++pl
->num_pages_free
;
118 EXPORT_SYMBOL(ceph_pagelist_reserve
);
120 /* Free any pages that have been preallocated. */
121 int ceph_pagelist_free_reserve(struct ceph_pagelist
*pl
)
123 while (!list_empty(&pl
->free_list
)) {
124 struct page
*page
= list_first_entry(&pl
->free_list
,
126 list_del(&page
->lru
);
128 --pl
->num_pages_free
;
130 BUG_ON(pl
->num_pages_free
);
133 EXPORT_SYMBOL(ceph_pagelist_free_reserve
);
135 /* Create a truncation point. */
136 void ceph_pagelist_set_cursor(struct ceph_pagelist
*pl
,
137 struct ceph_pagelist_cursor
*c
)
140 c
->page_lru
= pl
->head
.prev
;
143 EXPORT_SYMBOL(ceph_pagelist_set_cursor
);
145 /* Truncate a pagelist to the given point. Move extra pages to reserve.
147 * Returns: 0 on success,
148 * -EINVAL if the pagelist doesn't match the trunc point pagelist
150 int ceph_pagelist_truncate(struct ceph_pagelist
*pl
,
151 struct ceph_pagelist_cursor
*c
)
157 ceph_pagelist_unmap_tail(pl
);
158 while (pl
->head
.prev
!= c
->page_lru
) {
159 page
= list_entry(pl
->head
.prev
, struct page
, lru
);
160 /* move from pagelist to reserve */
161 list_move_tail(&page
->lru
, &pl
->free_list
);
162 ++pl
->num_pages_free
;
165 if (!list_empty(&pl
->head
)) {
166 page
= list_entry(pl
->head
.prev
, struct page
, lru
);
167 pl
->mapped_tail
= kmap(page
);
171 EXPORT_SYMBOL(ceph_pagelist_truncate
);