Blame view

net/ceph/pagelist.c 3.65 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
3d14c5d2b   Yehuda Sadeh   ceph: factor out ...
2
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
3
  #include <linux/gfp.h>
e4339d28f   Yan, Zheng   libceph: referenc...
4
  #include <linux/slab.h>
58bb3b374   Sage Weil   ceph: support cep...
5
6
  #include <linux/pagemap.h>
  #include <linux/highmem.h>
3d14c5d2b   Yehuda Sadeh   ceph: factor out ...
7
  #include <linux/ceph/pagelist.h>
58bb3b374   Sage Weil   ceph: support cep...
8

3d4401d9d   Yehuda Sadeh   ceph: fix pagelis...
9
10
  static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl)
  {
ac0b74d8a   Greg Farnum   ceph: add pagelis...
11
12
13
14
15
  	if (pl->mapped_tail) {
  		struct page *page = list_entry(pl->head.prev, struct page, lru);
  		kunmap(page);
  		pl->mapped_tail = NULL;
  	}
3d4401d9d   Yehuda Sadeh   ceph: fix pagelis...
16
  }
e4339d28f   Yan, Zheng   libceph: referenc...
17
  void ceph_pagelist_release(struct ceph_pagelist *pl)
58bb3b374   Sage Weil   ceph: support cep...
18
  {
0e1a5ee65   Elena Reshetova   libceph: convert ...
19
  	if (!refcount_dec_and_test(&pl->refcnt))
e4339d28f   Yan, Zheng   libceph: referenc...
20
  		return;
ac0b74d8a   Greg Farnum   ceph: add pagelis...
21
  	ceph_pagelist_unmap_tail(pl);
58bb3b374   Sage Weil   ceph: support cep...
22
23
24
25
26
27
  	while (!list_empty(&pl->head)) {
  		struct page *page = list_first_entry(&pl->head, struct page,
  						     lru);
  		list_del(&page->lru);
  		__free_page(page);
  	}
ac0b74d8a   Greg Farnum   ceph: add pagelis...
28
  	ceph_pagelist_free_reserve(pl);
e4339d28f   Yan, Zheng   libceph: referenc...
29
  	kfree(pl);
58bb3b374   Sage Weil   ceph: support cep...
30
  }
3d14c5d2b   Yehuda Sadeh   ceph: factor out ...
31
  EXPORT_SYMBOL(ceph_pagelist_release);
58bb3b374   Sage Weil   ceph: support cep...
32
33
34
  
  static int ceph_pagelist_addpage(struct ceph_pagelist *pl)
  {
ac0b74d8a   Greg Farnum   ceph: add pagelis...
35
36
37
38
39
40
41
  	struct page *page;
  
  	if (!pl->num_pages_free) {
  		page = __page_cache_alloc(GFP_NOFS);
  	} else {
  		page = list_first_entry(&pl->free_list, struct page, lru);
  		list_del(&page->lru);
240634e9b   Sage Weil   ceph: fix num_pag...
42
  		--pl->num_pages_free;
ac0b74d8a   Greg Farnum   ceph: add pagelis...
43
  	}
58bb3b374   Sage Weil   ceph: support cep...
44
45
46
  	if (!page)
  		return -ENOMEM;
  	pl->room += PAGE_SIZE;
ac0b74d8a   Greg Farnum   ceph: add pagelis...
47
  	ceph_pagelist_unmap_tail(pl);
58bb3b374   Sage Weil   ceph: support cep...
48
  	list_add_tail(&page->lru, &pl->head);
58bb3b374   Sage Weil   ceph: support cep...
49
50
51
  	pl->mapped_tail = kmap(page);
  	return 0;
  }
68b4476b0   Yehuda Sadeh   ceph: messenger a...
52
  int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len)
58bb3b374   Sage Weil   ceph: support cep...
53
54
55
56
  {
  	while (pl->room < len) {
  		size_t bit = pl->room;
  		int ret;
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
57
  		memcpy(pl->mapped_tail + (pl->length & ~PAGE_MASK),
58bb3b374   Sage Weil   ceph: support cep...
58
59
60
61
62
63
64
65
66
  		       buf, bit);
  		pl->length += bit;
  		pl->room -= bit;
  		buf += bit;
  		len -= bit;
  		ret = ceph_pagelist_addpage(pl);
  		if (ret)
  			return ret;
  	}
09cbfeaf1   Kirill A. Shutemov   mm, fs: get rid o...
67
  	memcpy(pl->mapped_tail + (pl->length & ~PAGE_MASK), buf, len);
58bb3b374   Sage Weil   ceph: support cep...
68
69
70
71
  	pl->length += len;
  	pl->room -= len;
  	return 0;
  }
3d14c5d2b   Yehuda Sadeh   ceph: factor out ...
72
  EXPORT_SYMBOL(ceph_pagelist_append);
ac0b74d8a   Greg Farnum   ceph: add pagelis...
73

ae86b9e38   Ben Hutchings   net: Fix non-kern...
74
  /* Allocate enough pages for a pagelist to append the given amount
ac0b74d8a   Greg Farnum   ceph: add pagelis...
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
   * of data without without allocating.
   * Returns: 0 on success, -ENOMEM on error.
   */
  int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space)
  {
  	if (space <= pl->room)
  		return 0;
  	space -= pl->room;
  	space = (space + PAGE_SIZE - 1) >> PAGE_SHIFT;   /* conv to num pages */
  
  	while (space > pl->num_pages_free) {
  		struct page *page = __page_cache_alloc(GFP_NOFS);
  		if (!page)
  			return -ENOMEM;
  		list_add_tail(&page->lru, &pl->free_list);
  		++pl->num_pages_free;
  	}
  	return 0;
  }
  EXPORT_SYMBOL(ceph_pagelist_reserve);
ae86b9e38   Ben Hutchings   net: Fix non-kern...
95
  /* Free any pages that have been preallocated. */
ac0b74d8a   Greg Farnum   ceph: add pagelis...
96
97
98
99
100
101
102
103
104
105
106
107
108
  int ceph_pagelist_free_reserve(struct ceph_pagelist *pl)
  {
  	while (!list_empty(&pl->free_list)) {
  		struct page *page = list_first_entry(&pl->free_list,
  						     struct page, lru);
  		list_del(&page->lru);
  		__free_page(page);
  		--pl->num_pages_free;
  	}
  	BUG_ON(pl->num_pages_free);
  	return 0;
  }
  EXPORT_SYMBOL(ceph_pagelist_free_reserve);
ae86b9e38   Ben Hutchings   net: Fix non-kern...
109
  /* Create a truncation point. */
ac0b74d8a   Greg Farnum   ceph: add pagelis...
110
111
112
113
114
115
116
117
  void ceph_pagelist_set_cursor(struct ceph_pagelist *pl,
  			      struct ceph_pagelist_cursor *c)
  {
  	c->pl = pl;
  	c->page_lru = pl->head.prev;
  	c->room = pl->room;
  }
  EXPORT_SYMBOL(ceph_pagelist_set_cursor);
ae86b9e38   Ben Hutchings   net: Fix non-kern...
118
  /* Truncate a pagelist to the given point. Move extra pages to reserve.
ac0b74d8a   Greg Farnum   ceph: add pagelis...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
   * This won't sleep.
   * Returns: 0 on success,
   *          -EINVAL if the pagelist doesn't match the trunc point pagelist
   */
  int ceph_pagelist_truncate(struct ceph_pagelist *pl,
  			   struct ceph_pagelist_cursor *c)
  {
  	struct page *page;
  
  	if (pl != c->pl)
  		return -EINVAL;
  	ceph_pagelist_unmap_tail(pl);
  	while (pl->head.prev != c->page_lru) {
  		page = list_entry(pl->head.prev, struct page, lru);
cc4829e59   Wei Yongjun   ceph: use list_mo...
133
134
  		/* move from pagelist to reserve */
  		list_move_tail(&page->lru, &pl->free_list);
ac0b74d8a   Greg Farnum   ceph: add pagelis...
135
136
137
138
139
140
141
142
143
144
  		++pl->num_pages_free;
  	}
  	pl->room = c->room;
  	if (!list_empty(&pl->head)) {
  		page = list_entry(pl->head.prev, struct page, lru);
  		pl->mapped_tail = kmap(page);
  	}
  	return 0;
  }
  EXPORT_SYMBOL(ceph_pagelist_truncate);