Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/ceph/pagelist.c
15111 views
1
2
#include <linux/module.h>
3
#include <linux/gfp.h>
4
#include <linux/pagemap.h>
5
#include <linux/highmem.h>
6
#include <linux/ceph/pagelist.h>
7
8
static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl)
9
{
10
if (pl->mapped_tail) {
11
struct page *page = list_entry(pl->head.prev, struct page, lru);
12
kunmap(page);
13
pl->mapped_tail = NULL;
14
}
15
}
16
17
int ceph_pagelist_release(struct ceph_pagelist *pl)
18
{
19
ceph_pagelist_unmap_tail(pl);
20
while (!list_empty(&pl->head)) {
21
struct page *page = list_first_entry(&pl->head, struct page,
22
lru);
23
list_del(&page->lru);
24
__free_page(page);
25
}
26
ceph_pagelist_free_reserve(pl);
27
return 0;
28
}
29
EXPORT_SYMBOL(ceph_pagelist_release);
30
31
static int ceph_pagelist_addpage(struct ceph_pagelist *pl)
32
{
33
struct page *page;
34
35
if (!pl->num_pages_free) {
36
page = __page_cache_alloc(GFP_NOFS);
37
} else {
38
page = list_first_entry(&pl->free_list, struct page, lru);
39
list_del(&page->lru);
40
--pl->num_pages_free;
41
}
42
if (!page)
43
return -ENOMEM;
44
pl->room += PAGE_SIZE;
45
ceph_pagelist_unmap_tail(pl);
46
list_add_tail(&page->lru, &pl->head);
47
pl->mapped_tail = kmap(page);
48
return 0;
49
}
50
51
int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len)
52
{
53
while (pl->room < len) {
54
size_t bit = pl->room;
55
int ret;
56
57
memcpy(pl->mapped_tail + (pl->length & ~PAGE_CACHE_MASK),
58
buf, bit);
59
pl->length += bit;
60
pl->room -= bit;
61
buf += bit;
62
len -= bit;
63
ret = ceph_pagelist_addpage(pl);
64
if (ret)
65
return ret;
66
}
67
68
memcpy(pl->mapped_tail + (pl->length & ~PAGE_CACHE_MASK), buf, len);
69
pl->length += len;
70
pl->room -= len;
71
return 0;
72
}
73
EXPORT_SYMBOL(ceph_pagelist_append);
74
75
/**
76
* Allocate enough pages for a pagelist to append the given amount
77
* of data without without allocating.
78
* Returns: 0 on success, -ENOMEM on error.
79
*/
80
int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space)
81
{
82
if (space <= pl->room)
83
return 0;
84
space -= pl->room;
85
space = (space + PAGE_SIZE - 1) >> PAGE_SHIFT; /* conv to num pages */
86
87
while (space > pl->num_pages_free) {
88
struct page *page = __page_cache_alloc(GFP_NOFS);
89
if (!page)
90
return -ENOMEM;
91
list_add_tail(&page->lru, &pl->free_list);
92
++pl->num_pages_free;
93
}
94
return 0;
95
}
96
EXPORT_SYMBOL(ceph_pagelist_reserve);
97
98
/**
99
* Free any pages that have been preallocated.
100
*/
101
int ceph_pagelist_free_reserve(struct ceph_pagelist *pl)
102
{
103
while (!list_empty(&pl->free_list)) {
104
struct page *page = list_first_entry(&pl->free_list,
105
struct page, lru);
106
list_del(&page->lru);
107
__free_page(page);
108
--pl->num_pages_free;
109
}
110
BUG_ON(pl->num_pages_free);
111
return 0;
112
}
113
EXPORT_SYMBOL(ceph_pagelist_free_reserve);
114
115
/**
116
* Create a truncation point.
117
*/
118
void ceph_pagelist_set_cursor(struct ceph_pagelist *pl,
119
struct ceph_pagelist_cursor *c)
120
{
121
c->pl = pl;
122
c->page_lru = pl->head.prev;
123
c->room = pl->room;
124
}
125
EXPORT_SYMBOL(ceph_pagelist_set_cursor);
126
127
/**
128
* Truncate a pagelist to the given point. Move extra pages to reserve.
129
* This won't sleep.
130
* Returns: 0 on success,
131
* -EINVAL if the pagelist doesn't match the trunc point pagelist
132
*/
133
int ceph_pagelist_truncate(struct ceph_pagelist *pl,
134
struct ceph_pagelist_cursor *c)
135
{
136
struct page *page;
137
138
if (pl != c->pl)
139
return -EINVAL;
140
ceph_pagelist_unmap_tail(pl);
141
while (pl->head.prev != c->page_lru) {
142
page = list_entry(pl->head.prev, struct page, lru);
143
list_del(&page->lru); /* remove from pagelist */
144
list_add_tail(&page->lru, &pl->free_list); /* add to reserve */
145
++pl->num_pages_free;
146
}
147
pl->room = c->room;
148
if (!list_empty(&pl->head)) {
149
page = list_entry(pl->head.prev, struct page, lru);
150
pl->mapped_tail = kmap(page);
151
}
152
return 0;
153
}
154
EXPORT_SYMBOL(ceph_pagelist_truncate);
155
156