making 'adjust_lim' global function
[util-vserver.git] / lib / planetlab.c
1 /* Copyright 2005 Princeton University
2
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions
5 are met: 
6
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9       
10     * Redistributions in binary form must reproduce the above
11       copyright notice, this list of conditions and the following
12       disclaimer in the documentation and/or other materials provided
13       with the distribution.
14       
15     * Neither the name of the copyright holder nor the names of its
16       contributors may be used to endorse or promote products derived
17       from this software without specific prior written permission.
18       
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRINCETON
23 UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26 OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29 WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 POSSIBILITY OF SUCH DAMAGE. 
31
32 */
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <stdint.h>
39 #include <stdarg.h>
40 #include <unistd.h>
41 #include <ctype.h>
42 #include <sys/resource.h>
43
44 #include "config.h"
45 #include "sched_cmd.h"
46 #include "virtual.h"
47 #include "vserver.h"
48 #include "planetlab.h"
49
50 static int
51 create_context(xid_t ctx, uint32_t flags, uint64_t bcaps, struct sliver_resources *slr)
52 {
53   struct vc_ctx_caps  vc_caps;
54
55   /*
56    * Create context info - this sets the STATE_SETUP and STATE_INIT flags.
57    * Don't ever clear the STATE_INIT flag, that makes us the init task.
58    *
59    * XXX - the kernel code allows initial flags to be passed as an arg.
60    */
61   if (vc_ctx_create(ctx) == VC_NOCTX)
62     return -1;
63
64   /* set capabilities - these don't take effect until SETUP flag is unset */
65   vc_caps.bcaps = bcaps;
66   vc_caps.bmask = ~0ULL;  /* currently unused */
67   vc_caps.ccaps = 0;      /* don't want any of these */
68   vc_caps.cmask = ~0ULL;
69   if (vc_set_ccaps(ctx, &vc_caps))
70     return -1;
71
72   pl_set_limits(ctx, slr);
73
74   return 0;
75 }
76
77 int
78 pl_setup_done(xid_t ctx)
79 {
80   struct vc_ctx_flags  vc_flags;
81
82   /* unset SETUP flag - this allows other processes to migrate */
83   vc_flags.mask = VC_VXF_STATE_SETUP;
84   vc_flags.flagword = 0;
85   if (vc_set_cflags(ctx, &vc_flags))
86     return -1;
87
88   return 0;
89 }
90
91 #define RETRY_LIMIT  10
92
93 int
94 pl_chcontext(xid_t ctx, uint32_t flags, uint64_t bcaps, struct sliver_resources *slr)
95 {
96   int  retry_count = 0;
97
98   for (;;)
99     {
100       struct vc_ctx_flags  vc_flags;
101
102       if (vc_get_cflags(ctx, &vc_flags))
103         {
104           if (errno != ESRCH)
105             return -1;
106
107           /* context doesn't exist - create it */
108           if (create_context(ctx, flags, bcaps,slr))
109             {
110               if (errno == EEXIST)
111                 /* another process beat us in a race */
112                 goto migrate;
113               if (errno == EBUSY)
114                 /* another process is creating - poll the SETUP flag */
115                 continue;
116               return -1;
117             }
118
119           /* created context and migrated to it i.e., we're done */
120           return 1;
121         }
122
123       /* check the SETUP flag */
124       if (vc_flags.flagword & VC_VXF_STATE_SETUP)
125         {
126           /* context is still being setup - wait a while then retry */
127           if (retry_count++ >= RETRY_LIMIT)
128             {
129               errno = EBUSY;
130               return -1;
131             }
132           sleep(1);
133           continue;
134         }
135
136       /* context has been setup */
137     migrate:
138       if (!vc_ctx_migrate(ctx))
139         break;  /* done */
140
141       /* context disappeared - retry */
142     }
143
144   return 0;
145 }
146
147 /* it's okay for a syscall to fail because the context doesn't exist */
148 #define VC_SYSCALL(x)                           \
149 do                                              \
150 {                                               \
151   if (x)                                        \
152     return errno == ESRCH ? 0 : -1;             \
153 }                                               \
154 while (0)
155
156 int
157 pl_setsched(xid_t ctx, uint32_t cpu_share, uint32_t cpu_sched_flags)
158 {
159   struct vc_set_sched  vc_sched;
160   struct vc_ctx_flags  vc_flags;
161   uint32_t  new_flags;
162
163   vc_sched.set_mask = (VC_VXSM_FILL_RATE | VC_VXSM_INTERVAL | VC_VXSM_TOKENS |
164                        VC_VXSM_TOKENS_MIN | VC_VXSM_TOKENS_MAX);
165   vc_sched.fill_rate = cpu_share;  /* tokens accumulated per interval */
166   vc_sched.interval = 1000;  /* milliseconds */
167   vc_sched.tokens = 100;     /* initial allocation of tokens */
168   vc_sched.tokens_min = 50;  /* need this many tokens to run */
169   vc_sched.tokens_max = 100;  /* max accumulated number of tokens */
170
171   VC_SYSCALL(vc_set_sched(ctx, &vc_sched));
172
173   /* get current flag values */
174   VC_SYSCALL(vc_get_cflags(ctx, &vc_flags));
175
176   /* guaranteed CPU corresponds to SCHED_SHARE flag being cleared */
177   new_flags = (cpu_sched_flags & VS_SCHED_CPU_GUARANTEED
178                ? 0
179                : VC_VXF_SCHED_SHARE);
180   if ((vc_flags.flagword & VC_VXF_SCHED_SHARE) != new_flags)
181     {
182       vc_flags.mask = VC_VXF_SCHED_FLAGS;
183       vc_flags.flagword = new_flags | VC_VXF_SCHED_HARD;
184       VC_SYSCALL(vc_set_cflags(ctx, &vc_flags));
185     }
186
187   return 0;
188 }
189
190 struct pl_resources {
191         char *name;
192         unsigned long long *limit;
193 };
194
195 #define WHITESPACE(buffer,index,len)     \
196   while(isspace((int)buffer[index])) \
197         if (index < len) index++; else goto out;
198
199 #define VSERVERCONF "/etc/vservers/"
200 void
201 pl_get_limits(char *context, struct sliver_resources *slr)
202 {
203   FILE *fb;
204   size_t len = strlen(VSERVERCONF) + strlen(context) + strlen(".conf") + NULLBYTE_SIZE;
205   char *conf = (char *)malloc(len);     
206   struct pl_resources *r;
207   struct pl_resources sliver_list[] = {
208     {"CPULIMIT", &slr->vs_cpu},
209     {"CPUSHARE", &slr->vs_cpu},
210     {"CPUGUARANTEED", &slr->vs_cpuguaranteed},
211   
212     {"TASKLIMIT", &slr->vs_nproc.hard}, /* backwards compatible */
213     {"VS_NPROC_HARD", &slr->vs_nproc.hard},
214     {"VS_NPROC_SOFT", &slr->vs_nproc.soft},
215     {"VS_NPROC_MINIMUM", &slr->vs_nproc.min},
216   
217     {"MEMLIMIT", &slr->vs_rss.hard}, /* backwards compatible */
218     {"VS_RSS_HARD", &slr->vs_rss.hard},
219     {"VS_RSS_SOFT", &slr->vs_rss.soft},
220     {"VS_RSS_MINIMUM", &slr->vs_rss.min},
221   
222     {"VS_AS_HARD", &slr->vs_as.hard},
223     {"VS_AS_SOFT", &slr->vs_as.soft},
224     {"VS_AS_MINIMUM", &slr->vs_as.min},
225   
226     {"VS_OPENFD_HARD", &slr->vs_openfd.hard},
227     {"VS_OPENFD_SOFT", &slr->vs_openfd.soft},
228     {"VS_OPENFD_MINIMUM", &slr->vs_openfd.min},
229
230     {"VS_WHITELISTED", &slr->vs_whitelisted},
231     {0,0}
232   };
233
234   sprintf(conf, "%s%s.conf", VSERVERCONF, context);
235
236   slr->vs_cpu = VC_LIM_KEEP;
237   slr->vs_cpuguaranteed = 0;
238
239   slr->vs_rss.hard = VC_LIM_KEEP;
240   slr->vs_rss.soft = VC_LIM_KEEP;
241   slr->vs_rss.min = VC_LIM_KEEP;
242
243   slr->vs_as.hard = VC_LIM_KEEP;
244   slr->vs_as.soft = VC_LIM_KEEP;
245   slr->vs_as.min = VC_LIM_KEEP;
246
247
248   slr->vs_nproc.hard = VC_LIM_KEEP;
249   slr->vs_nproc.soft = VC_LIM_KEEP;
250   slr->vs_nproc.min = VC_LIM_KEEP;
251
252   slr->vs_openfd.hard = VC_LIM_KEEP;
253   slr->vs_openfd.soft = VC_LIM_KEEP;
254   slr->vs_openfd.min = VC_LIM_KEEP;
255
256   slr->vs_whitelisted = 1;
257
258   /* open the conf file for reading */
259   fb = fopen(conf,"r");
260   if (fb != NULL) {
261     size_t index;
262     char *buffer = malloc(1000);
263     char *p;
264     
265     /* the conf file exist */ 
266     while((p=fgets(buffer,1000-1,fb))!=NULL) {
267       index = 0;
268       len = strnlen(buffer,1000);
269       WHITESPACE(buffer,index,len);
270       if (buffer[index] == '#') 
271         continue;
272       
273       for (r=&sliver_list[0]; r->name; r++)
274         if ((p=strstr(&buffer[index],r->name))!=NULL) {
275           /* adjust index into buffer */
276           index+= (p-&buffer[index])+strlen(r->name);
277           
278           /* skip over whitespace */
279           WHITESPACE(buffer,index,len);
280           
281           /* expecting to see = sign */
282           if (buffer[index++]!='=') goto out;
283           
284           /* skip over whitespace */
285           WHITESPACE(buffer,index,len);
286           
287           /* expecting to see a digit for number */
288           if (!isdigit((int)buffer[index])) goto out;
289           
290           *r->limit = atoi(&buffer[index]);
291           if (0) /* for debugging only */
292             fprintf(stderr,"pl_get_limits found %s=%ld\n",
293                     r->name,*r->limit);
294           break;
295         }
296     }
297   out:
298     fclose(fb);
299     free(buffer);
300   } else {
301     fprintf(stderr,"cannot open %s\n",conf);
302   }
303   free(conf);
304 }
305
306 int
307 adjust_lim(struct vc_rlimit *vcr, struct rlimit *lim)
308 {
309   int adjusted = 0;
310   if (vcr->min != VC_LIM_KEEP) {
311     if (vcr->min > lim->rlim_cur) {
312       lim->rlim_cur = vcr->min;
313       adjusted = 1;
314     }
315     if (vcr->min > lim->rlim_max) {
316       lim->rlim_max = vcr->min;
317       adjusted = 1;
318     }
319   }
320
321   if (vcr->soft != VC_LIM_KEEP) {
322     switch (vcr->min != VC_LIM_KEEP) {
323     case 1:
324       if (vcr->soft < vcr->min)
325         break;
326     case 0:
327         lim->rlim_cur = vcr->soft;
328         adjusted = 1;
329     }
330   }
331
332   if (vcr->hard != VC_LIM_KEEP) {
333     switch (vcr->min != VC_LIM_KEEP) {
334     case 1:
335       if (vcr->hard < vcr->min)
336         break;
337     case 0:
338         lim->rlim_cur = vcr->hard;
339         adjusted = 1;
340     }
341   }
342   return adjusted;
343 }
344
345
346 void
347 pl_set_limits(xid_t ctx, struct sliver_resources *slr)
348 {
349   struct rlimit lim; /* getrlimit values */
350   unsigned long long vs_cpu;
351   uint32_t cpu_sched_flags;
352
353   if (slr != 0) {
354     /* set memory limits */
355     getrlimit(RLIMIT_RSS,&lim);
356     if (adjust_lim(&slr->vs_rss, &lim)) {
357       setrlimit(RLIMIT_RSS, &lim);
358       if (vc_set_rlimit(ctx, RLIMIT_RSS, &slr->vs_rss))
359         {
360           PERROR("pl_setrlimit(%u, RLIMIT_RSS)", ctx);
361           exit(1);
362         }
363     }
364
365     /* set address space limits */
366     getrlimit(RLIMIT_AS,&lim);
367     if (adjust_lim(&slr->vs_as, &lim)) {
368       setrlimit(RLIMIT_AS, &lim);
369       if (vc_set_rlimit(ctx, RLIMIT_AS, &slr->vs_as))
370         {
371           PERROR("pl_setrlimit(%u, RLIMIT_AS)", ctx);
372           exit(1);
373         }
374     }
375     /* set nrpoc limit */
376     getrlimit(RLIMIT_NPROC,&lim);
377     if (adjust_lim(&slr->vs_nproc, &lim)) {
378       setrlimit(RLIMIT_NPROC, &lim);
379       if (vc_set_rlimit(ctx, RLIMIT_NPROC, &slr->vs_nproc))
380         {
381           PERROR("pl_setrlimit(%u, RLIMIT_NPROC)", ctx);
382           exit(1);
383         }
384     }
385
386     /* set openfd limit */
387     getrlimit(RLIMIT_NOFILE,&lim);
388     if (adjust_lim(&slr->vs_openfd, &lim)) {
389       setrlimit(RLIMIT_NOFILE, &lim);
390       if (vc_set_rlimit(ctx, RLIMIT_NOFILE, &slr->vs_openfd))
391         {
392           PERROR("pl_setrlimit(%u, RLIMIT_NOFILE)", ctx);
393           exit(1);
394         }
395 #ifndef VLIMIT_OPENFD
396 #warning VLIMIT_OPENFD should be defined from standard header
397 #define VLIMIT_OPENFD   17
398 #endif
399       if (vc_set_rlimit(ctx, VLIMIT_OPENFD, &slr->vs_openfd))
400         {
401           PERROR("pl_setrlimit(%u, VLIMIT_OPENFD)", ctx);
402           exit(1);
403         }
404     }
405     vs_cpu = slr->vs_cpu;    
406     cpu_sched_flags = slr->vs_cpuguaranteed & VS_SCHED_CPU_GUARANTEED;
407   } else {
408     vs_cpu = 1;
409     cpu_sched_flags = 0;
410   }
411   
412   if (pl_setsched(ctx, vs_cpu, cpu_sched_flags) < 0) {
413     PERROR("pl_setsched(&u)", ctx);
414     exit(1);
415   }
416 }