Branch data Line data Source code
1 : : #include <unistd.h>
2 : : #include <fcntl.h>
3 : :
4 : : #undef LOG_PREFIX
5 : : #define LOG_PREFIX "page-pipe: "
6 : :
7 : : #include "config.h"
8 : : #include "util.h"
9 : : #include "page-pipe.h"
10 : :
11 : 4938 : static int page_pipe_grow(struct page_pipe *pp)
12 : : {
13 : : struct page_pipe_buf *ppb;
14 : :
15 : 4938 : pr_debug("Will grow page pipe (iov off is %u)\n", pp->free_iov);
16 : :
17 [ - + ]: 4938 : if (!list_empty(&pp->free_bufs)) {
18 : 0 : ppb = list_first_entry(&pp->free_bufs, struct page_pipe_buf, l);
19 : 0 : list_move_tail(&ppb->l, &pp->bufs);
20 : : goto out;
21 : : }
22 : :
23 [ + + ][ + - ]: 4938 : if (pp->chunk_mode && pp->nr_pipes == NR_PIPES_PER_CHUNK)
24 : : return -EAGAIN;
25 : :
26 [ - + ]: 4938 : ppb = xmalloc(sizeof(*ppb));
27 [ + - ]: 4938 : if (!ppb)
28 : : return -1;
29 : :
30 [ - + ]: 4938 : if (pipe(ppb->p)) {
31 [ # # ]: 0 : xfree(ppb);
32 : 0 : pr_perror("Can't make pipe for page-pipe");
33 : 0 : return -1;
34 : : }
35 : :
36 : 4938 : ppb->pipe_size = fcntl(ppb->p[0], F_GETPIPE_SZ, 0) / PAGE_SIZE;
37 : :
38 : 4938 : list_add_tail(&ppb->l, &pp->bufs);
39 : : out:
40 : 4938 : ppb->pages_in = 0;
41 : 4938 : ppb->nr_segs = 0;
42 : 4938 : ppb->iov = &pp->iovs[pp->free_iov];
43 : :
44 : 4938 : pp->nr_pipes++;
45 : :
46 : 4938 : return 0;
47 : : }
48 : :
49 : 3706 : struct page_pipe *create_page_pipe(unsigned int nr_segs,
50 : : struct iovec *iovs, bool chunk_mode)
51 : : {
52 : : struct page_pipe *pp;
53 : :
54 : 3706 : pr_debug("Create page pipe for %u segs\n", nr_segs);
55 : :
56 [ - + ]: 3706 : pp = xmalloc(sizeof(*pp));
57 [ + - ]: 3706 : if (pp) {
58 : 3706 : pp->nr_pipes = 0;
59 : 3706 : INIT_LIST_HEAD(&pp->bufs);
60 : 3706 : INIT_LIST_HEAD(&pp->free_bufs);
61 : 3706 : pp->nr_iovs = nr_segs;
62 : 3706 : pp->iovs = iovs;
63 : 3706 : pp->free_iov = 0;
64 : :
65 : 3706 : pp->nr_holes = 0;
66 : 3706 : pp->free_hole = 0;
67 : 3706 : pp->holes = NULL;
68 : :
69 : 3706 : pp->chunk_mode = chunk_mode;
70 : :
71 [ + - ]: 3706 : if (page_pipe_grow(pp))
72 : : return NULL;
73 : : }
74 : :
75 : 3706 : return pp;
76 : : }
77 : :
78 : 3706 : void destroy_page_pipe(struct page_pipe *pp)
79 : : {
80 : : struct page_pipe_buf *ppb, *n;
81 : :
82 : 3706 : pr_debug("Killing page pipe\n");
83 : :
84 : 3706 : list_splice(&pp->free_bufs, &pp->bufs);
85 [ + + ]: 8644 : list_for_each_entry_safe(ppb, n, &pp->bufs, l) {
86 : 4938 : close(ppb->p[0]);
87 : 4938 : close(ppb->p[1]);
88 [ + - ]: 4938 : xfree(ppb);
89 : : }
90 : :
91 [ + - ]: 3706 : xfree(pp);
92 : 3706 : }
93 : :
94 : 0 : void page_pipe_reinit(struct page_pipe *pp)
95 : : {
96 : : struct page_pipe_buf *ppb, *n;
97 : :
98 [ # # ]: 0 : BUG_ON(!pp->chunk_mode);
99 : :
100 : 0 : pr_debug("Clean up page pipe\n");
101 : :
102 [ # # ]: 0 : list_for_each_entry_safe(ppb, n, &pp->bufs, l)
103 : 0 : list_move(&ppb->l, &pp->free_bufs);
104 : :
105 : 0 : pp->free_hole = 0;
106 : :
107 [ # # ]: 0 : if (page_pipe_grow(pp))
108 : 0 : BUG(); /* It can't fail, because ppb is in free_bufs */
109 : 0 : }
110 : :
111 : 1383968 : static inline int try_add_page_to(struct page_pipe *pp, struct page_pipe_buf *ppb,
112 : : unsigned long addr)
113 : : {
114 : : struct iovec *iov;
115 : :
116 [ + + ]: 691984 : if (ppb->pages_in == ppb->pipe_size) {
117 : 8885 : unsigned long new_size = ppb->pipe_size << 1;
118 : : int ret;
119 : :
120 [ + + ]: 8885 : if (new_size > PIPE_MAX_SIZE)
121 : : return 1;
122 : :
123 : 7653 : ret = fcntl(ppb->p[0], F_SETPIPE_SZ, new_size * PAGE_SIZE);
124 [ + - ]: 7653 : if (ret < 0)
125 : : return 1; /* need to add another buf */
126 : :
127 : 7653 : ret /= PAGE_SIZE;
128 [ - + ]: 7653 : BUG_ON(ret < ppb->pipe_size);
129 : :
130 : 7653 : pr_debug("Grow pipe %x -> %x\n", ppb->pipe_size, ret);
131 : 7653 : ppb->pipe_size = ret;
132 : : }
133 : :
134 [ + + ]: 690752 : if (ppb->nr_segs) {
135 : : /* can existing iov accumulate the page? */
136 : 685830 : iov = &ppb->iov[ppb->nr_segs - 1];
137 [ + + ]: 685830 : if ((unsigned long)iov->iov_base + iov->iov_len == addr) {
138 : 567816 : iov->iov_len += PAGE_SIZE;
139 : : goto out;
140 : : }
141 : :
142 [ + - ]: 118014 : if (ppb->nr_segs == UIO_MAXIOV)
143 : : /* XXX -- shrink pipe back? */
144 : : return 1;
145 : : }
146 : :
147 : 122936 : pr_debug("Add iov to page pipe (%u iovs, %u/%u total)\n",
148 : : ppb->nr_segs, pp->free_iov, pp->nr_iovs);
149 : 122936 : ppb->iov[ppb->nr_segs].iov_base = (void *)addr;
150 : 122936 : ppb->iov[ppb->nr_segs].iov_len = PAGE_SIZE;
151 : 122936 : ppb->nr_segs++;
152 : 122936 : pp->free_iov++;
153 [ - + ]: 122936 : BUG_ON(pp->free_iov > pp->nr_iovs);
154 : : out:
155 : 690752 : ppb->pages_in++;
156 : : return 0;
157 : : }
158 : :
159 : 691984 : static inline int try_add_page(struct page_pipe *pp, unsigned long addr)
160 : : {
161 [ - + ]: 691984 : BUG_ON(list_empty(&pp->bufs));
162 : 691984 : return try_add_page_to(pp, list_entry(pp->bufs.prev, struct page_pipe_buf, l), addr);
163 : : }
164 : :
165 : 690752 : int page_pipe_add_page(struct page_pipe *pp, unsigned long addr)
166 : : {
167 : : int ret;
168 : :
169 : 690752 : ret = try_add_page(pp, addr);
170 [ + + ]: 690752 : if (ret <= 0)
171 : : return ret;
172 : :
173 : 1232 : ret = page_pipe_grow(pp);
174 [ + - ]: 1232 : if (ret < 0)
175 : : return ret;
176 : :
177 : 1232 : ret = try_add_page(pp, addr);
178 [ - + ]: 1232 : BUG_ON(ret > 0);
179 : 1232 : return ret;
180 : : }
181 : :
182 : : #define PP_HOLES_BATCH 32
183 : :
184 : 1658317 : int page_pipe_add_hole(struct page_pipe *pp, unsigned long addr)
185 : : {
186 : : struct iovec *iov;
187 : :
188 [ + + ]: 1658317 : if (pp->free_hole >= pp->nr_holes) {
189 [ - + ]: 2872 : pp->holes = xrealloc(pp->holes,
190 : : (pp->nr_holes + PP_HOLES_BATCH) * sizeof(struct iovec));
191 [ + - ]: 2872 : if (!pp->holes)
192 : : return -1;
193 : :
194 : 2872 : pp->nr_holes += PP_HOLES_BATCH;
195 : : }
196 : :
197 [ + + ]: 1658317 : if (pp->free_hole) {
198 : 1655577 : iov = &pp->holes[pp->free_hole - 1];
199 [ + + ]: 1655577 : if ((unsigned long)iov->iov_base + iov->iov_len == addr) {
200 : 1627586 : iov->iov_len += PAGE_SIZE;
201 : 1627586 : goto out;
202 : : }
203 : : }
204 : :
205 : 30731 : iov = &pp->holes[pp->free_hole];
206 : 30731 : iov->iov_base = (void *)addr;
207 : 30731 : iov->iov_len = PAGE_SIZE;
208 : 30731 : pp->free_hole++;
209 : : out:
210 : : return 0;
211 : : }
212 : :
213 : 3664 : void debug_show_page_pipe(struct page_pipe *pp)
214 : : {
215 : : struct page_pipe_buf *ppb;
216 : : int i;
217 : : struct iovec *iov;
218 : :
219 [ + - ]: 3664 : if (log_get_loglevel() < LOG_DEBUG)
220 : 3664 : return;
221 : :
222 : 3664 : pr_debug("Page pipe:\n");
223 : 3664 : pr_debug("* %u pipes %u/%u iovs:\n",
224 : : pp->nr_pipes, pp->free_iov, pp->nr_iovs);
225 [ + + ]: 8560 : list_for_each_entry(ppb, &pp->bufs, l) {
226 : 4896 : pr_debug("\tbuf %u pages, %u iovs:\n",
227 : : ppb->pages_in, ppb->nr_segs);
228 [ + + ]: 127794 : for (i = 0; i < ppb->nr_segs; i++) {
229 : 122898 : iov = &ppb->iov[i];
230 : 122898 : pr_debug("\t\t%p %lu\n", iov->iov_base, iov->iov_len / PAGE_SIZE);
231 : : }
232 : : }
233 : :
234 : 3664 : pr_debug("* %u holes:\n", pp->free_hole);
235 [ + + ]: 34395 : for (i = 0; i < pp->free_hole; i++) {
236 : 30731 : iov = &pp->holes[i];
237 : 30731 : pr_debug("\t%p %lu\n", iov->iov_base, iov->iov_len / PAGE_SIZE);
238 : : }
239 : : }
|