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 static void ceph_pagelist_unmap_tail(struct ceph_pagelist
*pl
)
11 if (pl
->mapped_tail
) {
12 struct page
*page
= list_entry(pl
->head
.prev
, struct page
, lru
);
14 pl
->mapped_tail
= NULL
;
18 void ceph_pagelist_release(struct ceph_pagelist
*pl
)
20 if (!refcount_dec_and_test(&pl
->refcnt
))
22 ceph_pagelist_unmap_tail(pl
);
23 while (!list_empty(&pl
->head
)) {
24 struct page
*page
= list_first_entry(&pl
->head
, struct page
,
29 ceph_pagelist_free_reserve(pl
);
32 EXPORT_SYMBOL(ceph_pagelist_release
);
34 static int ceph_pagelist_addpage(struct ceph_pagelist
*pl
)
38 if (!pl
->num_pages_free
) {
39 page
= __page_cache_alloc(GFP_NOFS
);
41 page
= list_first_entry(&pl
->free_list
, struct page
, lru
);
47 pl
->room
+= PAGE_SIZE
;
48 ceph_pagelist_unmap_tail(pl
);
49 list_add_tail(&page
->lru
, &pl
->head
);
50 pl
->mapped_tail
= kmap(page
);
54 int ceph_pagelist_append(struct ceph_pagelist
*pl
, const void *buf
, size_t len
)
56 while (pl
->room
< len
) {
57 size_t bit
= pl
->room
;
60 memcpy(pl
->mapped_tail
+ (pl
->length
& ~PAGE_MASK
),
66 ret
= ceph_pagelist_addpage(pl
);
71 memcpy(pl
->mapped_tail
+ (pl
->length
& ~PAGE_MASK
), buf
, len
);
76 EXPORT_SYMBOL(ceph_pagelist_append
);
78 /* Allocate enough pages for a pagelist to append the given amount
79 * of data without without allocating.
80 * Returns: 0 on success, -ENOMEM on error.
82 int ceph_pagelist_reserve(struct ceph_pagelist
*pl
, size_t space
)
84 if (space
<= pl
->room
)
87 space
= (space
+ PAGE_SIZE
- 1) >> PAGE_SHIFT
; /* conv to num pages */
89 while (space
> pl
->num_pages_free
) {
90 struct page
*page
= __page_cache_alloc(GFP_NOFS
);
93 list_add_tail(&page
->lru
, &pl
->free_list
);
98 EXPORT_SYMBOL(ceph_pagelist_reserve
);
100 /* Free any pages that have been preallocated. */
101 int ceph_pagelist_free_reserve(struct ceph_pagelist
*pl
)
103 while (!list_empty(&pl
->free_list
)) {
104 struct page
*page
= list_first_entry(&pl
->free_list
,
106 list_del(&page
->lru
);
108 --pl
->num_pages_free
;
110 BUG_ON(pl
->num_pages_free
);
113 EXPORT_SYMBOL(ceph_pagelist_free_reserve
);
115 /* Create a truncation point. */
116 void ceph_pagelist_set_cursor(struct ceph_pagelist
*pl
,
117 struct ceph_pagelist_cursor
*c
)
120 c
->page_lru
= pl
->head
.prev
;
123 EXPORT_SYMBOL(ceph_pagelist_set_cursor
);
125 /* Truncate a pagelist to the given point. Move extra pages to reserve.
127 * Returns: 0 on success,
128 * -EINVAL if the pagelist doesn't match the trunc point pagelist
130 int ceph_pagelist_truncate(struct ceph_pagelist
*pl
,
131 struct ceph_pagelist_cursor
*c
)
137 ceph_pagelist_unmap_tail(pl
);
138 while (pl
->head
.prev
!= c
->page_lru
) {
139 page
= list_entry(pl
->head
.prev
, struct page
, lru
);
140 /* move from pagelist to reserve */
141 list_move_tail(&page
->lru
, &pl
->free_list
);
142 ++pl
->num_pages_free
;
145 if (!list_empty(&pl
->head
)) {
146 page
= list_entry(pl
->head
.prev
, struct page
, lru
);
147 pl
->mapped_tail
= kmap(page
);
151 EXPORT_SYMBOL(ceph_pagelist_truncate
);