1 | /* $NetBSD: dtv_scatter.c,v 1.2 2014/08/09 13:34:10 jmcneill Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2008 Patrick Mahoney <pat@polycrystal.org> |
5 | * All rights reserved. |
6 | * |
7 | * This code was written by Patrick Mahoney (pat@polycrystal.org) as |
8 | * part of Google Summer of Code 2008. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: dtv_scatter.c,v 1.2 2014/08/09 13:34:10 jmcneill Exp $" ); |
34 | |
35 | #include <sys/param.h> |
36 | #include <sys/ioctl.h> |
37 | #include <sys/fcntl.h> |
38 | #include <sys/vnode.h> |
39 | #include <sys/poll.h> |
40 | #include <sys/select.h> |
41 | #include <sys/kmem.h> |
42 | #include <sys/pool.h> |
43 | #include <sys/conf.h> |
44 | #include <sys/types.h> |
45 | #include <sys/device.h> |
46 | #include <sys/condvar.h> |
47 | #include <sys/queue.h> |
48 | |
49 | #include <dev/dtv/dtvvar.h> |
50 | |
51 | void |
52 | dtv_scatter_buf_init(struct dtv_scatter_buf *sb) |
53 | { |
54 | sb->sb_pool = pool_cache_init(PAGE_SIZE, 0, 0, 0, |
55 | "dtvscatter" , NULL, IPL_SCHED, |
56 | NULL, NULL, NULL); |
57 | sb->sb_size = 0; |
58 | sb->sb_npages = 0; |
59 | sb->sb_page_ary = NULL; |
60 | } |
61 | |
62 | void |
63 | dtv_scatter_buf_destroy(struct dtv_scatter_buf *sb) |
64 | { |
65 | /* Do we need to return everything to the pool first? */ |
66 | dtv_scatter_buf_set_size(sb, 0); |
67 | pool_cache_destroy(sb->sb_pool); |
68 | sb->sb_pool = 0; |
69 | sb->sb_npages = 0; |
70 | sb->sb_page_ary = NULL; |
71 | } |
72 | |
73 | /* Increase or decrease the size of the buffer */ |
74 | int |
75 | dtv_scatter_buf_set_size(struct dtv_scatter_buf *sb, size_t sz) |
76 | { |
77 | unsigned int i; |
78 | size_t npages, minpages, oldnpages; |
79 | uint8_t **old_ary; |
80 | |
81 | npages = (sz >> PAGE_SHIFT) + ((sz & PAGE_MASK) > 0); |
82 | |
83 | if (sb->sb_npages == npages) { |
84 | return 0; |
85 | } |
86 | |
87 | oldnpages = sb->sb_npages; |
88 | old_ary = sb->sb_page_ary; |
89 | |
90 | sb->sb_npages = npages; |
91 | if (npages > 0) { |
92 | sb->sb_page_ary = |
93 | kmem_alloc(sizeof(uint8_t *) * npages, KM_SLEEP); |
94 | if (sb->sb_page_ary == NULL) { |
95 | sb->sb_npages = oldnpages; |
96 | sb->sb_page_ary = old_ary; |
97 | return ENOMEM; |
98 | } |
99 | } else { |
100 | sb->sb_page_ary = NULL; |
101 | } |
102 | |
103 | minpages = min(npages, oldnpages); |
104 | /* copy any pages that will be reused */ |
105 | for (i = 0; i < minpages; ++i) |
106 | sb->sb_page_ary[i] = old_ary[i]; |
107 | /* allocate any new pages */ |
108 | for (; i < npages; ++i) { |
109 | sb->sb_page_ary[i] = pool_cache_get(sb->sb_pool, 0); |
110 | /* TODO: does pool_cache_get return NULL on |
111 | * ENOMEM? If so, we need to release or note |
112 | * the pages with did allocate |
113 | * successfully. */ |
114 | if (sb->sb_page_ary[i] == NULL) { |
115 | return ENOMEM; |
116 | } |
117 | } |
118 | /* return any pages no longer needed */ |
119 | for (; i < oldnpages; ++i) |
120 | pool_cache_put(sb->sb_pool, old_ary[i]); |
121 | |
122 | if (old_ary != NULL) |
123 | kmem_free(old_ary, sizeof(uint8_t *) * oldnpages); |
124 | |
125 | sb->sb_size = sb->sb_npages << PAGE_SHIFT; |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | |
131 | paddr_t |
132 | dtv_scatter_buf_map(struct dtv_scatter_buf *sb, off_t off) |
133 | { |
134 | size_t pg; |
135 | paddr_t pa; |
136 | |
137 | pg = off >> PAGE_SHIFT; |
138 | |
139 | if (pg >= sb->sb_npages) |
140 | return -1; |
141 | else if (!pmap_extract(pmap_kernel(), (vaddr_t)sb->sb_page_ary[pg], &pa)) |
142 | return -1; |
143 | |
144 | return atop(pa); |
145 | } |
146 | |
147 | /* Initialize data for an io operation on a scatter buffer. Returns |
148 | * true if the transfer is valid, or false if out of range. */ |
149 | bool |
150 | dtv_scatter_io_init(struct dtv_scatter_buf *sb, |
151 | off_t off, size_t len, |
152 | struct dtv_scatter_io *sio) |
153 | { |
154 | if ((off + len) > sb->sb_size) { |
155 | printf("dtv: %s failed: off=%" PRId64 |
156 | " len=%zu sb->sb_size=%zu\n" , |
157 | __func__, off, len, sb->sb_size); |
158 | return false; |
159 | } |
160 | |
161 | sio->sio_buf = sb; |
162 | sio->sio_offset = off; |
163 | sio->sio_resid = len; |
164 | |
165 | return true; |
166 | } |
167 | |
168 | /* Store the pointer and size of the next contiguous segment. Returns |
169 | * true if the segment is valid, or false if all has been transfered. |
170 | * Does not check for overflow. */ |
171 | bool |
172 | dtv_scatter_io_next(struct dtv_scatter_io *sio, void **p, size_t *sz) |
173 | { |
174 | size_t pg, pgo; |
175 | |
176 | if (sio->sio_resid == 0) |
177 | return false; |
178 | |
179 | pg = sio->sio_offset >> PAGE_SHIFT; |
180 | pgo = sio->sio_offset & PAGE_MASK; |
181 | |
182 | *sz = min(PAGE_SIZE - pgo, sio->sio_resid); |
183 | *p = sio->sio_buf->sb_page_ary[pg] + pgo; |
184 | |
185 | sio->sio_offset += *sz; |
186 | sio->sio_resid -= *sz; |
187 | |
188 | return true; |
189 | } |
190 | |
191 | /* Semi-undo of a failed segment copy. Updates the scatter_io |
192 | * struct to the previous values prior to a failed segment copy. */ |
193 | void |
194 | dtv_scatter_io_undo(struct dtv_scatter_io *sio, size_t sz) |
195 | { |
196 | sio->sio_offset -= sz; |
197 | sio->sio_resid += sz; |
198 | } |
199 | |
200 | /* Copy data from src into the scatter_buf as described by io. */ |
201 | void |
202 | dtv_scatter_io_copyin(struct dtv_scatter_io *sio, const void *p) |
203 | { |
204 | void *dst; |
205 | const uint8_t *src = p; |
206 | size_t sz; |
207 | |
208 | while (dtv_scatter_io_next(sio, &dst, &sz)) { |
209 | memcpy(dst, src, sz); |
210 | src += sz; |
211 | } |
212 | } |
213 | |
214 | /* --not used; commented to avoid compiler warnings-- |
215 | void |
216 | dtv_scatter_io_copyout(struct dtv_scatter_io *sio, void *p) |
217 | { |
218 | void *src; |
219 | uint8_t *dst = p; |
220 | size_t sz; |
221 | |
222 | while (dtv_scatter_io_next(sio, &src, &sz)) { |
223 | memcpy(dst, src, sz); |
224 | dst += sz; |
225 | } |
226 | } |
227 | */ |
228 | |
229 | /* Performat a series of uiomove calls on a scatter buf. Returns |
230 | * EFAULT if uiomove EFAULTs on the first segment. Otherwise, returns |
231 | * an incomplete transfer but with no error. */ |
232 | int |
233 | dtv_scatter_io_uiomove(struct dtv_scatter_io *sio, struct uio *uio) |
234 | { |
235 | void *p; |
236 | size_t sz; |
237 | bool first = true; |
238 | int err; |
239 | |
240 | while (dtv_scatter_io_next(sio, &p, &sz)) { |
241 | err = uiomove(p, sz, uio); |
242 | if (err == EFAULT) { |
243 | dtv_scatter_io_undo(sio, sz); |
244 | if (first) |
245 | return EFAULT; |
246 | else |
247 | return 0; |
248 | } |
249 | first = false; |
250 | } |
251 | |
252 | return 0; |
253 | } |
254 | |