ia64/xen-unstable

view xen/common/xencomm.c @ 17402:146f214a0e63

xencomm: add support for log dirty mode

Signed-off-by: Kouya Shimura <kouya@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Apr 07 15:02:47 2008 +0100 (2008-04-07)
parents adad9f3820f1
children b3454459ba31
line source
1 /******************************************************************************
2 * xencomm.c
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 * Copyright (C) IBM Corp. 2006
19 *
20 * Authors: Hollis Blanchard <hollisb@us.ibm.com>
21 * Tristan Gingold <tristan.gingold@bull.net>
22 * Isaku Yamahata <yamahata@valinux.co.jp> multiple page support
23 */
25 #include <xen/config.h>
26 #include <xen/mm.h>
27 #include <xen/sched.h>
28 #include <xen/xencomm.h>
29 #include <public/xen.h>
30 #include <public/xencomm.h>
32 #undef DEBUG
33 #ifdef DEBUG
34 #define xc_dprintk(f, a...) printk("[xencomm]" f , ## a)
35 #else
36 #define xc_dprintk(f, a...) ((void)0)
37 #endif
39 static void *
40 xencomm_vaddr(unsigned long paddr, struct page_info *page)
41 {
42 return (void*)((paddr & ~PAGE_MASK) | (unsigned long)page_to_virt(page));
43 }
45 /* get_page() to prevent another vcpu freeing the page. */
46 static int
47 xencomm_get_page(unsigned long paddr, struct page_info **page)
48 {
49 unsigned long maddr = paddr_to_maddr(paddr);
50 if ( maddr == 0 )
51 return -EFAULT;
53 *page = maddr_to_page(maddr);
54 if ( get_page(*page, current->domain) == 0 )
55 {
56 if ( page_get_owner(*page) != current->domain )
57 {
58 /*
59 * This page might be a page granted by another domain, or
60 * this page is freed with decrease reservation hypercall at
61 * the same time.
62 */
63 gdprintk(XENLOG_WARNING,
64 "bad page is passed. paddr 0x%lx maddr 0x%lx\n",
65 paddr, maddr);
66 return -EFAULT;
67 }
69 /* Try again. */
70 cpu_relax();
71 return -EAGAIN;
72 }
74 return 0;
75 }
77 /* check if struct desc doesn't cross page boundry */
78 static int
79 xencomm_desc_cross_page_boundary(unsigned long paddr)
80 {
81 unsigned long offset = paddr & ~PAGE_MASK;
82 if ( offset > PAGE_SIZE - sizeof(struct xencomm_desc) )
83 return 1;
84 return 0;
85 }
87 struct xencomm_ctxt {
88 struct xencomm_desc __user *desc_in_paddr;
89 uint32_t nr_addrs;
91 struct page_info *page;
92 unsigned long *address;
93 };
95 static uint32_t
96 xencomm_ctxt_nr_addrs(const struct xencomm_ctxt *ctxt)
97 {
98 return ctxt->nr_addrs;
99 }
101 static unsigned long*
102 xencomm_ctxt_address(struct xencomm_ctxt *ctxt)
103 {
104 return ctxt->address;
105 }
107 static int
108 xencomm_ctxt_init(const void *handle, struct xencomm_ctxt *ctxt)
109 {
110 struct page_info *page;
111 struct xencomm_desc *desc;
112 int ret;
114 /* Avoid unaligned access. */
115 if ( ((unsigned long)handle % __alignof__(*desc)) != 0 )
116 return -EINVAL;
117 if ( xencomm_desc_cross_page_boundary((unsigned long)handle) )
118 return -EINVAL;
120 /* First we need to access the descriptor. */
121 ret = xencomm_get_page((unsigned long)handle, &page);
122 if ( ret )
123 return ret;
125 desc = xencomm_vaddr((unsigned long)handle, page);
126 if ( desc->magic != XENCOMM_MAGIC )
127 {
128 printk("%s: error: %p magic was 0x%x\n", __func__, desc, desc->magic);
129 put_page(page);
130 return -EINVAL;
131 }
133 /* Copy before use: It is possible for a guest to modify concurrently. */
134 ctxt->nr_addrs = desc->nr_addrs;
135 ctxt->desc_in_paddr = (struct xencomm_desc*)handle;
136 ctxt->page = page;
137 ctxt->address = &desc->address[0];
138 return 0;
139 }
141 /*
142 * Calculate the vaddr of &ctxt->desc_in_paddr->address[i] and get_page().
143 * And put the results in ctxt->page and ctxt->address.
144 * If there is the previous page, put_page().
145 *
146 * A guest domain passes the array, ctxt->desc_in_paddr->address[].
147 * It is gpaddr-contiguous, but not maddr-contiguous so that
148 * we can't obtain the vaddr by simple offsetting.
149 * We need to convert gpaddr, &ctxt->desc_in_paddr->address[i],
150 * into maddr and then convert it to the xen virtual address in order
151 * to access there.
152 * The conversion can be optimized out by using the last result of
153 * ctxt->address because we access the array sequentially.
154 * The conversion, gpaddr -> maddr -> vaddr, is necessary only when
155 * crossing page boundary.
156 */
157 static int
158 xencomm_ctxt_next(struct xencomm_ctxt *ctxt, int i)
159 {
160 unsigned long paddr;
161 struct page_info *page;
162 int ret;
164 BUG_ON(i >= ctxt->nr_addrs);
166 /* For i == 0 case we already calculated it in xencomm_ctxt_init(). */
167 if ( i != 0 )
168 ctxt->address++;
170 if ( ((unsigned long)ctxt->address & ~PAGE_MASK) != 0 )
171 return 0;
173 /* Crossing page boundary: machine address must be calculated. */
174 paddr = (unsigned long)&ctxt->desc_in_paddr->address[i];
175 ret = xencomm_get_page(paddr, &page);
176 if ( ret )
177 return ret;
179 put_page(ctxt->page);
180 ctxt->page = page;
181 ctxt->address = xencomm_vaddr(paddr, page);
183 return 0;
184 }
186 static void
187 xencomm_ctxt_done(struct xencomm_ctxt *ctxt)
188 {
189 put_page(ctxt->page);
190 }
192 static int
193 xencomm_copy_chunk_from(
194 unsigned long to, unsigned long paddr, unsigned int len)
195 {
196 struct page_info *page;
197 int res;
199 do {
200 res = xencomm_get_page(paddr, &page);
201 } while ( res == -EAGAIN );
203 if ( res )
204 return res;
206 xc_dprintk("%lx[%d] -> %lx\n",
207 (unsigned long)xencomm_vaddr(paddr, page), len, to);
209 memcpy((void *)to, xencomm_vaddr(paddr, page), len);
210 put_page(page);
212 return 0;
213 }
215 static unsigned long
216 xencomm_inline_from_guest(
217 void *to, const void *from, unsigned int n, unsigned int skip)
218 {
219 unsigned long src_paddr = xencomm_inline_addr(from) + skip;
221 while ( n > 0 )
222 {
223 unsigned int chunksz, bytes;
225 chunksz = PAGE_SIZE - (src_paddr % PAGE_SIZE);
226 bytes = min(chunksz, n);
228 if ( xencomm_copy_chunk_from((unsigned long)to, src_paddr, bytes) )
229 return n;
230 src_paddr += bytes;
231 to += bytes;
232 n -= bytes;
233 }
235 /* Always successful. */
236 return 0;
237 }
239 /**
240 * xencomm_copy_from_guest: Copy a block of data from domain space.
241 * @to: Machine address.
242 * @from: Physical address to a xencomm buffer descriptor.
243 * @n: Number of bytes to copy.
244 * @skip: Number of bytes from the start to skip.
245 *
246 * Copy data from domain to hypervisor.
247 *
248 * Returns number of bytes that could not be copied.
249 * On success, this will be zero.
250 */
251 unsigned long
252 xencomm_copy_from_guest(
253 void *to, const void *from, unsigned int n, unsigned int skip)
254 {
255 struct xencomm_ctxt ctxt;
256 unsigned int from_pos = 0;
257 unsigned int to_pos = 0;
258 unsigned int i = 0;
260 if ( xencomm_is_inline(from) )
261 return xencomm_inline_from_guest(to, from, n, skip);
263 if ( xencomm_ctxt_init(from, &ctxt) )
264 return n;
266 /* Iterate through the descriptor, copying up to a page at a time */
267 while ( (to_pos < n) && (i < xencomm_ctxt_nr_addrs(&ctxt)) )
268 {
269 unsigned long src_paddr;
270 unsigned int pgoffset, chunksz, chunk_skip;
272 if ( xencomm_ctxt_next(&ctxt, i) )
273 goto out;
274 src_paddr = *xencomm_ctxt_address(&ctxt);
275 if ( src_paddr == XENCOMM_INVALID )
276 {
277 i++;
278 continue;
279 }
281 pgoffset = src_paddr % PAGE_SIZE;
282 chunksz = PAGE_SIZE - pgoffset;
284 chunk_skip = min(chunksz, skip);
285 from_pos += chunk_skip;
286 chunksz -= chunk_skip;
287 skip -= chunk_skip;
289 if ( skip == 0 && chunksz > 0 )
290 {
291 unsigned int bytes = min(chunksz, n - to_pos);
293 if ( xencomm_copy_chunk_from((unsigned long)to + to_pos,
294 src_paddr + chunk_skip, bytes) )
295 goto out;
296 from_pos += bytes;
297 to_pos += bytes;
298 }
300 i++;
301 }
303 out:
304 xencomm_ctxt_done(&ctxt);
305 return n - to_pos;
306 }
308 static int
309 xencomm_copy_chunk_to(
310 unsigned long paddr, unsigned long from, unsigned int len)
311 {
312 struct page_info *page;
313 int res;
315 do {
316 res = xencomm_get_page(paddr, &page);
317 } while ( res == -EAGAIN );
319 if ( res )
320 return res;
322 xc_dprintk("%lx[%d] -> %lx\n", from, len,
323 (unsigned long)xencomm_vaddr(paddr, page));
325 memcpy(xencomm_vaddr(paddr, page), (void *)from, len);
326 xencomm_mark_dirty(xencomm_vaddr(paddr, page), len);
327 put_page(page);
329 return 0;
330 }
332 static unsigned long
333 xencomm_inline_to_guest(
334 void *to, const void *from, unsigned int n, unsigned int skip)
335 {
336 unsigned long dest_paddr = xencomm_inline_addr(to) + skip;
338 while ( n > 0 )
339 {
340 unsigned int chunksz, bytes;
342 chunksz = PAGE_SIZE - (dest_paddr % PAGE_SIZE);
343 bytes = min(chunksz, n);
345 if ( xencomm_copy_chunk_to(dest_paddr, (unsigned long)from, bytes) )
346 return n;
347 dest_paddr += bytes;
348 from += bytes;
349 n -= bytes;
350 }
352 /* Always successful. */
353 return 0;
354 }
356 /**
357 * xencomm_copy_to_guest: Copy a block of data to domain space.
358 * @to: Physical address to xencomm buffer descriptor.
359 * @from: Machine address.
360 * @n: Number of bytes to copy.
361 * @skip: Number of bytes from the start to skip.
362 *
363 * Copy data from hypervisor to domain.
364 *
365 * Returns number of bytes that could not be copied.
366 * On success, this will be zero.
367 */
368 unsigned long
369 xencomm_copy_to_guest(
370 void *to, const void *from, unsigned int n, unsigned int skip)
371 {
372 struct xencomm_ctxt ctxt;
373 unsigned int from_pos = 0;
374 unsigned int to_pos = 0;
375 unsigned int i = 0;
377 if ( xencomm_is_inline(to) )
378 return xencomm_inline_to_guest(to, from, n, skip);
380 if ( xencomm_ctxt_init(to, &ctxt) )
381 return n;
383 /* Iterate through the descriptor, copying up to a page at a time */
384 while ( (from_pos < n) && (i < xencomm_ctxt_nr_addrs(&ctxt)) )
385 {
386 unsigned long dest_paddr;
387 unsigned int pgoffset, chunksz, chunk_skip;
389 if ( xencomm_ctxt_next(&ctxt, i) )
390 goto out;
391 dest_paddr = *xencomm_ctxt_address(&ctxt);
392 if ( dest_paddr == XENCOMM_INVALID )
393 {
394 i++;
395 continue;
396 }
398 pgoffset = dest_paddr % PAGE_SIZE;
399 chunksz = PAGE_SIZE - pgoffset;
401 chunk_skip = min(chunksz, skip);
402 to_pos += chunk_skip;
403 chunksz -= chunk_skip;
404 skip -= chunk_skip;
406 if ( skip == 0 && chunksz > 0 )
407 {
408 unsigned int bytes = min(chunksz, n - from_pos);
410 if ( xencomm_copy_chunk_to(dest_paddr + chunk_skip,
411 (unsigned long)from + from_pos, bytes) )
412 goto out;
413 from_pos += bytes;
414 to_pos += bytes;
415 }
417 i++;
418 }
420 out:
421 xencomm_ctxt_done(&ctxt);
422 return n - from_pos;
423 }
425 static int xencomm_inline_add_offset(void **handle, unsigned int bytes)
426 {
427 *handle += bytes;
428 return 0;
429 }
431 /* Offset page addresses in 'handle' to skip 'bytes' bytes. Set completely
432 * exhausted pages to XENCOMM_INVALID. */
433 int xencomm_add_offset(void **handle, unsigned int bytes)
434 {
435 struct xencomm_ctxt ctxt;
436 int i = 0;
437 int res = 0;
439 if ( xencomm_is_inline(*handle) )
440 return xencomm_inline_add_offset(handle, bytes);
442 res = xencomm_ctxt_init(handle, &ctxt);
443 if ( res != 0 )
444 return res;
446 /* Iterate through the descriptor incrementing addresses */
447 while ( (bytes > 0) && (i < xencomm_ctxt_nr_addrs(&ctxt)) )
448 {
449 unsigned long *address;
450 unsigned long dest_paddr;
451 unsigned int pgoffset, chunksz, chunk_skip;
453 res = xencomm_ctxt_next(&ctxt, i);
454 if ( res )
455 goto out;
456 address = xencomm_ctxt_address(&ctxt);
457 dest_paddr = *address;
458 if ( dest_paddr == XENCOMM_INVALID )
459 {
460 i++;
461 continue;
462 }
464 pgoffset = dest_paddr % PAGE_SIZE;
465 chunksz = PAGE_SIZE - pgoffset;
467 chunk_skip = min(chunksz, bytes);
468 if ( chunk_skip == chunksz )
469 *address = XENCOMM_INVALID; /* exhausted this page */
470 else
471 *address += chunk_skip;
472 bytes -= chunk_skip;
474 i++;
475 }
477 out:
478 xencomm_ctxt_done(&ctxt);
479 return res;
480 }
482 int xencomm_handle_is_null(void *handle)
483 {
484 struct xencomm_ctxt ctxt;
485 int i;
486 int res = 1;
488 if ( xencomm_is_inline(handle) )
489 return xencomm_inline_addr(handle) == 0;
491 if ( xencomm_ctxt_init(handle, &ctxt) )
492 return 1;
494 for ( i = 0; i < xencomm_ctxt_nr_addrs(&ctxt); i++ )
495 {
496 if ( xencomm_ctxt_next(&ctxt, i) )
497 goto out;
498 if ( *xencomm_ctxt_address(&ctxt) != XENCOMM_INVALID )
499 {
500 res = 0;
501 goto out;
502 }
503 }
505 out:
506 xencomm_ctxt_done(&ctxt);
507 return res;
508 }
510 /*
511 * Local variables:
512 * mode: C
513 * c-set-style: "BSD"
514 * c-basic-offset: 4
515 * tab-width: 4
516 * indent-tabs-mode: nil
517 * End:
518 */