| /* |
| * Copyright (C) 2000-2005 SWsoft. All rights reserved. |
| * |
| * This file may be distributed under the terms of the Q Public License |
| * as defined by Trolltech AS of Norway and appearing in the file |
| * LICENSE.QPL included in the packaging of this file. |
| * |
| * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE |
| * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/vfs.h> |
| #include <unistd.h> |
| |
| #define SYSRSRV 52428800 |
| #define MEMPERVE 5542912 |
| #define LOWPERVE 1348608 |
| #define MAXVAL LONG_MAX |
| #define DEF_CPUUNITS 1000 |
| #define CHECK_LIMIT(val) (val > MAXVAL) ? MAXVAL: val |
| |
| #define PROCMEM "/proc/meminfo" |
| #define PROCTHREADS "/proc/sys/kernel/threads-max" |
| #define OUTPATH "/etc/sysconfig/vz-scripts/" |
| #define PROCCPU "/proc/cpuinfo" |
| |
| #define MAX_SL 3 |
| |
| /* maximal per system values */ |
| #define MAX_TOTAL_PIDS 8000 |
| #define MAXIPTENT 2000 |
| |
| /* minimal per VE values */ |
| #define MINPROC 30 |
| #define MINTCPBUF 65536 |
| #define MINDGRAMBUF 32768 |
| #define MINSOCKBUF 2560 |
| #define MIN_PTY 4 |
| #define MIN_IPTENT 4 |
| #define MIN_PGLOCK 4 |
| #define MIN_GUARPG 1024 |
| |
| /* maximal per VE values */ |
| #define PRIVVM_PVE 0.6 / 4096 |
| #define IPTENT_PVE 200 |
| #define FLOCK_PVE 1000 |
| #define PTY_PVE 512 |
| |
| /* default discspace values */ |
| #define HOST_DS 10737418240ULL /* 10 GB */ |
| #define DEF_DS 225280 |
| #define HOST_DI 100000 |
| #define DEF_DI 88000 |
| |
| /* helper values */ |
| #define SHMPG_PRIVVM 0.10 |
| #define NFLOCK_NFILE 0.10 |
| #define NFILE_AVNPROC 32 |
| #define PTY_PROC 0.1 |
| #define PGLOCK_KMEM 204800 /* 2% KMEM */ |
| #define K_KMEM_MAX 2.0 |
| |
| /* limit/barrier delta values */ |
| #define KMEM_DELTA 1.1 |
| #define NFLOCK_DELTA 1.1 |
| #define PRIVVM_DELTA 1.1 |
| #define DCACHE_DELTA 1.03 |
| #define DS_DELTA 1.1 |
| #define DI_DELTA 1.1 |
| |
| #define NUMUBC 23 |
| |
| enum { NPROC = 0, |
| AVNUMPROC, |
| NTCPSOCK, |
| NOTHSOCK, |
| VMGUAR, |
| KMEM, |
| TCPSND, |
| TCPRCV, |
| SOCKOTH, |
| DGRAM, |
| OOMGUAR, |
| PRIVVMPG, |
| LOCKPG, |
| SHMPG, |
| NPHYPG, |
| NFILE, |
| NFLOCK, |
| NPTY, |
| NSIGINFO, |
| DCACHE, |
| IPTENT, |
| DISKSPACE, |
| DISKINODES |
| }; |
| |
| char *ubcnames[] = { |
| "NUMPROC", |
| "AVNUMPROC", |
| "NUMTCPSOCK", |
| "NUMOTHERSOCK", |
| "VMGUARPAGES", |
| "KMEMSIZE", |
| "TCPSNDBUF", |
| "TCPRCVBUF", |
| "OTHERSOCKBUF", |
| "DGRAMRCVBUF", |
| "OOMGUARPAGES", |
| "PRIVVMPAGES", |
| "LOCKEDPAGES", |
| "SHMPAGES", |
| "PHYSPAGES", |
| "NUMFILE", |
| "NUMFLOCK", |
| "NUMPTY", |
| "NUMSIGINFO", |
| "DCACHESIZE", |
| "NUMIPTENT", |
| "DISKSPACE", |
| "DISKINODES", |
| NULL}; |
| |
| struct par_limits { |
| unsigned long long bar; |
| unsigned long long lim; |
| }; |
| |
| struct par_limits params[NUMUBC]; |
| |
| /* Global variables */ |
| unsigned long long mem_total, low_total, swap_total, ds_total, di_total; |
| long pagesize, proc_calc; |
| int num_ve, osl; |
| |
| float k_kmem[MAX_SL] = {1, 1.2, 1.8}; |
| float k_nproc[MAX_SL] = {1, 1.5, 2}; |
| float k_avnpr[MAX_SL] = {8, 5, 3}; |
| int k_kpp[MAX_SL] = {81920, 65536, 53248}; |
| int k_sock1[MAX_SL] = {262144, 131072, 65536}; |
| int k_sock2[MAX_SL] = {4096, 3036, 2560}; |
| int numsiginfo[MAX_SL] = {1024, 512, 256}; |
| float k_dcache[MAX_SL] = {1.5 * 384, 1.2 * 384, 1 * 384}; |
| float k_privvm[MAX_SL] = {6, 3, 1.5}; |
| int k_pglock[MAX_SL] = {10, 3, 1}; |
| int k_msl[MAX_SL] = {10485760, 2097152, 0}; |
| |
| char *level_string[MAX_SL+1] = { |
| "Free resource distribution. Any parameters may be increased\0", |
| "Normal resource distribution. Secondary parameters may be increased\0", |
| "Partial resource shortage. Auxiliary parameters may be increased\0", |
| "Overall resource shortage. Please, do not change any parameters!\0" |
| }; |
| |
| void usage() |
| { |
| fprintf(stderr, "Usage: vzsplit [-f config_name] " |
| "| [-n numves] | [-s swap_size\n" |
| "\t -f specified config name\n" |
| "\t -n specified number of VPS\n" |
| "\t -s specified swap in Kbytes\n"); |
| } |
| |
| void header(FILE *fp) |
| { |
| fprintf(fp,"# Configuration file generated by vzsplit for %d VPS\n" |
| "# on HN with total amount of physical mem %llu Mb\n" |
| "# low memory %llu Mb, swap size %llu Mb, Max treads %lu\n" |
| "# Resourse commit level %d:\n# %s\n", |
| num_ve, (mem_total >> 20), (low_total >> 20), (swap_total >> 20), |
| proc_calc, osl, level_string[osl]); |
| return; |
| } |
| |
| int get_cpupower(int *cpuunits) |
| { |
| FILE *fd; |
| char str[1024]; |
| int val, total = 0; |
| |
| *cpuunits = DEF_CPUUNITS; |
| if ((fd = fopen(PROCCPU, "r")) == NULL) { |
| fprintf(stderr, "Cannot open " PROCCPU "\n"); |
| return 1; |
| } |
| while (fgets(str, sizeof(str), fd)) |
| if (sscanf(str, "bogomips\t: %u", &val) == 1) |
| total += val; |
| if (total) { |
| total *= 25; |
| *cpuunits = total / (num_ve + 1); |
| return 0; |
| } |
| return 1; |
| } |
| |
| int lconv(char *name) |
| { |
| int i, cpuunits; |
| FILE *fp; |
| |
| if (name != NULL) { |
| if ((fp = fopen(name, "w")) == NULL) { |
| fprintf(stderr, "Unable to create %s: %s\n", name, |
| strerror(errno)); |
| return 1; |
| } |
| } else { |
| fp = stdout; |
| } |
| header(fp); |
| |
| fprintf(fp, "# Primary parameters\n"); |
| for (i = 0; i < NUMUBC; i++) { |
| if (i == KMEM) |
| fprintf(fp, "\n# Secondary parameters\n"); |
| else if (i ==LOCKPG) |
| fprintf(fp, "\n# Auxiliary parameters\n"); |
| fprintf(fp, "%s=\"%lu:%lu\"\n", ubcnames[i], |
| (unsigned long) params[i].bar, |
| (unsigned long) params[i].lim); |
| } |
| get_cpupower(&cpuunits); |
| fprintf(fp, "CPUUNITS=\"%d\"\n", cpuunits); |
| if (name) { |
| fclose(fp); |
| fprintf(stderr, "Config %s was created\n", name); |
| } |
| return 0; |
| } |
| |
| int calculate_values() |
| { |
| long long rest, delta, low_pve, tot_pve; |
| long long kmem, numproc, avnproc, sockbuf, tcpbuf, iptent, numfile; |
| long long dcache, numflock; |
| long long lockedpages, numpty, guarpg, privvm, shmpg, di_pve, ds_pve; |
| int sl; |
| |
| tot_pve = (mem_total + swap_total - SYSRSRV) / num_ve; |
| for (osl = 0; osl < MAX_SL; osl++) { |
| numproc = k_nproc[osl] * proc_calc / num_ve; |
| low_pve = low_total * 0.4 * k_kmem[osl] / num_ve; |
| guarpg = (tot_pve - low_pve) / pagesize; |
| if (guarpg < MIN_GUARPG) { |
| guarpg = MIN_GUARPG; |
| low_pve = tot_pve - guarpg * pagesize; |
| } |
| kmem = low_pve / 2; |
| if (low_pve < k_msl[osl]) |
| continue; |
| if (numproc < MINPROC) |
| numproc = MINPROC; |
| avnproc = kmem / k_kpp[osl]; |
| if (avnproc < MINPROC / 2) |
| continue; |
| |
| if (numproc < avnproc) |
| numproc = avnproc; |
| if ((numproc < 2 * avnproc) && (osl < MAX_SL)) |
| numproc = 2 * avnproc; |
| if (numproc > avnproc * k_avnpr[osl]) |
| numproc = avnproc * k_avnpr[osl]; |
| |
| sockbuf = low_pve - kmem; |
| tcpbuf = sockbuf / 3; |
| if (tcpbuf < k_sock1[osl] + k_sock2[osl] * numproc) |
| continue; |
| |
| sl = osl; |
| break; |
| } |
| |
| if (osl == MAX_SL) { |
| sl = osl - 1; |
| numproc = k_nproc[sl] * proc_calc / num_ve; |
| low_pve = low_total * 0.4 * K_KMEM_MAX / num_ve; |
| guarpg = (tot_pve - low_pve) / pagesize; |
| if (guarpg < MIN_GUARPG) { |
| guarpg = MIN_GUARPG; |
| low_pve = tot_pve - guarpg * pagesize; |
| } |
| do { |
| numproc /= 2; |
| if (numproc < MINPROC) |
| numproc = MINPROC; |
| avnproc = numproc / 2; |
| delta = MINSOCKBUF * numproc; |
| sockbuf = 2 * (MINDGRAMBUF + MINTCPBUF + delta); |
| kmem = low_pve - sockbuf; |
| rest = kmem - avnproc * k_kpp[sl]; |
| } while ((rest < 0) && (numproc > MINPROC)); |
| |
| if (rest < 0) { |
| fprintf(stderr, "Fatal resource shortage, " |
| "try to decrease the number of VPS\n"); |
| return 1; |
| } |
| params[DGRAM].lim = params[DGRAM].bar = MINDGRAMBUF; |
| params[SOCKOTH].bar = MINDGRAMBUF; |
| params[SOCKOTH].lim = MINDGRAMBUF + delta; |
| params[TCPSND].bar = MINTCPBUF; |
| params[TCPSND].lim = MINTCPBUF + delta; |
| params[TCPRCV].bar = MINTCPBUF; |
| if (rest > delta) { |
| params[TCPRCV].lim = MINTCPBUF + delta; |
| kmem += rest - delta; |
| } else |
| params[TCPRCV].lim = MINTCPBUF + rest; |
| } else { |
| delta = k_sock2[sl] * numproc; |
| rest = tcpbuf - delta; |
| params[TCPSND].lim = params[TCPRCV].lim = CHECK_LIMIT(tcpbuf); |
| params[TCPSND].bar = params[TCPRCV].bar = CHECK_LIMIT(rest); |
| params[DGRAM].lim = params[DGRAM].bar = |
| params[SOCKOTH].bar = CHECK_LIMIT(rest / 2); |
| params[SOCKOTH].lim = CHECK_LIMIT(rest / 2 + delta); |
| } |
| |
| params[KMEM].lim = CHECK_LIMIT(kmem * KMEM_DELTA); |
| params[KMEM].bar = CHECK_LIMIT(kmem); |
| params[NPROC].lim = params[NPROC].bar = CHECK_LIMIT(numproc); |
| params[AVNUMPROC].lim = params[AVNUMPROC].bar = CHECK_LIMIT(avnproc); |
| params[NTCPSOCK].lim = params[NTCPSOCK].bar = CHECK_LIMIT(numproc); |
| params[NOTHSOCK].lim = params[NOTHSOCK].bar = CHECK_LIMIT(numproc); |
| |
| iptent = MAXIPTENT / num_ve; |
| if (iptent > IPTENT_PVE) |
| iptent = IPTENT_PVE; |
| else if (iptent < MIN_IPTENT) |
| iptent = MIN_IPTENT; |
| |
| params[IPTENT].lim = params[IPTENT].bar = iptent; |
| |
| numfile = avnproc * NFILE_AVNPROC; |
| params[NFILE].lim = params[NFILE].bar = CHECK_LIMIT(numfile); |
| |
| dcache = numfile * k_dcache[sl]; |
| params[DCACHE].lim = CHECK_LIMIT(dcache); |
| params[DCACHE].bar = CHECK_LIMIT(dcache / DCACHE_DELTA); |
| |
| numflock = numfile * NFLOCK_NFILE; |
| if (numflock > FLOCK_PVE) |
| numflock = FLOCK_PVE; |
| params[NFLOCK].lim = CHECK_LIMIT(numflock * NFLOCK_DELTA); |
| params[NFLOCK].bar = numflock; |
| |
| lockedpages = kmem * k_pglock[sl] / PGLOCK_KMEM; |
| if (lockedpages < MIN_PGLOCK) |
| lockedpages = MIN_PGLOCK; |
| params[LOCKPG].lim = params[LOCKPG].bar = CHECK_LIMIT(lockedpages); |
| |
| numpty = numproc * PTY_PROC; |
| if (numpty < MIN_PTY) |
| numpty = MIN_PTY; |
| if (numpty > PTY_PVE) |
| numpty = PTY_PVE; |
| |
| params[NPTY].lim = params[NPTY].bar = numpty; |
| |
| params[NSIGINFO].lim = params[NSIGINFO].bar = numsiginfo[sl]; |
| params[NPHYPG].bar = 0; params[NPHYPG].lim = MAXVAL; |
| |
| privvm = guarpg * k_privvm[sl]; |
| if (privvm > PRIVVM_PVE * mem_total) { |
| privvm = PRIVVM_PVE * mem_total; |
| guarpg = privvm; |
| } |
| params[PRIVVMPG].bar = CHECK_LIMIT(privvm); |
| params[PRIVVMPG].lim = CHECK_LIMIT(privvm * PRIVVM_DELTA); |
| params[VMGUAR].bar = params[OOMGUAR].bar = CHECK_LIMIT(guarpg); |
| params[VMGUAR].lim = params[OOMGUAR].lim = MAXVAL; |
| |
| shmpg = privvm * SHMPG_PRIVVM; |
| params[SHMPG].bar = params[SHMPG].lim = CHECK_LIMIT(shmpg); |
| |
| if (ds_total == 0) { |
| ds_pve = DEF_DS; |
| di_pve = DEF_DI; |
| } else { |
| ds_pve = ds_total / (2 * num_ve); |
| di_pve = di_total / (2 * num_ve); |
| } |
| params[DISKSPACE].bar = ds_pve / DS_DELTA; |
| params[DISKSPACE].lim = ds_pve; |
| params[DISKINODES].bar = di_pve / DI_DELTA; |
| params[DISKINODES].lim = di_pve; |
| |
| return 0; |
| } |
| |
| int main(int argc, char **argv) |
| { |
| int len, opt, swp; |
| char *tail; |
| char *name = NULL; |
| struct stat st; |
| FILE *fd; |
| char str[1024]; |
| unsigned long long val; |
| struct statfs statfs_buf; |
| int retval, ve_allowed; |
| |
| num_ve = -1; |
| swp = 0; |
| mem_total = 0; |
| low_total = 0; |
| swap_total = 0; |
| |
| while ((opt = getopt(argc, argv, "f:n:s:h")) > 0) { |
| switch(opt) { |
| case 'f': |
| len = strlen(optarg) + strlen(OUTPATH) + |
| strlen("ve-.conf-sample"); |
| name = (char*)malloc(len + 1); |
| sprintf(name, OUTPATH "ve-%s.conf-sample", optarg); |
| if (!stat(name, &st)) { |
| fprintf(stderr,"File %s already exist\n", |
| name); |
| exit(1); |
| } |
| break; |
| case 'n': |
| num_ve = strtol(optarg, &tail, 10); |
| if (*tail != '\0') { |
| fprintf(stderr, "Invalid argument for -n: %s\n", |
| optarg); |
| exit(1); |
| } |
| break; |
| case 's': |
| if (optarg[0] == '-') { |
| fprintf(stderr, "Negative value for -s\n"); |
| exit(-1); |
| } |
| swp = 1; |
| swap_total = strtoll(optarg, &tail, 10); |
| swap_total <<= 10; |
| if (*tail != '\0') { |
| fprintf(stderr, "Invalid argument for -s: %s\n", |
| optarg); |
| exit(1); |
| } |
| break; |
| case 'h': |
| default : |
| usage(); |
| exit(0); |
| } |
| } |
| if (optind < argc) { |
| usage(); |
| exit(1); |
| } |
| if (num_ve == -1) { |
| printf("Enter the number of VPS: "); |
| if (scanf("%d", &num_ve) != 1) { |
| fprintf(stderr, "Invalid value.\n"); |
| exit(1); |
| } |
| } |
| if (num_ve < 1) { |
| fprintf(stderr, "Incorrect value for number of VPS.\n"); |
| exit(1); |
| } |
| |
| if ((fd = fopen(PROCMEM, "r")) == NULL) { |
| fprintf(stderr, "Cannot open " PROCMEM"\n"); |
| exit(1); |
| } |
| |
| while (fgets(str, sizeof(str), fd)) |
| if (sscanf(str, "MemTotal:\t %llu", &val) == 1) |
| mem_total = val << 10; |
| else if (sscanf(str, "LowTotal:\t %llu", &val) == 1) |
| low_total = val << 10; |
| else if (!swp && sscanf(str, "SwapTotal:\t %llu", &val) == 1) |
| swap_total = val << 10; |
| |
| fclose(fd); |
| if (mem_total < SYSRSRV) { |
| fprintf(stderr, "At least 128 Mb of RAM should be " |
| "installed on Hardware Node\n"); |
| exit(1); |
| } |
| |
| if (swap_total > 2 * mem_total) |
| fprintf(stderr, "The optimal swap space size is %llu Mb, " |
| "twice bigger than the RAM size\n\n", |
| (2 * mem_total) >> 20); |
| ve_allowed = num_ve; |
| retval = 0; |
| |
| if (((mem_total + swap_total - SYSRSRV) / num_ve) < MEMPERVE) { |
| fprintf(stderr, "On node with %llu Mb of memory (RAM + swap) " |
| "cannot be allocated %d VPS\n", |
| (mem_total + swap_total) >> 20, num_ve); |
| ve_allowed = (mem_total + swap_total - SYSRSRV) / MEMPERVE; |
| retval = 1; |
| } |
| |
| if (((low_total - SYSRSRV)/ ve_allowed) < LOWPERVE) { |
| int ve_low; |
| |
| fprintf(stderr, "On node with %llu Mb of Low Memory " |
| "cannot be allocated %d VPS\n", |
| low_total >> 20, num_ve); |
| ve_low = (low_total - SYSRSRV) / LOWPERVE; |
| if (ve_low < ve_allowed) |
| ve_allowed = ve_low; |
| retval = 1; |
| } |
| |
| if (retval != 0) { |
| fprintf(stderr, "The maximum allowed value is %d\n", ve_allowed); |
| exit(retval); |
| } |
| |
| if ((fd = fopen(PROCTHREADS, "r")) == NULL) { |
| fprintf(stderr, "Cannot open " PROCTHREADS "\n"); |
| exit(1); |
| } |
| if (fgets(str, sizeof(str), fd)) |
| if (sscanf(str, "%llu", &val) == 1) |
| proc_calc = val; |
| fclose(fd); |
| |
| if (statfs ("/vz", &statfs_buf) < 0) { |
| fprintf(stderr, "WARNING: statfs /vz return error\n"); |
| fprintf(stderr, "Therefore default disk values will be used\n\n"); |
| ds_total = 0; di_total = 0; |
| } else { |
| long ve_ds, ve_di; |
| long rec; |
| |
| ds_total = statfs_buf.f_blocks; |
| di_total = statfs_buf.f_files; |
| |
| rec = 0; |
| if (ds_total / 2 < HOST_DS / statfs_buf.f_bsize) { |
| rec = 1; |
| ds_total /= 2; |
| } else |
| ds_total -= HOST_DS / statfs_buf.f_bsize; |
| if (di_total / 2 < HOST_DI) { |
| rec = 1; |
| di_total /= 2; |
| } else |
| di_total -= HOST_DI; |
| if (rec) |
| fprintf(stderr, "WARNING: Recommended minimal size" |
| "of /vz partition is 20Gb!\n"); |
| |
| ve_ds = ds_total / (DEF_DS); |
| ve_di = di_total / (DEF_DI); |
| |
| if (ve_ds < num_ve) { |
| retval = 1; |
| ve_allowed = ve_ds; |
| } |
| if (ve_di < num_ve) { |
| retval = 1; |
| if (ve_di < ve_ds) |
| ve_allowed = ve_di; |
| } |
| if (retval == 1) { |
| fprintf(stderr, "WARNING: /vz partition do not have space " |
| "required for %d VPS\n" |
| "The maximum allowed value is %d\n", |
| num_ve, ve_allowed); |
| fprintf(stderr, "Default disc space values will be used\n\n"); |
| ds_total = 0; di_total = 0; |
| /* exit(retval);*/ |
| } |
| } |
| if ((pagesize = sysconf(_SC_PAGE_SIZE)) == -1) |
| pagesize = 4096; |
| |
| if (low_total > 2 << 30) { |
| if (proc_calc > 2 * MAX_TOTAL_PIDS) |
| proc_calc = 2 * MAX_TOTAL_PIDS; |
| } else if (proc_calc > MAX_TOTAL_PIDS) |
| proc_calc = MAX_TOTAL_PIDS; |
| |
| if (calculate_values()) |
| exit(1); |
| |
| retval = lconv(name); |
| exit(retval); |
| } |
| |