/* * linux/kernel/vserver/context.c * * Virtual Server: Context Support * * Copyright (C) 2003-2004 Herbert Pötzl * * V0.01 context helper * V0.02 vx_ctx_kill syscall command * V0.03 replaced context_info calls * V0.04 redesign of struct (de)alloc * V0.05 rlimit basic implementation * V0.06 task_xid and info commands * */ #include //#include #include #include #include //#include #include //#include #include #include //#include /* system functions */ LIST_HEAD(vx_infos); spinlock_t vxlist_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; /* * struct vx_info allocation and deallocation */ static struct vx_info *alloc_vx_info(int id) { struct vx_info *new = NULL; int lim; vxdprintk("alloc_vx_info(%d)\n", id); /* would this benefit from a slab cache? */ new = kmalloc(sizeof(struct vx_info), GFP_KERNEL); if (!new) return 0; memset (new, 0, sizeof(struct vx_info)); new->vx_id = id; INIT_LIST_HEAD(&new->vx_list); /* rest of init goes here */ for (lim=0; limlimit.rlim[lim] = RLIM_INFINITY; atomic_set(&new->limit.ticks, current->counter); new->virt.nr_threads = 1; new->virt.bias_cswtch = kstat.context_swtch; new->virt.bias_jiffies = jiffies; new->virt.bias_idle = init_tasks[0]->times.tms_utime + init_tasks[0]->times.tms_stime; down_read(&uts_sem); new->virt.utsname = system_utsname; up_read(&uts_sem); vxdprintk("alloc_vx_info(%d) = %p\n", id, new); return new; } void free_vx_info(struct vx_info *vxi) { vxdprintk("free_vx_info(%p)\n", vxi); kfree(vxi); } /* * struct vx_info search by id * assumes vxlist_lock is held */ static __inline__ struct vx_info *__find_vx_info(int id) { struct vx_info *vxi; list_for_each_entry(vxi, &vx_infos, vx_list) if (vxi->vx_id == id) return vxi; return 0; } /* * struct vx_info ref stuff */ struct vx_info *find_vx_info(int id) { struct vx_info *vxi; spin_lock(&vxlist_lock); if ((vxi = __find_vx_info(id))) get_vx_info(vxi); spin_unlock(&vxlist_lock); return vxi; } /* * struct vx_info search by id * assumes vxlist_lock is held */ static __inline__ xid_t __vx_dynamic_id(void) { static xid_t seq = MAX_S_CONTEXT; xid_t barrier = seq; do { if (++seq > MAX_S_CONTEXT) seq = MIN_D_CONTEXT; if (!__find_vx_info(seq)) return seq; } while (barrier != seq); return 0; } struct vx_info *find_or_create_vx_info(int id) { struct vx_info *new, *vxi = NULL; vxdprintk("find_or_create_vx_info(%d)\n", id); if (!(new = alloc_vx_info(id))) return 0; spin_lock(&vxlist_lock); /* dynamic context requested */ if (id == VX_DYNAMIC_ID) { id = __vx_dynamic_id(); if (!id) { printk(KERN_ERR "no dynamic context available.\n"); goto out_unlock; } new->vx_id = id; } /* existing context requested */ else if ((vxi = __find_vx_info(id))) { vxdprintk("find_or_create_vx_info(%d) = %p (found)\n", id, vxi); get_vx_info(vxi); goto out_unlock; } /* new context requested */ vxdprintk("find_or_create_vx_info(%d) = %p (new)\n", id, vxi); atomic_set(&new->vx_refcount, 1); list_add(&new->vx_list, &vx_infos); vxi = new, new = NULL; out_unlock: spin_unlock(&vxlist_lock); if (new) free_vx_info(new); return vxi; } #include int vc_task_xid(uint32_t id, void *data) { xid_t xid; if (id) { struct task_struct *tsk; if (!vx_check(0, VX_ADMIN|VX_WATCH)) return -EPERM; read_lock(&tasklist_lock); tsk = find_task_by_pid(id); xid = (tsk) ? tsk->xid : -ESRCH; read_unlock(&tasklist_lock); } else xid = current->xid; return xid; } int vc_vx_info(uint32_t id, void *data) { struct vx_info *vxi; struct vcmd_vx_info_v0 vc_data; if (!vx_check(0, VX_ADMIN)) return -ENOSYS; if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RESOURCE)) return -EPERM; vxi = find_vx_info(id); if (!vxi) return -ESRCH; if (copy_to_user (data, &vc_data, sizeof(vc_data))) return -EFAULT; return 0; } /* virtual host info names */ static char * vx_vhi_name(struct vx_info *vxi, int id) { switch (id) { case VHIN_CONTEXT: return vxi->vx_name; case VHIN_SYSNAME: return vxi->virt.utsname.sysname; case VHIN_NODENAME: return vxi->virt.utsname.nodename; case VHIN_RELEASE: return vxi->virt.utsname.release; case VHIN_VERSION: return vxi->virt.utsname.version; case VHIN_MACHINE: return vxi->virt.utsname.machine; case VHIN_DOMAINNAME: return vxi->virt.utsname.domainname; default: } return NULL; } int vc_set_vhi_name(uint32_t id, void *data) { struct vx_info *vxi; struct vcmd_vx_vhi_name_v0 vc_data; char *name; if (!vx_check(0, VX_ADMIN)) return -ENOSYS; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (copy_from_user (&vc_data, data, sizeof(vc_data))) return -EFAULT; vxi = find_vx_info(id); if (!vxi) return -ESRCH; name = vx_vhi_name(vxi, vc_data.field); if (name) memcpy(name, vc_data.name, 65); put_vx_info(vxi); return (name ? 0 : -EFAULT); } int vc_get_vhi_name(uint32_t id, void *data) { struct vx_info *vxi; struct vcmd_vx_vhi_name_v0 vc_data; char *name; if (!vx_check(0, VX_ADMIN)) return -ENOSYS; if (copy_from_user (&vc_data, data, sizeof(vc_data))) return -EFAULT; vxi = find_vx_info(id); if (!vxi) return -ESRCH; name = vx_vhi_name(vxi, vc_data.field); if (!name) goto out_put; memcpy(vc_data.name, name, 65); if (copy_to_user (data, &vc_data, sizeof(vc_data))) return -EFAULT; out_put: put_vx_info(vxi); return (name ? 0 : -EFAULT); } #include int vc_enter_namespace(uint32_t id, void *data) { struct vx_info *vxi; struct namespace *old_ns; if (!vx_check(0, VX_ADMIN)) return -ENOSYS; vxi = find_vx_info(id); if (!vxi) return -ESRCH; if (!vxi->vx_namespace) goto out_put; old_ns = current->namespace; get_namespace(vxi->vx_namespace); current->namespace = vxi->vx_namespace; put_namespace(old_ns); out_put: put_vx_info(vxi); return 0; }