ia64/linux-2.6.18-xen.hg

annotate arch/ia64/xen/xcom_privcmd.c @ 897:329ea0ccb344

balloon: try harder to balloon up under memory pressure.

Currently if the balloon driver is unable to increase the guest's
reservation it assumes the failure was due to reaching its full
allocation, gives up on the ballooning operation and records the limit
it reached as the "hard limit". The driver will not try again until
the target is set again (even to the same value).

However it is possible that ballooning has in fact failed due to
memory pressure in the host and therefore it is desirable to keep
attempting to reach the target in case memory becomes available. The
most likely scenario is that some guests are ballooning down while
others are ballooning up and therefore there is temporary memory
pressure while things stabilise. You would not expect a well behaved
toolstack to ask a domain to balloon to more than its allocation nor
would you expect it to deliberately over-commit memory by setting
balloon targets which exceed the total host memory.

This patch drops the concept of a hard limit and causes the balloon
driver to retry increasing the reservation on a timer in the same
manner as when decreasing the reservation.

Also if we partially succeed in increasing the reservation
(i.e. receive less pages than we asked for) then we may as well keep
those pages rather than returning them to Xen.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jun 05 14:01:20 2009 +0100 (2009-06-05)
parents 9ab1c319531f
children 5bb4e5dd1abc
rev   line source
ian@26 1 /*
ian@26 2 * This program is free software; you can redistribute it and/or modify
ian@26 3 * it under the terms of the GNU General Public License as published by
ian@26 4 * the Free Software Foundation; either version 2 of the License, or
ian@26 5 * (at your option) any later version.
ian@26 6 *
ian@26 7 * This program is distributed in the hope that it will be useful,
ian@26 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
ian@26 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
ian@26 10 * GNU General Public License for more details.
ian@26 11 *
ian@26 12 * You should have received a copy of the GNU General Public License
ian@26 13 * along with this program; if not, write to the Free Software
ian@26 14 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
ian@26 15 *
ian@26 16 * Authors: Hollis Blanchard <hollisb@us.ibm.com>
ian@26 17 * Tristan Gingold <tristan.gingold@bull.net>
ian@26 18 */
ian@26 19 #include <linux/types.h>
ian@26 20 #include <linux/errno.h>
ian@26 21 #include <linux/kernel.h>
ian@26 22 #include <linux/gfp.h>
ian@26 23 #include <linux/module.h>
ian@26 24 #include <xen/interface/xen.h>
ian@26 25 #include <xen/interface/platform.h>
ian@26 26 #define __XEN__
ian@26 27 #include <xen/interface/domctl.h>
ian@26 28 #include <xen/interface/sysctl.h>
ian@26 29 #include <xen/interface/memory.h>
ian@26 30 #include <xen/interface/version.h>
ian@26 31 #include <xen/interface/event_channel.h>
keir@271 32 #include <xen/interface/xsm/acm_ops.h>
ian@26 33 #include <xen/interface/hvm/params.h>
alex@324 34 #include <xen/interface/arch-ia64/debug_op.h>
ian@26 35 #include <xen/public/privcmd.h>
ian@26 36 #include <asm/hypercall.h>
ian@26 37 #include <asm/page.h>
ian@26 38 #include <asm/uaccess.h>
ian@26 39 #include <asm/xen/xencomm.h>
ian@26 40
ian@26 41 #define ROUND_DIV(v,s) (((v) + (s) - 1) / (s))
ian@26 42
ian@26 43 static int
ian@26 44 xencomm_privcmd_platform_op(privcmd_hypercall_t *hypercall)
ian@26 45 {
ian@26 46 struct xen_platform_op kern_op;
ian@26 47 struct xen_platform_op __user *user_op = (struct xen_platform_op __user *)hypercall->arg[0];
ian@26 48 struct xencomm_handle *op_desc;
ian@26 49 struct xencomm_handle *desc = NULL;
ian@26 50 int ret = 0;
ian@26 51
ian@26 52 if (copy_from_user(&kern_op, user_op, sizeof(struct xen_platform_op)))
ian@26 53 return -EFAULT;
ian@26 54
ian@26 55 if (kern_op.interface_version != XENPF_INTERFACE_VERSION)
ian@26 56 return -EACCES;
ian@26 57
alex@187 58 op_desc = xencomm_map_no_alloc(&kern_op, sizeof(kern_op));
ian@26 59
ian@26 60 switch (kern_op.cmd) {
ian@26 61 default:
ian@26 62 printk("%s: unknown platform cmd %d\n", __func__, kern_op.cmd);
ian@26 63 return -ENOSYS;
ian@26 64 }
ian@26 65
ian@26 66 if (ret) {
ian@26 67 /* error mapping the nested pointer */
ian@26 68 return ret;
ian@26 69 }
ian@26 70
ian@26 71 ret = xencomm_arch_hypercall_platform_op(op_desc);
ian@26 72
ian@26 73 /* FIXME: should we restore the handle? */
ian@26 74 if (copy_to_user(user_op, &kern_op, sizeof(struct xen_platform_op)))
ian@26 75 ret = -EFAULT;
ian@26 76
alex@187 77 xencomm_free(desc);
ian@26 78 return ret;
ian@26 79 }
ian@26 80
ian@26 81 static int
ian@26 82 xencomm_privcmd_sysctl(privcmd_hypercall_t *hypercall)
ian@26 83 {
ian@26 84 xen_sysctl_t kern_op;
ian@26 85 xen_sysctl_t __user *user_op;
ian@26 86 struct xencomm_handle *op_desc;
ian@26 87 struct xencomm_handle *desc = NULL;
ian@26 88 struct xencomm_handle *desc1 = NULL;
ian@26 89 int ret = 0;
ian@26 90
ian@26 91 user_op = (xen_sysctl_t __user *)hypercall->arg[0];
ian@26 92
ian@26 93 if (copy_from_user(&kern_op, user_op, sizeof(xen_sysctl_t)))
ian@26 94 return -EFAULT;
ian@26 95
ian@26 96 if (kern_op.interface_version != XEN_SYSCTL_INTERFACE_VERSION)
ian@26 97 return -EACCES;
ian@26 98
alex@187 99 op_desc = xencomm_map_no_alloc(&kern_op, sizeof(kern_op));
ian@26 100
ian@26 101 switch (kern_op.cmd) {
ian@26 102 case XEN_SYSCTL_readconsole:
alex@187 103 desc = xencomm_map(
ian@26 104 xen_guest_handle(kern_op.u.readconsole.buffer),
alex@187 105 kern_op.u.readconsole.count);
alex@187 106 if (xen_guest_handle(kern_op.u.readconsole.buffer) != NULL &&
alex@187 107 kern_op.u.readconsole.count > 0 && desc == NULL)
alex@187 108 return -ENOMEM;
ian@26 109 set_xen_guest_handle(kern_op.u.readconsole.buffer,
ian@26 110 (void *)desc);
ian@26 111 break;
ian@26 112 case XEN_SYSCTL_tbuf_op:
ian@26 113 case XEN_SYSCTL_sched_id:
alex@442 114 case XEN_SYSCTL_availheap:
ian@26 115 break;
ian@26 116 case XEN_SYSCTL_perfc_op:
ian@26 117 {
ian@26 118 struct xencomm_handle *tmp_desc;
ian@26 119 xen_sysctl_t tmp_op = {
ian@26 120 .cmd = XEN_SYSCTL_perfc_op,
ian@26 121 .interface_version = XEN_SYSCTL_INTERFACE_VERSION,
ian@26 122 .u.perfc_op = {
ian@26 123 .cmd = XEN_SYSCTL_PERFCOP_query,
alex@392 124 /* .desc.p = NULL, */
alex@392 125 /* .val.p = NULL, */
ian@26 126 },
ian@26 127 };
ian@26 128
ian@26 129 if (xen_guest_handle(kern_op.u.perfc_op.desc) == NULL) {
ian@26 130 if (xen_guest_handle(kern_op.u.perfc_op.val) != NULL)
ian@26 131 return -EINVAL;
ian@26 132 break;
ian@26 133 }
ian@26 134
ian@26 135 /* query the buffer size for xencomm */
alex@187 136 tmp_desc = xencomm_map_no_alloc(&tmp_op, sizeof(tmp_op));
ian@26 137 ret = xencomm_arch_hypercall_sysctl(tmp_desc);
ian@26 138 if (ret)
ian@26 139 return ret;
ian@26 140
alex@187 141 desc = xencomm_map(xen_guest_handle(kern_op.u.perfc_op.desc),
alex@187 142 tmp_op.u.perfc_op.nr_counters *
alex@187 143 sizeof(xen_sysctl_perfc_desc_t));
alex@187 144 if (xen_guest_handle(kern_op.u.perfc_op.desc) != NULL &&
alex@187 145 tmp_op.u.perfc_op.nr_counters > 0 && desc == NULL)
alex@187 146 return -ENOMEM;
ian@26 147
ian@26 148 set_xen_guest_handle(kern_op.u.perfc_op.desc, (void *)desc);
ian@26 149
alex@187 150 desc1 = xencomm_map(xen_guest_handle(kern_op.u.perfc_op.val),
alex@187 151 tmp_op.u.perfc_op.nr_vals *
alex@187 152 sizeof(xen_sysctl_perfc_val_t));
alex@187 153 if (xen_guest_handle(kern_op.u.perfc_op.val) != NULL &&
alex@187 154 tmp_op.u.perfc_op.nr_vals > 0 && desc1 == NULL) {
ian@26 155 xencomm_free(desc);
alex@187 156 return -ENOMEM;
alex@187 157 }
ian@26 158
ian@26 159 set_xen_guest_handle(kern_op.u.perfc_op.val, (void *)desc1);
ian@26 160 break;
ian@26 161 }
ian@26 162 case XEN_SYSCTL_getdomaininfolist:
alex@187 163 desc = xencomm_map(
ian@26 164 xen_guest_handle(kern_op.u.getdomaininfolist.buffer),
ian@26 165 kern_op.u.getdomaininfolist.max_domains *
alex@187 166 sizeof(xen_domctl_getdomaininfo_t));
alex@187 167 if (xen_guest_handle(kern_op.u.getdomaininfolist.buffer) !=
alex@187 168 NULL && kern_op.u.getdomaininfolist.max_domains > 0 &&
alex@187 169 desc == NULL)
alex@187 170 return -ENOMEM;
ian@26 171 set_xen_guest_handle(kern_op.u.getdomaininfolist.buffer,
ian@26 172 (void *)desc);
ian@26 173 break;
ian@26 174 case XEN_SYSCTL_debug_keys:
alex@187 175 desc = xencomm_map(
ian@26 176 xen_guest_handle(kern_op.u.debug_keys.keys),
alex@187 177 kern_op.u.debug_keys.nr_keys);
alex@187 178 if (xen_guest_handle(kern_op.u.debug_keys.keys) != NULL &&
alex@187 179 kern_op.u.debug_keys.nr_keys > 0 && desc == NULL)
alex@187 180 return -ENOMEM;
ian@26 181 set_xen_guest_handle(kern_op.u.debug_keys.keys,
ian@26 182 (void *)desc);
ian@26 183 break;
ian@26 184
ian@26 185 case XEN_SYSCTL_physinfo:
alex@187 186 desc = xencomm_map(
alex@122 187 xen_guest_handle(kern_op.u.physinfo.cpu_to_node),
alex@187 188 kern_op.u.physinfo.max_cpu_id * sizeof(uint32_t));
alex@187 189 if (xen_guest_handle(kern_op.u.physinfo.cpu_to_node) != NULL &&
alex@187 190 kern_op.u.physinfo.max_cpu_id > 0 && desc == NULL)
alex@187 191 return -ENOMEM;
ian@26 192
ian@26 193 set_xen_guest_handle(kern_op.u.physinfo.cpu_to_node,
alex@122 194 (void *)desc);
ian@26 195 break;
yamahata@688 196
yamahata@688 197 case XEN_SYSCTL_get_pmstat:
yamahata@688 198 if (kern_op.u.get_pmstat.type == PMSTAT_get_pxstat) {
yamahata@688 199 struct pm_px_stat *getpx =
yamahata@688 200 &kern_op.u.get_pmstat.u.getpx;
yamahata@688 201 desc = xencomm_map(
yamahata@688 202 xen_guest_handle(getpx->trans_pt),
yamahata@688 203 getpx->total * getpx->total *
yamahata@688 204 sizeof(uint64_t));
yamahata@688 205 if (xen_guest_handle(getpx->trans_pt) != NULL &&
yamahata@688 206 getpx->total > 0 && desc == NULL)
yamahata@688 207 return -ENOMEM;
yamahata@688 208
yamahata@688 209 set_xen_guest_handle(getpx->trans_pt, (void *)desc);
yamahata@688 210
yamahata@688 211 desc1 = xencomm_map(xen_guest_handle(getpx->pt),
yamahata@688 212 getpx->total * sizeof(pm_px_val_t));
yamahata@688 213 if (xen_guest_handle(getpx->pt) != NULL &&
yamahata@688 214 getpx->total > 0 && desc1 == NULL)
yamahata@688 215 return -ENOMEM;
yamahata@688 216
yamahata@688 217 set_xen_guest_handle(getpx->pt, (void *)desc1);
yamahata@688 218 }
yamahata@688 219 break;
yamahata@688 220
ian@26 221 default:
ian@26 222 printk("%s: unknown sysctl cmd %d\n", __func__, kern_op.cmd);
ian@26 223 return -ENOSYS;
ian@26 224 }
ian@26 225
ian@26 226 if (ret) {
ian@26 227 /* error mapping the nested pointer */
ian@26 228 return ret;
ian@26 229 }
ian@26 230
ian@26 231 ret = xencomm_arch_hypercall_sysctl(op_desc);
ian@26 232
ian@26 233 /* FIXME: should we restore the handles? */
ian@26 234 if (copy_to_user(user_op, &kern_op, sizeof(xen_sysctl_t)))
ian@26 235 ret = -EFAULT;
ian@26 236
alex@187 237 xencomm_free(desc);
alex@187 238 xencomm_free(desc1);
ian@26 239 return ret;
ian@26 240 }
ian@26 241
ian@26 242 static int
ian@26 243 xencomm_privcmd_domctl(privcmd_hypercall_t *hypercall)
ian@26 244 {
ian@26 245 xen_domctl_t kern_op;
ian@26 246 xen_domctl_t __user *user_op;
ian@26 247 struct xencomm_handle *op_desc;
ian@26 248 struct xencomm_handle *desc = NULL;
ian@26 249 int ret = 0;
ian@26 250
ian@26 251 user_op = (xen_domctl_t __user *)hypercall->arg[0];
ian@26 252
ian@26 253 if (copy_from_user(&kern_op, user_op, sizeof(xen_domctl_t)))
ian@26 254 return -EFAULT;
ian@26 255
ian@26 256 if (kern_op.interface_version != XEN_DOMCTL_INTERFACE_VERSION)
ian@26 257 return -EACCES;
ian@26 258
alex@187 259 op_desc = xencomm_map_no_alloc(&kern_op, sizeof(kern_op));
ian@26 260
ian@26 261 switch (kern_op.cmd) {
ian@26 262 case XEN_DOMCTL_createdomain:
ian@26 263 case XEN_DOMCTL_destroydomain:
ian@26 264 case XEN_DOMCTL_pausedomain:
ian@26 265 case XEN_DOMCTL_unpausedomain:
alex@255 266 case XEN_DOMCTL_resumedomain:
ian@26 267 case XEN_DOMCTL_getdomaininfo:
ian@26 268 break;
ian@26 269 case XEN_DOMCTL_getmemlist:
ian@26 270 {
ian@26 271 unsigned long nr_pages = kern_op.u.getmemlist.max_pfns;
ian@26 272
alex@187 273 desc = xencomm_map(
ian@26 274 xen_guest_handle(kern_op.u.getmemlist.buffer),
alex@187 275 nr_pages * sizeof(unsigned long));
alex@187 276 if (xen_guest_handle(kern_op.u.getmemlist.buffer) != NULL &&
alex@187 277 nr_pages > 0 && desc == NULL)
alex@187 278 return -ENOMEM;
ian@26 279 set_xen_guest_handle(kern_op.u.getmemlist.buffer,
ian@26 280 (void *)desc);
ian@26 281 break;
ian@26 282 }
ian@26 283 case XEN_DOMCTL_getpageframeinfo:
ian@26 284 break;
ian@26 285 case XEN_DOMCTL_getpageframeinfo2:
alex@187 286 desc = xencomm_map(
ian@26 287 xen_guest_handle(kern_op.u.getpageframeinfo2.array),
alex@187 288 kern_op.u.getpageframeinfo2.num);
alex@187 289 if (xen_guest_handle(kern_op.u.getpageframeinfo2.array) !=
alex@187 290 NULL && kern_op.u.getpageframeinfo2.num > 0 &&
alex@187 291 desc == NULL)
alex@187 292 return -ENOMEM;
ian@26 293 set_xen_guest_handle(kern_op.u.getpageframeinfo2.array,
ian@26 294 (void *)desc);
ian@26 295 break;
ian@26 296 case XEN_DOMCTL_shadow_op:
alex@187 297 desc = xencomm_map(
ian@26 298 xen_guest_handle(kern_op.u.shadow_op.dirty_bitmap),
alex@187 299 ROUND_DIV(kern_op.u.shadow_op.pages, 8));
alex@187 300 if (xen_guest_handle(kern_op.u.shadow_op.dirty_bitmap) != NULL
alex@187 301 && kern_op.u.shadow_op.pages > 0 && desc == NULL)
alex@187 302 return -ENOMEM;
ian@26 303 set_xen_guest_handle(kern_op.u.shadow_op.dirty_bitmap,
ian@26 304 (void *)desc);
ian@26 305 break;
ian@26 306 case XEN_DOMCTL_max_mem:
ian@26 307 break;
ian@26 308 case XEN_DOMCTL_setvcpucontext:
ian@26 309 case XEN_DOMCTL_getvcpucontext:
alex@187 310 desc = xencomm_map(
ian@26 311 xen_guest_handle(kern_op.u.vcpucontext.ctxt),
alex@187 312 sizeof(vcpu_guest_context_t));
alex@187 313 if (xen_guest_handle(kern_op.u.vcpucontext.ctxt) != NULL &&
alex@187 314 desc == NULL)
alex@187 315 return -ENOMEM;
ian@26 316 set_xen_guest_handle(kern_op.u.vcpucontext.ctxt, (void *)desc);
ian@26 317 break;
ian@26 318 case XEN_DOMCTL_getvcpuinfo:
ian@26 319 break;
ian@26 320 case XEN_DOMCTL_setvcpuaffinity:
ian@26 321 case XEN_DOMCTL_getvcpuaffinity:
alex@187 322 desc = xencomm_map(
ian@26 323 xen_guest_handle(kern_op.u.vcpuaffinity.cpumap.bitmap),
alex@187 324 ROUND_DIV(kern_op.u.vcpuaffinity.cpumap.nr_cpus, 8));
alex@187 325 if (xen_guest_handle(kern_op.u.vcpuaffinity.cpumap.bitmap) !=
alex@187 326 NULL && kern_op.u.vcpuaffinity.cpumap.nr_cpus > 0 &&
alex@187 327 desc == NULL)
alex@187 328 return -ENOMEM;
ian@26 329 set_xen_guest_handle(kern_op.u.vcpuaffinity.cpumap.bitmap,
ian@26 330 (void *)desc);
ian@26 331 break;
alex@273 332 case XEN_DOMCTL_gethvmcontext:
alex@273 333 case XEN_DOMCTL_sethvmcontext:
alex@273 334 if (kern_op.u.hvmcontext.size > 0)
alex@273 335 desc = xencomm_map(
alex@273 336 xen_guest_handle(kern_op.u.hvmcontext.buffer),
alex@273 337 kern_op.u.hvmcontext.size);
alex@273 338 if (xen_guest_handle(kern_op.u.hvmcontext.buffer) != NULL &&
alex@273 339 kern_op.u.hvmcontext.size > 0 && desc == NULL)
alex@273 340 return -ENOMEM;
alex@273 341 set_xen_guest_handle(kern_op.u.hvmcontext.buffer, (void*)desc);
alex@273 342 break;
yamahata@706 343 case XEN_DOMCTL_get_device_group:
yamahata@706 344 {
yamahata@706 345 struct xen_domctl_get_device_group *get_device_group =
yamahata@706 346 &kern_op.u.get_device_group;
yamahata@706 347 desc = xencomm_map(
yamahata@706 348 xen_guest_handle(get_device_group->sdev_array),
yamahata@706 349 get_device_group->max_sdevs * sizeof(uint32_t));
yamahata@706 350 if (xen_guest_handle(get_device_group->sdev_array) != NULL &&
yamahata@706 351 get_device_group->max_sdevs > 0 && desc == NULL)
yamahata@706 352 return -ENOMEM;
yamahata@706 353 set_xen_guest_handle(kern_op.u.get_device_group.sdev_array,
yamahata@706 354 (void*)desc);
yamahata@706 355 break;
yamahata@706 356 }
ian@26 357 case XEN_DOMCTL_max_vcpus:
ian@26 358 case XEN_DOMCTL_scheduler_op:
ian@26 359 case XEN_DOMCTL_setdomainhandle:
ian@26 360 case XEN_DOMCTL_setdebugging:
ian@26 361 case XEN_DOMCTL_irq_permission:
ian@26 362 case XEN_DOMCTL_iomem_permission:
ian@26 363 case XEN_DOMCTL_ioport_permission:
ian@26 364 case XEN_DOMCTL_hypercall_init:
ian@26 365 case XEN_DOMCTL_arch_setup:
ian@26 366 case XEN_DOMCTL_settimeoffset:
ian@26 367 case XEN_DOMCTL_sendtrigger:
alex@327 368 case XEN_DOMCTL_set_opt_feature:
yamahata@633 369 case XEN_DOMCTL_assign_device:
yamahata@648 370 case XEN_DOMCTL_subscribe:
yamahata@706 371 case XEN_DOMCTL_test_assign_device:
yamahata@706 372 case XEN_DOMCTL_deassign_device:
yamahata@706 373 case XEN_DOMCTL_bind_pt_irq:
yamahata@706 374 case XEN_DOMCTL_unbind_pt_irq:
yamahata@706 375 case XEN_DOMCTL_memory_mapping:
yamahata@706 376 case XEN_DOMCTL_ioport_mapping:
yamahata@774 377 case XEN_DOMCTL_set_address_size:
yamahata@774 378 case XEN_DOMCTL_get_address_size:
ian@26 379 break;
alex@298 380 case XEN_DOMCTL_pin_mem_cacheattr:
alex@298 381 return -ENOSYS;
ian@26 382 default:
ian@26 383 printk("%s: unknown domctl cmd %d\n", __func__, kern_op.cmd);
ian@26 384 return -ENOSYS;
ian@26 385 }
ian@26 386
ian@26 387 if (ret) {
ian@26 388 /* error mapping the nested pointer */
ian@26 389 return ret;
ian@26 390 }
ian@26 391
ian@26 392 ret = xencomm_arch_hypercall_domctl (op_desc);
ian@26 393
ian@26 394 /* FIXME: should we restore the handle? */
ian@26 395 if (copy_to_user(user_op, &kern_op, sizeof(xen_domctl_t)))
ian@26 396 ret = -EFAULT;
ian@26 397
alex@187 398 xencomm_free(desc);
ian@26 399 return ret;
ian@26 400 }
ian@26 401
ian@26 402 static int
keir@260 403 xencomm_privcmd_xsm_op(privcmd_hypercall_t *hypercall)
ian@26 404 {
alex@49 405 void __user *arg = (void __user *)hypercall->arg[0];
alex@49 406 xen_acmctl_t kern_arg;
ian@26 407 struct xencomm_handle *op_desc;
ian@26 408 struct xencomm_handle *desc = NULL;
ian@26 409 int ret;
ian@26 410
alex@49 411 if (copy_from_user(&kern_arg, arg, sizeof(kern_arg)))
alex@49 412 return -EFAULT;
alex@49 413 if (kern_arg.interface_version != ACM_INTERFACE_VERSION)
alex@49 414 return -ENOSYS;
alex@49 415
alex@49 416 switch (kern_arg.cmd) {
alex@49 417 case ACMOP_getssid: {
alex@187 418 op_desc = xencomm_map_no_alloc(&kern_arg, sizeof(kern_arg));
ian@26 419
alex@187 420 desc = xencomm_map(
alex@49 421 xen_guest_handle(kern_arg.u.getssid.ssidbuf),
alex@187 422 kern_arg.u.getssid.ssidbuf_size);
alex@187 423 if (xen_guest_handle(kern_arg.u.getssid.ssidbuf) != NULL &&
alex@187 424 kern_arg.u.getssid.ssidbuf_size > 0 && desc == NULL)
alex@187 425 return -ENOMEM;
ian@26 426
alex@49 427 set_xen_guest_handle(kern_arg.u.getssid.ssidbuf, (void *)desc);
ian@26 428
keir@260 429 ret = xencomm_arch_hypercall_xsm_op(op_desc);
ian@26 430
ian@26 431 xencomm_free(desc);
ian@26 432
alex@49 433 if (copy_to_user(arg, &kern_arg, sizeof(kern_arg)))
ian@26 434 return -EFAULT;
ian@26 435 return ret;
ian@26 436 }
ian@26 437 default:
alex@49 438 printk("%s: unknown acm_op cmd %d\n", __func__, kern_arg.cmd);
ian@26 439 return -ENOSYS;
ian@26 440 }
ian@26 441
ian@26 442 return ret;
ian@26 443 }
ian@26 444
ian@26 445 static int
alex@188 446 xencomm_privcmd_memory_reservation_op(privcmd_hypercall_t *hypercall)
alex@188 447 {
alex@188 448 const unsigned long cmd = hypercall->arg[0];
alex@188 449 int ret = 0;
alex@188 450 xen_memory_reservation_t kern_op;
alex@188 451 xen_memory_reservation_t __user *user_op;
alex@188 452 struct xencomm_handle *desc = NULL;
alex@188 453 struct xencomm_handle *desc_op;
alex@188 454
alex@188 455 user_op = (xen_memory_reservation_t __user *)hypercall->arg[1];
alex@188 456 if (copy_from_user(&kern_op, user_op,
alex@188 457 sizeof(xen_memory_reservation_t)))
alex@188 458 return -EFAULT;
alex@188 459 desc_op = xencomm_map_no_alloc(&kern_op, sizeof(kern_op));
alex@188 460
alex@188 461 if (!xen_guest_handle(kern_op.extent_start)) {
alex@188 462 ret = xencomm_arch_hypercall_memory_op(cmd, desc_op);
alex@188 463 if (ret < 0)
alex@188 464 return ret;
alex@188 465 } else {
alex@188 466 xen_ulong_t nr_done = 0;
alex@188 467 xen_ulong_t nr_extents = kern_op.nr_extents;
alex@188 468 void *addr = xen_guest_handle(kern_op.extent_start);
alex@188 469
alex@188 470 /*
alex@188 471 * Work around.
alex@188 472 * Xencomm has single page size limit caused
alex@188 473 * by xencomm_alloc()/xencomm_free() so that
alex@188 474 * we have to repeat the hypercall.
alex@188 475 * This limitation can be removed.
alex@188 476 */
alex@188 477 #define MEMORYOP_XENCOMM_LIMIT \
alex@188 478 (((((PAGE_SIZE - sizeof(struct xencomm_desc)) / \
alex@188 479 sizeof(uint64_t)) - 2) * PAGE_SIZE) / \
alex@188 480 sizeof(*xen_guest_handle(kern_op.extent_start)))
alex@188 481
alex@188 482 /*
alex@188 483 * Work around.
alex@188 484 * Even if the above limitation is removed,
alex@188 485 * the hypercall with large number of extents
alex@188 486 * may cause the soft lockup warning.
alex@188 487 * In order to avoid the warning, we limit
alex@188 488 * the number of extents and repeat the hypercall.
yamahata@617 489 * The following value is determined by evaluation.
yamahata@617 490 * Time of one hypercall should be smaller than
yamahata@617 491 * a vcpu time slice. The time with current
yamahata@617 492 * MEMORYOP_MAX_EXTENTS is around 5 msec.
yamahata@617 493 * If the following limit causes some issues,
alex@188 494 * we should decrease this value.
alex@188 495 *
alex@188 496 * Another way would be that start with small value and
alex@188 497 * increase adoptively measuring hypercall time.
alex@188 498 * It might be over-kill.
alex@188 499 */
yamahata@617 500 #define MEMORYOP_MAX_EXTENTS (MEMORYOP_XENCOMM_LIMIT / 512)
alex@188 501
alex@188 502 while (nr_extents > 0) {
alex@188 503 xen_ulong_t nr_tmp = nr_extents;
alex@188 504 if (nr_tmp > MEMORYOP_MAX_EXTENTS)
alex@188 505 nr_tmp = MEMORYOP_MAX_EXTENTS;
alex@188 506
alex@188 507 kern_op.nr_extents = nr_tmp;
alex@188 508 desc = xencomm_map
alex@188 509 (addr + nr_done * sizeof(*xen_guest_handle(kern_op.extent_start)),
alex@188 510 nr_tmp * sizeof(*xen_guest_handle(kern_op.extent_start)));
alex@188 511 if (addr != NULL && nr_tmp > 0 && desc == NULL)
alex@188 512 return nr_done > 0 ? nr_done : -ENOMEM;
alex@188 513
alex@188 514 set_xen_guest_handle(kern_op.extent_start,
alex@188 515 (void *)desc);
alex@188 516
alex@188 517 ret = xencomm_arch_hypercall_memory_op(cmd, desc_op);
alex@188 518 xencomm_free(desc);
alex@188 519 if (ret < 0)
alex@188 520 return nr_done > 0 ? nr_done : ret;
alex@188 521
alex@188 522 nr_done += ret;
alex@188 523 nr_extents -= ret;
alex@188 524 if (ret < nr_tmp)
alex@188 525 break;
alex@188 526
alex@188 527 /*
alex@188 528 * prevent softlock up message.
alex@188 529 * give cpu to soft lockup kernel thread.
alex@188 530 */
alex@188 531 if (nr_extents > 0)
alex@188 532 schedule();
alex@188 533 }
alex@188 534 ret = nr_done;
alex@188 535 set_xen_guest_handle(kern_op.extent_start, addr);
alex@188 536 }
alex@188 537
alex@188 538 if (copy_to_user(user_op, &kern_op, sizeof(xen_memory_reservation_t)))
alex@188 539 return -EFAULT;
alex@188 540
alex@188 541 return ret;
alex@188 542 }
alex@188 543
alex@188 544 static int
ian@26 545 xencomm_privcmd_memory_op(privcmd_hypercall_t *hypercall)
ian@26 546 {
ian@26 547 const unsigned long cmd = hypercall->arg[0];
ian@26 548 int ret = 0;
ian@26 549
ian@26 550 switch (cmd) {
ian@26 551 case XENMEM_increase_reservation:
ian@26 552 case XENMEM_decrease_reservation:
ian@26 553 case XENMEM_populate_physmap:
alex@188 554 return xencomm_privcmd_memory_reservation_op(hypercall);
alex@43 555 case XENMEM_maximum_gpfn:
alex@43 556 {
alex@43 557 domid_t kern_domid;
alex@43 558 domid_t __user *user_domid;
alex@43 559 struct xencomm_handle *desc;
alex@43 560
alex@43 561 user_domid = (domid_t __user *)hypercall->arg[1];
alex@43 562 if (copy_from_user(&kern_domid, user_domid, sizeof(domid_t)))
alex@43 563 return -EFAULT;
alex@187 564 desc = xencomm_map_no_alloc(&kern_domid, sizeof(kern_domid));
alex@43 565
alex@43 566 ret = xencomm_arch_hypercall_memory_op(cmd, desc);
alex@43 567
alex@43 568 return ret;
alex@43 569 }
yamahata@673 570 case XENMEM_add_to_physmap:
yamahata@673 571 {
yamahata@673 572 void __user *arg = (void __user *)hypercall->arg[1];
yamahata@673 573 struct xencomm_handle *desc;
yamahata@673 574
keir@776 575 desc = xencomm_map(arg, sizeof(struct xen_add_to_physmap));
yamahata@673 576 if (desc == NULL)
yamahata@673 577 return -ENOMEM;
yamahata@673 578
yamahata@673 579 ret = xencomm_arch_hypercall_memory_op(cmd, desc);
yamahata@673 580
yamahata@673 581 xencomm_free(desc);
yamahata@673 582 return ret;
yamahata@673 583 }
ian@26 584 default:
ian@26 585 printk("%s: unknown memory op %lu\n", __func__, cmd);
ian@26 586 ret = -ENOSYS;
ian@26 587 }
ian@26 588 return ret;
ian@26 589 }
ian@26 590
ian@26 591 static int
ian@26 592 xencomm_privcmd_xen_version(privcmd_hypercall_t *hypercall)
ian@26 593 {
ian@26 594 int cmd = hypercall->arg[0];
ian@26 595 void __user *arg = (void __user *)hypercall->arg[1];
ian@26 596 struct xencomm_handle *desc;
ian@26 597 size_t argsize;
ian@26 598 int rc;
ian@26 599
ian@26 600 switch (cmd) {
ian@26 601 case XENVER_version:
ian@26 602 /* do not actually pass an argument */
ian@26 603 return xencomm_arch_hypercall_xen_version(cmd, 0);
ian@26 604 case XENVER_extraversion:
ian@26 605 argsize = sizeof(xen_extraversion_t);
ian@26 606 break;
ian@26 607 case XENVER_compile_info:
ian@26 608 argsize = sizeof(xen_compile_info_t);
ian@26 609 break;
ian@26 610 case XENVER_capabilities:
ian@26 611 argsize = sizeof(xen_capabilities_info_t);
ian@26 612 break;
ian@26 613 case XENVER_changeset:
ian@26 614 argsize = sizeof(xen_changeset_info_t);
ian@26 615 break;
ian@26 616 case XENVER_platform_parameters:
ian@26 617 argsize = sizeof(xen_platform_parameters_t);
ian@26 618 break;
ian@26 619 case XENVER_pagesize:
ian@26 620 argsize = (arg == NULL) ? 0 : sizeof(void *);
ian@26 621 break;
ian@26 622 case XENVER_get_features:
ian@26 623 argsize = (arg == NULL) ? 0 : sizeof(xen_feature_info_t);
ian@26 624 break;
ian@26 625
ian@26 626 default:
ian@26 627 printk("%s: unknown version op %d\n", __func__, cmd);
ian@26 628 return -ENOSYS;
ian@26 629 }
ian@26 630
alex@187 631 desc = xencomm_map(arg, argsize);
alex@187 632 if (arg != NULL && argsize > 0 && desc == NULL)
alex@187 633 return -ENOMEM;
ian@26 634
ian@26 635 rc = xencomm_arch_hypercall_xen_version(cmd, desc);
ian@26 636
ian@26 637 xencomm_free(desc);
ian@26 638
ian@26 639 return rc;
ian@26 640 }
ian@26 641
ian@26 642 static int
ian@26 643 xencomm_privcmd_event_channel_op(privcmd_hypercall_t *hypercall)
ian@26 644 {
ian@26 645 int cmd = hypercall->arg[0];
ian@26 646 struct xencomm_handle *desc;
ian@26 647 unsigned int argsize;
ian@26 648 int ret;
ian@26 649
ian@26 650 switch (cmd) {
ian@26 651 case EVTCHNOP_alloc_unbound:
ian@26 652 argsize = sizeof(evtchn_alloc_unbound_t);
ian@26 653 break;
ian@26 654
ian@26 655 case EVTCHNOP_status:
ian@26 656 argsize = sizeof(evtchn_status_t);
ian@26 657 break;
ian@26 658
ian@26 659 default:
ian@26 660 printk("%s: unknown EVTCHNOP %d\n", __func__, cmd);
ian@26 661 return -EINVAL;
ian@26 662 }
ian@26 663
alex@187 664 desc = xencomm_map((void *)hypercall->arg[1], argsize);
alex@187 665 if ((void *)hypercall->arg[1] != NULL && argsize > 0 && desc == NULL)
alex@187 666 return -ENOMEM;
ian@26 667
ian@26 668 ret = xencomm_arch_hypercall_event_channel_op(cmd, desc);
ian@26 669
ian@26 670 xencomm_free(desc);
ian@26 671 return ret;
ian@26 672 }
ian@26 673
ian@26 674 static int
yamahata@543 675 xencomm_privcmd_hvm_op_track_dirty_vram(privcmd_hypercall_t *hypercall)
yamahata@543 676 {
yamahata@543 677 #if 1
yamahata@543 678 /*
yamahata@543 679 * At this moment HVMOP_track_dirty_vram isn't implemented
yamahata@543 680 * on xen/ia64 so that it just returns -ENOSYS.
yamahata@543 681 * Don't issue hypercall to get -ENOSYS.
yamahata@543 682 * When the hypercall is implemented, enable the following codes.
yamahata@543 683 */
yamahata@543 684 return -ENOSYS;
yamahata@543 685 #else
yamahata@543 686 int cmd = hypercall->arg[0];
yamahata@543 687 struct xen_hvm_track_dirty_vram *user_op = (void*)hypercall->arg[1];
yamahata@543 688 struct xen_hvm_track_dirty_vram kern_op;
yamahata@543 689 struct xencomm_handle *desc;
yamahata@543 690 struct xencomm_handle *bitmap_desc;
yamahata@543 691 int ret;
yamahata@543 692
yamahata@543 693 BUG_ON(cmd != HVMOP_track_dirty_vram);
yamahata@543 694 if (copy_from_user(&kern_op, user_op, sizeof(kern_op)))
yamahata@543 695 return -EFAULT;
yamahata@543 696 desc = xencomm_map_no_alloc(&kern_op, sizeof(kern_op));
yamahata@543 697 bitmap_desc = xencomm_map(xen_guest_handle(kern_op.dirty_bitmap),
yamahata@543 698 kern_op.nr * sizeof(uint8_t));
yamahata@543 699 if (bitmap_desc == NULL)
yamahata@543 700 return -ENOMEM;
yamahata@543 701 set_xen_guest_handle(kern_op.dirty_bitmap, (void*)bitmap_desc);
yamahata@543 702 ret = xencomm_arch_hypercall_hvm_op(cmd, desc);
yamahata@543 703 xencomm_free(bitmap_desc);
yamahata@543 704
yamahata@543 705 return ret;
yamahata@543 706 #endif
yamahata@543 707 }
yamahata@543 708
yamahata@543 709 static int
ian@26 710 xencomm_privcmd_hvm_op(privcmd_hypercall_t *hypercall)
ian@26 711 {
ian@26 712 int cmd = hypercall->arg[0];
ian@26 713 struct xencomm_handle *desc;
ian@26 714 unsigned int argsize;
ian@26 715 int ret;
ian@26 716
ian@26 717 switch (cmd) {
ian@26 718 case HVMOP_get_param:
ian@26 719 case HVMOP_set_param:
ian@26 720 argsize = sizeof(xen_hvm_param_t);
ian@26 721 break;
ian@26 722 case HVMOP_set_pci_intx_level:
ian@26 723 argsize = sizeof(xen_hvm_set_pci_intx_level_t);
ian@26 724 break;
ian@26 725 case HVMOP_set_isa_irq_level:
ian@26 726 argsize = sizeof(xen_hvm_set_isa_irq_level_t);
ian@26 727 break;
ian@26 728 case HVMOP_set_pci_link_route:
ian@26 729 argsize = sizeof(xen_hvm_set_pci_link_route_t);
ian@26 730 break;
yamahata@649 731 case HVMOP_set_mem_type:
yamahata@649 732 argsize = sizeof(xen_hvm_set_mem_type_t);
yamahata@649 733 break;
ian@26 734
yamahata@543 735 case HVMOP_track_dirty_vram:
yamahata@543 736 return xencomm_privcmd_hvm_op_track_dirty_vram(hypercall);
yamahata@543 737
ian@26 738 default:
ian@26 739 printk("%s: unknown HVMOP %d\n", __func__, cmd);
ian@26 740 return -EINVAL;
ian@26 741 }
ian@26 742
alex@187 743 desc = xencomm_map((void *)hypercall->arg[1], argsize);
alex@187 744 if ((void *)hypercall->arg[1] != NULL && argsize > 0 && desc == NULL)
alex@187 745 return -ENOMEM;
ian@26 746
ian@26 747 ret = xencomm_arch_hypercall_hvm_op(cmd, desc);
ian@26 748
ian@26 749 xencomm_free(desc);
ian@26 750 return ret;
ian@26 751 }
ian@26 752
ian@26 753 static int
ian@26 754 xencomm_privcmd_sched_op(privcmd_hypercall_t *hypercall)
ian@26 755 {
ian@26 756 int cmd = hypercall->arg[0];
ian@26 757 struct xencomm_handle *desc;
ian@26 758 unsigned int argsize;
ian@26 759 int ret;
ian@26 760
ian@26 761 switch (cmd) {
ian@26 762 case SCHEDOP_remote_shutdown:
ian@26 763 argsize = sizeof(sched_remote_shutdown_t);
ian@26 764 break;
ian@26 765 default:
ian@26 766 printk("%s: unknown SCHEDOP %d\n", __func__, cmd);
ian@26 767 return -EINVAL;
ian@26 768 }
ian@26 769
alex@187 770 desc = xencomm_map((void *)hypercall->arg[1], argsize);
alex@187 771 if ((void *)hypercall->arg[1] != NULL && argsize > 0 && desc == NULL)
alex@187 772 return -ENOMEM;
ian@26 773
ian@26 774 ret = xencomm_arch_hypercall_sched_op(cmd, desc);
ian@26 775
ian@26 776 xencomm_free(desc);
ian@26 777 return ret;
ian@26 778 }
ian@26 779
alex@43 780 static int
yamahata@687 781 xencomm_privcmd_dom0vp_get_memmap(domid_t domid,
yamahata@687 782 char* __user buf, unsigned long bufsize)
yamahata@687 783 {
yamahata@687 784 int ret;
yamahata@687 785 struct xencomm_handle *desc;
yamahata@687 786
yamahata@687 787 desc = xencomm_map(buf, bufsize);
yamahata@687 788 if (bufsize > 0 && desc == NULL)
yamahata@687 789 return -ENOMEM;
yamahata@687 790
yamahata@687 791 ret = xencomm_arch_hypercall_get_memmap((domid_t)domid, desc);
yamahata@687 792
yamahata@687 793 xencomm_free(desc);
yamahata@687 794 return ret;
yamahata@687 795 }
yamahata@687 796
yamahata@687 797 static int
alex@43 798 xencomm_privcmd_ia64_dom0vp_op(privcmd_hypercall_t *hypercall)
alex@43 799 {
alex@43 800 int cmd = hypercall->arg[0];
alex@43 801 int ret;
alex@212 802
alex@43 803 switch (cmd) {
alex@43 804 case IA64_DOM0VP_fpswa_revision: {
alex@43 805 unsigned int revision;
alex@43 806 unsigned int __user *revision_user =
alex@43 807 (unsigned int* __user)hypercall->arg[1];
alex@43 808 struct xencomm_handle *desc;
alex@187 809 desc = xencomm_map(&revision, sizeof(revision));
alex@187 810 if (desc == NULL)
alex@187 811 return -ENOMEM;
alex@187 812
alex@43 813 ret = xencomm_arch_hypercall_fpswa_revision(desc);
alex@43 814 xencomm_free(desc);
alex@43 815 if (ret)
alex@43 816 break;
alex@43 817 if (copy_to_user(revision_user, &revision, sizeof(revision)))
alex@43 818 ret = -EFAULT;
alex@43 819 break;
alex@43 820 }
alex@209 821 #ifdef CONFIG_XEN_IA64_EXPOSE_P2M
alex@197 822 case IA64_DOM0VP_expose_foreign_p2m:
alex@197 823 ret = xen_foreign_p2m_expose(hypercall);
alex@197 824 break;
alex@209 825 #endif
yamahata@687 826 case IA64_DOM0VP_get_memmap:
yamahata@687 827 ret = xencomm_privcmd_dom0vp_get_memmap(
yamahata@687 828 (domid_t)hypercall->arg[1],
yamahata@687 829 (char* __user)hypercall->arg[2], hypercall->arg[3]);
yamahata@687 830 break;
alex@43 831 default:
alex@43 832 printk("%s: unknown IA64 DOM0VP op %d\n", __func__, cmd);
alex@43 833 ret = -EINVAL;
alex@43 834 break;
alex@43 835 }
alex@43 836 return ret;
alex@43 837 }
alex@43 838
alex@212 839 static int
alex@212 840 xencomm_privcmd_ia64_debug_op(privcmd_hypercall_t *hypercall)
alex@212 841 {
alex@212 842 int cmd = hypercall->arg[0];
alex@212 843 unsigned long domain = hypercall->arg[1];
alex@212 844 struct xencomm_handle *desc;
alex@212 845 int ret;
alex@212 846
alex@212 847 switch (cmd) {
alex@212 848 case XEN_IA64_DEBUG_OP_SET_FLAGS:
alex@212 849 case XEN_IA64_DEBUG_OP_GET_FLAGS:
alex@212 850 break;
alex@212 851 default:
alex@212 852 printk("%s: unknown IA64 DEBUGOP %d\n", __func__, cmd);
alex@212 853 return -EINVAL;
alex@212 854 }
alex@212 855
alex@212 856 desc = xencomm_map((void *)hypercall->arg[2],
alex@212 857 sizeof(xen_ia64_debug_op_t));
alex@212 858 if (desc == NULL)
alex@212 859 return -ENOMEM;
alex@212 860
alex@212 861 ret = xencomm_arch_hypercall_ia64_debug_op(cmd, domain, desc);
alex@212 862
alex@212 863 xencomm_free(desc);
alex@212 864 return ret;
alex@212 865 }
alex@212 866
yamahata@633 867 static int
yamahata@633 868 xencomm_privcmd_ia64_physdev_op(privcmd_hypercall_t *hypercall)
yamahata@633 869 {
yamahata@633 870 int cmd = hypercall->arg[0];
yamahata@633 871 struct xencomm_handle *desc;
yamahata@633 872 unsigned int argsize;
yamahata@633 873 int ret;
yamahata@633 874
yamahata@633 875 switch (cmd) {
yamahata@633 876 case PHYSDEVOP_map_pirq:
yamahata@633 877 argsize = sizeof(physdev_map_pirq_t);
yamahata@633 878 break;
yamahata@633 879 case PHYSDEVOP_unmap_pirq:
yamahata@633 880 argsize = sizeof(physdev_unmap_pirq_t);
yamahata@633 881 break;
yamahata@633 882 default:
yamahata@633 883 printk("%s: unknown PHYSDEVOP %d\n", __func__, cmd);
yamahata@633 884 return -EINVAL;
yamahata@633 885 }
yamahata@633 886
yamahata@633 887 desc = xencomm_map((void *)hypercall->arg[1], argsize);
yamahata@633 888 if ((void *)hypercall->arg[1] != NULL && argsize > 0 && desc == NULL)
yamahata@633 889 return -ENOMEM;
yamahata@633 890
yamahata@633 891 ret = xencomm_arch_hypercall_physdev_op(cmd, desc);
yamahata@633 892
yamahata@633 893 xencomm_free(desc);
yamahata@633 894 return ret;
yamahata@633 895 }
yamahata@633 896
ian@26 897 int
ian@26 898 privcmd_hypercall(privcmd_hypercall_t *hypercall)
ian@26 899 {
ian@26 900 switch (hypercall->op) {
ian@26 901 case __HYPERVISOR_platform_op:
ian@26 902 return xencomm_privcmd_platform_op(hypercall);
ian@26 903 case __HYPERVISOR_domctl:
ian@26 904 return xencomm_privcmd_domctl(hypercall);
ian@26 905 case __HYPERVISOR_sysctl:
ian@26 906 return xencomm_privcmd_sysctl(hypercall);
keir@260 907 case __HYPERVISOR_xsm_op:
keir@260 908 return xencomm_privcmd_xsm_op(hypercall);
ian@26 909 case __HYPERVISOR_xen_version:
ian@26 910 return xencomm_privcmd_xen_version(hypercall);
ian@26 911 case __HYPERVISOR_memory_op:
ian@26 912 return xencomm_privcmd_memory_op(hypercall);
ian@26 913 case __HYPERVISOR_event_channel_op:
ian@26 914 return xencomm_privcmd_event_channel_op(hypercall);
ian@26 915 case __HYPERVISOR_hvm_op:
ian@26 916 return xencomm_privcmd_hvm_op(hypercall);
ian@26 917 case __HYPERVISOR_sched_op:
ian@26 918 return xencomm_privcmd_sched_op(hypercall);
alex@43 919 case __HYPERVISOR_ia64_dom0vp_op:
alex@43 920 return xencomm_privcmd_ia64_dom0vp_op(hypercall);
alex@212 921 case __HYPERVISOR_ia64_debug_op:
alex@212 922 return xencomm_privcmd_ia64_debug_op(hypercall);
yamahata@633 923 case __HYPERVISOR_physdev_op:
yamahata@633 924 return xencomm_privcmd_ia64_physdev_op(hypercall);
ian@26 925 default:
ian@26 926 printk("%s: unknown hcall (%ld)\n", __func__, hypercall->op);
ian@26 927 return -ENOSYS;
ian@26 928 }
ian@26 929 }
ian@26 930