|  | /* | 
|  | *  Copyright (C) 2000-2009, Parallels, Inc. All rights reserved. | 
|  | * | 
|  | *  This program is free software; you can redistribute it and/or modify | 
|  | *  it under the terms of the GNU General Public License as published by | 
|  | *  the Free Software Foundation; either version 2 of the License, or | 
|  | *  (at your option) any later version. | 
|  | * | 
|  | *  This program is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | *  GNU General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU General Public License | 
|  | *  along with this program; if not, write to the Free Software | 
|  | *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <errno.h> | 
|  | #include <unistd.h> | 
|  | #include <fcntl.h> | 
|  | #include <string.h> | 
|  | #include <limits.h> | 
|  |  | 
|  | #include "types.h" | 
|  | #include "ub.h" | 
|  | #include "env.h" | 
|  | #include "vzctl_param.h" | 
|  | #include "vzerror.h" | 
|  | #include "logger.h" | 
|  | #include "vzsyscalls.h" | 
|  | #include "util.h" | 
|  |  | 
|  |  | 
|  | static struct ubname2id { | 
|  | char *name; | 
|  | unsigned int id; | 
|  | } ubname2id[] = { | 
|  | {"KMEMSIZE",	PARAM_KMEMSIZE}, | 
|  | {"LOCKEDPAGES",	PARAM_LOCKEDPAGES}, | 
|  | {"PRIVVMPAGES",	PARAM_PRIVVMPAGES}, | 
|  | {"SHMPAGES",	PARAM_SHMPAGES}, | 
|  | {"NUMPROC",	PARAM_NUMPROC}, | 
|  | {"PHYSPAGES",	PARAM_PHYSPAGES}, | 
|  | {"VMGUARPAGES",	PARAM_VMGUARPAGES}, | 
|  | {"OOMGUARPAGES",PARAM_OOMGUARPAGES}, | 
|  | {"NUMTCPSOCK",	PARAM_NUMTCPSOCK}, | 
|  | {"NUMFLOCK",	PARAM_NUMFLOCK}, | 
|  | {"NUMPTY",	PARAM_NUMPTY}, | 
|  | {"NUMSIGINFO",	PARAM_NUMSIGINFO}, | 
|  | {"TCPSNDBUF",	PARAM_TCPSNDBUF}, | 
|  | {"TCPRCVBUF",	PARAM_TCPRCVBUF}, | 
|  | {"OTHERSOCKBUF",PARAM_OTHERSOCKBUF}, | 
|  | {"DGRAMRCVBUF",	PARAM_DGRAMRCVBUF}, | 
|  | {"NUMOTHERSOCK",PARAM_NUMOTHERSOCK}, | 
|  | {"NUMFILE",	PARAM_NUMFILE}, | 
|  | {"DCACHESIZE",	PARAM_DCACHESIZE}, | 
|  | {"NUMIPTENT",	PARAM_NUMIPTENT}, | 
|  | {"AVNUMPROC",	PARAM_AVNUMPROC}, | 
|  | {"SWAPPAGES",	PARAM_SWAPPAGES}, | 
|  | {NULL, 0}, | 
|  | }; | 
|  |  | 
|  | /** Check that all required UBC parameters are specified. | 
|  | * | 
|  | * @param ub		UBC parameters. | 
|  | * @return		0 on success. | 
|  | */ | 
|  | int check_ub(vps_handler *h, ub_param *ub) | 
|  | { | 
|  | int ret = 0; | 
|  |  | 
|  | #define CHECK_UB(name)							\ | 
|  | if (ub->name == NULL) {							\ | 
|  | logger(-1, 0, "Error: required UB parameter " #name " not set");\ | 
|  | ret = VZ_NOTENOUGHUBCPARAMS;					\ | 
|  | } | 
|  |  | 
|  | if (is_vswap_config(ub)) | 
|  | { | 
|  | CHECK_UB(physpages); | 
|  | CHECK_UB(swappages); | 
|  | if (is_vz_kernel(h) && !is_vswap_mode()) { | 
|  | logger(-1, 0, "Error: detected vswap CT config but " | 
|  | "kernel does not support vswap"); | 
|  | logger(-1, 0, "This means either old kernel " | 
|  | "or bad config (physpages NOT set " | 
|  | "to 0:unlimited)"); | 
|  | ret = VZ_BAD_KERNEL; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  | /* else | 
|  | * non-vswap case, require all params except swappages */ | 
|  | CHECK_UB(kmemsize) | 
|  | CHECK_UB(lockedpages) | 
|  | CHECK_UB(privvmpages) | 
|  | CHECK_UB(shmpages) | 
|  | CHECK_UB(numproc) | 
|  | CHECK_UB(physpages) | 
|  | CHECK_UB(vmguarpages) | 
|  | CHECK_UB(oomguarpages) | 
|  | CHECK_UB(numtcpsock) | 
|  | CHECK_UB(numflock) | 
|  | CHECK_UB(numpty) | 
|  | CHECK_UB(numsiginfo) | 
|  | CHECK_UB(tcpsndbuf) | 
|  | CHECK_UB(tcprcvbuf) | 
|  | CHECK_UB(othersockbuf) | 
|  | CHECK_UB(dgramrcvbuf) | 
|  | CHECK_UB(numothersock) | 
|  | CHECK_UB(numfile) | 
|  | CHECK_UB(dcachesize) | 
|  | CHECK_UB(numiptent) | 
|  | #undef CHECK_UB | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | inline static int is_ub_empty(ub_param *ub) | 
|  | { | 
|  | #define CHECK_UB(name)	if (ub->name != NULL) return 0; | 
|  |  | 
|  | CHECK_UB(kmemsize) | 
|  | CHECK_UB(lockedpages) | 
|  | CHECK_UB(privvmpages) | 
|  | CHECK_UB(shmpages) | 
|  | CHECK_UB(numproc) | 
|  | CHECK_UB(physpages) | 
|  | CHECK_UB(vmguarpages) | 
|  | CHECK_UB(oomguarpages) | 
|  | CHECK_UB(numtcpsock) | 
|  | CHECK_UB(numflock) | 
|  | CHECK_UB(numpty) | 
|  | CHECK_UB(numsiginfo) | 
|  | CHECK_UB(tcpsndbuf) | 
|  | CHECK_UB(tcprcvbuf) | 
|  | CHECK_UB(othersockbuf) | 
|  | CHECK_UB(dgramrcvbuf) | 
|  | CHECK_UB(numothersock) | 
|  | CHECK_UB(numfile) | 
|  | CHECK_UB(dcachesize) | 
|  | CHECK_UB(numiptent) | 
|  | CHECK_UB(swappages) | 
|  | #undef CHECK_UB | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int get_ub_resid(char *name) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; ubname2id[i].name != NULL; i++) | 
|  | if (!strcasecmp(name, ubname2id[i].name)) | 
|  | return ubname2id[i].id; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | #ifdef VZ_KERNEL_SUPPORTED | 
|  | static inline int setublimit(uid_t uid, unsigned long resource, | 
|  | const unsigned long *rlim) | 
|  | { | 
|  | return syscall(__NR_setublimit, uid, resource, rlim); | 
|  | } | 
|  |  | 
|  | static const char *get_ub_name(unsigned int res_id) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; ubname2id[i].name != NULL; i++) | 
|  | if (ubname2id[i].id == res_id) | 
|  | return ubname2id[i].name; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | int set_ublimit(vps_handler *h, envid_t veid, ub_param *ub) | 
|  | { | 
|  | int vswap = is_vswap_config(ub); | 
|  | const unsigned long unlim_ub[2] = {LONG_MAX, LONG_MAX}; | 
|  | const unsigned long *res; | 
|  |  | 
|  | #define SET_UB_LIMIT(name, id)						\ | 
|  | res = ub->name;								\ | 
|  | if (vswap && !res)							\ | 
|  | res = unlim_ub;							\ | 
|  | if (res != NULL) {							\ | 
|  | if (setublimit(veid, id, res)) {				\ | 
|  | logger(-1, errno, "setublimit %s %lu:%lu failed",	\ | 
|  | get_ub_name(id), res[0], res[1]);		\ | 
|  | return VZ_SETUBC_ERROR;					\ | 
|  | }								\ | 
|  | } | 
|  |  | 
|  | SET_UB_LIMIT(kmemsize, UB_KMEMSIZE) | 
|  | SET_UB_LIMIT(lockedpages, UB_LOCKEDPAGES) | 
|  | SET_UB_LIMIT(privvmpages, UB_PRIVVMPAGES) | 
|  | SET_UB_LIMIT(shmpages, UB_SHMPAGES) | 
|  | SET_UB_LIMIT(numproc, UB_NUMPROC) | 
|  | SET_UB_LIMIT(physpages, UB_PHYSPAGES) | 
|  | SET_UB_LIMIT(vmguarpages, UB_VMGUARPAGES) | 
|  | SET_UB_LIMIT(oomguarpages, UB_OOMGUARPAGES) | 
|  | SET_UB_LIMIT(numtcpsock, UB_NUMTCPSOCK) | 
|  | SET_UB_LIMIT(numflock, UB_NUMFLOCK) | 
|  | SET_UB_LIMIT(numpty, UB_NUMPTY) | 
|  | SET_UB_LIMIT(numsiginfo, UB_NUMSIGINFO) | 
|  | SET_UB_LIMIT(tcpsndbuf, UB_TCPSNDBUF ) | 
|  | SET_UB_LIMIT(tcprcvbuf, UB_TCPRCVBUF) | 
|  | SET_UB_LIMIT(othersockbuf, UB_OTHERSOCKBUF) | 
|  | SET_UB_LIMIT(dgramrcvbuf, UB_DGRAMRCVBUF) | 
|  | SET_UB_LIMIT(numothersock, UB_NUMOTHERSOCK) | 
|  | SET_UB_LIMIT(numfile, UB_NUMFILE) | 
|  | SET_UB_LIMIT(dcachesize, UB_DCACHESIZE) | 
|  | SET_UB_LIMIT(numiptent, UB_IPTENTRIES) | 
|  | if (ub->swappages && | 
|  | setublimit(veid, UB_SWAPPAGES, ub->swappages) == -1) | 
|  | { | 
|  | if (errno == EINVAL) { | 
|  | logger(-1, ENOSYS, "failed to set swappages"); | 
|  | } else { | 
|  | logger(-1, errno, "failed to set swappages"); | 
|  | return VZ_SETUBC_ERROR; | 
|  | } | 
|  | } | 
|  | #undef SET_UB_LIMIT | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /** Apply UBC resources. | 
|  | * | 
|  | * @param h		CT handler. | 
|  | * @param veid		CT ID. | 
|  | * @param ubc		UBC parameters | 
|  | * @return		0 on success | 
|  | */ | 
|  | int vps_set_ublimit(vps_handler *h, envid_t veid, ub_param *ub) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | if (is_ub_empty(ub)) | 
|  | return 0; | 
|  | if (!vps_is_run(h, veid)) { | 
|  | logger(-1, 0, "Unable to apply UBC parameters: " | 
|  | "container is not running"); | 
|  | return VZ_VE_NOT_RUNNING; | 
|  | } | 
|  | if ((ret = h->setlimits(h, veid, ub))) | 
|  | return ret; | 
|  | logger(0, 0, "UB limits were set successfully"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void free_ub_param(ub_param *ub) | 
|  | { | 
|  | #define FREE_P(x) free(ub->x); ub->x = NULL; | 
|  | if (ub == NULL) | 
|  | return; | 
|  | FREE_P(kmemsize) | 
|  | FREE_P(lockedpages) | 
|  | FREE_P(privvmpages) | 
|  | FREE_P(shmpages) | 
|  | FREE_P(numproc) | 
|  | FREE_P(physpages) | 
|  | FREE_P(vmguarpages) | 
|  | FREE_P(oomguarpages) | 
|  | FREE_P(numtcpsock) | 
|  | FREE_P(numflock) | 
|  | FREE_P(numpty) | 
|  | FREE_P(numsiginfo) | 
|  | FREE_P(tcpsndbuf) | 
|  | FREE_P(tcprcvbuf) | 
|  | FREE_P(othersockbuf) | 
|  | FREE_P(dgramrcvbuf) | 
|  | FREE_P(numothersock) | 
|  | FREE_P(numfile) | 
|  | FREE_P(dcachesize) | 
|  | FREE_P(numiptent) | 
|  | FREE_P(avnumproc) | 
|  | FREE_P(swappages) | 
|  | #undef FREE_P | 
|  | } | 
|  |  | 
|  | void add_ub_limit(struct ub_struct *ub, int res_id, unsigned long *limit) | 
|  | { | 
|  | #define ADD_UB_PARAM(name, id)					\ | 
|  | if (res_id == id) {						\ | 
|  | ub->name = limit;					\ | 
|  | return;							\ | 
|  | } | 
|  |  | 
|  | ADD_UB_PARAM(kmemsize, PARAM_KMEMSIZE) | 
|  | ADD_UB_PARAM(lockedpages, PARAM_LOCKEDPAGES) | 
|  | ADD_UB_PARAM(privvmpages, PARAM_PRIVVMPAGES) | 
|  | ADD_UB_PARAM(shmpages, PARAM_SHMPAGES) | 
|  | ADD_UB_PARAM(numproc, PARAM_NUMPROC) | 
|  | ADD_UB_PARAM(physpages, PARAM_PHYSPAGES) | 
|  | ADD_UB_PARAM(vmguarpages, PARAM_VMGUARPAGES) | 
|  | ADD_UB_PARAM(oomguarpages, PARAM_OOMGUARPAGES) | 
|  | ADD_UB_PARAM(numtcpsock, PARAM_NUMTCPSOCK) | 
|  | ADD_UB_PARAM(numflock, PARAM_NUMFLOCK) | 
|  | ADD_UB_PARAM(numpty, PARAM_NUMPTY) | 
|  | ADD_UB_PARAM(numsiginfo, PARAM_NUMSIGINFO) | 
|  | ADD_UB_PARAM(tcpsndbuf, PARAM_TCPSNDBUF) | 
|  | ADD_UB_PARAM(tcprcvbuf, PARAM_TCPRCVBUF) | 
|  | ADD_UB_PARAM(othersockbuf, PARAM_OTHERSOCKBUF) | 
|  | ADD_UB_PARAM(dgramrcvbuf, PARAM_DGRAMRCVBUF) | 
|  | ADD_UB_PARAM(numothersock, PARAM_NUMOTHERSOCK) | 
|  | ADD_UB_PARAM(numfile, PARAM_NUMFILE) | 
|  | ADD_UB_PARAM(dcachesize, PARAM_DCACHESIZE) | 
|  | ADD_UB_PARAM(numiptent, PARAM_NUMIPTENT) | 
|  | ADD_UB_PARAM(avnumproc, PARAM_AVNUMPROC) | 
|  | ADD_UB_PARAM(swappages, PARAM_SWAPPAGES) | 
|  | #undef ADD_UB_PARAM | 
|  | } | 
|  |  | 
|  | /** Add UBC resource in ub_res format to UBC struct | 
|  | * | 
|  | * @param ub		UBC parameters. | 
|  | * @param res		UBC resource in ub_res format. | 
|  | * @return		0 on success. | 
|  | */ | 
|  | int add_ub_param(ub_param *ub, ub_res *res) | 
|  | { | 
|  | unsigned long *limit; | 
|  |  | 
|  | limit = malloc(sizeof(*limit) * 2); | 
|  | if (limit == NULL) | 
|  | return ERR_NOMEM; | 
|  | limit[0] = res->limit[0]; | 
|  | limit[1] = res->limit[1]; | 
|  |  | 
|  | add_ub_limit(ub, res->res_id, limit); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void merge_ub(ub_param *dst, ub_param *src) | 
|  | { | 
|  | #define MERGE_P2(x)						\ | 
|  | if ((src->x) != NULL) {						\ | 
|  | if ((dst->x) == NULL)					\ | 
|  | dst->x = malloc(sizeof(*(dst->x)) * 2);		\ | 
|  | dst->x[0] = src->x[0];					\ | 
|  | dst->x[1] = src->x[1];					\ | 
|  | } | 
|  |  | 
|  | MERGE_P2(kmemsize) | 
|  | MERGE_P2(lockedpages) | 
|  | MERGE_P2(privvmpages) | 
|  | MERGE_P2(shmpages) | 
|  | MERGE_P2(numproc) | 
|  | MERGE_P2(physpages) | 
|  | MERGE_P2(vmguarpages) | 
|  | MERGE_P2(oomguarpages) | 
|  | MERGE_P2(numtcpsock) | 
|  | MERGE_P2(numflock) | 
|  | MERGE_P2(numpty) | 
|  | MERGE_P2(numsiginfo) | 
|  | MERGE_P2(tcpsndbuf) | 
|  | MERGE_P2(tcprcvbuf) | 
|  | MERGE_P2(othersockbuf) | 
|  | MERGE_P2(dgramrcvbuf) | 
|  | MERGE_P2(numothersock) | 
|  | MERGE_P2(numfile) | 
|  | MERGE_P2(dcachesize) | 
|  | MERGE_P2(numiptent) | 
|  | MERGE_P2(avnumproc) | 
|  | MERGE_P2(swappages) | 
|  | #undef MERGE_P2 | 
|  | } | 
|  |  | 
|  | /** Read UBC resources current usage from /proc/user_beancounters | 
|  | * | 
|  | * @param veid		CT ID. | 
|  | * @param ub		UBC parameters. | 
|  | * @return		0 on success. | 
|  | */ | 
|  | int vps_read_ubc(envid_t veid, ub_param *ub) | 
|  | { | 
|  | FILE *fd; | 
|  | char str[STR_SIZE]; | 
|  | char name[64]; | 
|  | const char *fmt = NULL; /* make gcc happy */ | 
|  | int ret, found; | 
|  | envid_t id; | 
|  | unsigned long held, maxheld, barrier, limit; | 
|  | ub_res res; | 
|  |  | 
|  | fd = fopen(PROCUBC, "r"); | 
|  | if (fd == NULL) { | 
|  | logger(-1, errno, "Unable to open " PROCUBC); | 
|  | return -1; | 
|  | } | 
|  | found = 0; | 
|  | while (fgets(str, sizeof(str), fd)) { | 
|  | if ((ret = sscanf(str, "%d:", &id)) == 1) { | 
|  | if (id == veid) { | 
|  | fmt =  "%*lu:%s%lu%lu%lu%lu"; | 
|  | found = 1; | 
|  | } else { | 
|  | if (found) | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | fmt = "%s%lu%lu%lu%lu"; | 
|  | } | 
|  | if (!found) | 
|  | continue; | 
|  | if ((ret = sscanf(str, fmt, | 
|  | name, &held, &maxheld, &barrier, &limit)) != 5) | 
|  | { | 
|  | continue; | 
|  | } | 
|  | if ((res.res_id = get_ub_resid(name)) >= 0) { | 
|  | res.limit[0] = held; | 
|  | res.limit[1] = held; | 
|  | add_ub_param(ub, &res); | 
|  | } | 
|  | } | 
|  | fclose(fd); | 
|  | return !found; | 
|  | } | 
|  |  | 
|  | /* If you want to modify this function, don't forget print_vswap() */ | 
|  | int is_vswap_config(const ub_param *param) | 
|  | { | 
|  | /* Dirty hack: treat INT_MAX (i.e. 32 bit LONG_MAX) as unlimited. | 
|  | * Required for old CTs migrated from 32 bit to 64 bit host. | 
|  | */ | 
|  | return  (param != NULL) && | 
|  | (param->physpages != NULL) && | 
|  | (param->physpages[1] != LONG_MAX) && | 
|  | (param->physpages[1] != INT_MAX); | 
|  | } |