adf60837f7cceff99b9af8aa50138335338abef8
[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, 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    */
58   if (vc_ctx_create(ctx) == VC_NOCTX)
59     return -1;
60
61   /* Set capabilities - these don't take effect until SETUP flag is unset */
62   vc_caps.bcaps = bcaps;
63   vc_caps.bmask = ~0ULL;  /* currently unused */
64   vc_caps.ccaps = 0;      /* don't want any of these */
65   vc_caps.cmask = ~0ULL;
66   if (vc_set_ccaps(ctx, &vc_caps))
67     return -1;
68
69   pl_set_limits(ctx, slr);
70
71   return 0;
72 }
73
74 int
75 pl_setup_done(xid_t ctx)
76 {
77   struct vc_ctx_flags  vc_flags;
78
79   /* unset SETUP flag - this allows other processes to migrate */
80
81   /* Don't clear the STATE_INIT flag, as that would make us the init task. */
82   vc_flags.mask = VC_VXF_STATE_SETUP;
83   vc_flags.flagword = 0;
84   if (vc_set_cflags(ctx, &vc_flags))
85     return -1;
86
87   return 0;
88 }
89
90 #define RETRY_LIMIT  10
91
92 int
93 pl_chcontext(xid_t ctx, uint64_t bcaps, struct sliver_resources *slr)
94 {
95   int  retry_count = 0;
96
97   for (;;)
98     {
99       struct vc_ctx_flags  vc_flags;
100
101       if (vc_get_cflags(ctx, &vc_flags))
102         {
103           if (errno != ESRCH)
104             return -1;
105
106           /* context doesn't exist - create it */
107           if (create_context(ctx, bcaps,slr))
108             {
109               if (errno == EEXIST)
110                 /* another process beat us in a race */
111                 goto migrate;
112               if (errno == EBUSY)
113                 /* another process is creating - poll the SETUP flag */
114                 continue;
115               return -1;
116             }
117
118           /* created context and migrated to it i.e., we're done */
119           return 1;
120         }
121
122       /* check the SETUP flag */
123       if (vc_flags.flagword & VC_VXF_STATE_SETUP)
124         {
125           /* context is still being setup - wait a while then retry */
126           if (retry_count++ >= RETRY_LIMIT)
127             {
128               errno = EBUSY;
129               return -1;
130             }
131           sleep(1);
132           continue;
133         }
134
135       /* context has been setup */
136     migrate:
137       if (!vc_ctx_migrate(ctx))
138         break;  /* done */
139
140       /* context disappeared - retry */
141     }
142
143   return 0;
144 }
145
146 /* it's okay for a syscall to fail because the context doesn't exist */
147 #define VC_SYSCALL(x)                           \
148 do                                              \
149 {                                               \
150   if (x)                                        \
151     return errno == ESRCH ? 0 : -1;             \
152 }                                               \
153 while (0)
154
155 int
156 pl_setsched(xid_t ctx, uint32_t cpu_share, uint32_t cpu_sched_flags)
157 {
158   struct vc_set_sched  vc_sched;
159   struct vc_ctx_flags  vc_flags;
160   uint32_t  new_flags;
161
162   vc_sched.set_mask = (VC_VXSM_FILL_RATE | VC_VXSM_INTERVAL | VC_VXSM_TOKENS |
163                        VC_VXSM_TOKENS_MIN | VC_VXSM_TOKENS_MAX);
164   vc_sched.fill_rate = cpu_share;  /* tokens accumulated per interval */
165   vc_sched.interval = 1000;  /* milliseconds */
166   vc_sched.tokens = 100;     /* initial allocation of tokens */
167   vc_sched.tokens_min = 50;  /* need this many tokens to run */
168   vc_sched.tokens_max = 100;  /* max accumulated number of tokens */
169
170   VC_SYSCALL(vc_set_sched(ctx, &vc_sched));
171
172   /* get current flag values */
173   VC_SYSCALL(vc_get_cflags(ctx, &vc_flags));
174
175   /* guaranteed CPU corresponds to SCHED_SHARE flag being cleared */
176   new_flags = (cpu_sched_flags & VS_SCHED_CPU_GUARANTEED
177                ? 0
178                : VC_VXF_SCHED_SHARE);
179   if ((vc_flags.flagword & VC_VXF_SCHED_SHARE) != new_flags)
180     {
181       vc_flags.mask = VC_VXF_SCHED_FLAGS;
182       vc_flags.flagword = new_flags | VC_VXF_SCHED_HARD;
183       VC_SYSCALL(vc_set_cflags(ctx, &vc_flags));
184     }
185
186   return 0;
187 }
188
189 struct pl_resources {
190         char *name;
191         unsigned long long *limit;
192 };
193
194 #define WHITESPACE(buffer,index,len)     \
195   while(isspace((int)buffer[index])) \
196         if (index < len) index++; else goto out;
197
198 #define VSERVERCONF "/etc/vservers/"
199 void
200 pl_get_limits(char *context, struct sliver_resources *slr)
201 {
202   FILE *fb;
203   size_t len = strlen(VSERVERCONF) + strlen(context) + strlen(".conf") + NULLBYTE_SIZE;
204   char *conf = (char *)malloc(len);     
205   struct pl_resources *r;
206   struct pl_resources sliver_list[] = {
207     {"CPULIMIT", &slr->vs_cpu},
208     {"CPUSHARE", &slr->vs_cpu},
209     {"CPUGUARANTEED", &slr->vs_cpuguaranteed},
210   
211     {"TASKLIMIT", &slr->vs_nproc.hard}, /* backwards compatible */
212     {"VS_NPROC_HARD", &slr->vs_nproc.hard},
213     {"VS_NPROC_SOFT", &slr->vs_nproc.soft},
214     {"VS_NPROC_MINIMUM", &slr->vs_nproc.min},
215   
216     {"MEMLIMIT", &slr->vs_rss.hard}, /* backwards compatible */
217     {"VS_RSS_HARD", &slr->vs_rss.hard},
218     {"VS_RSS_SOFT", &slr->vs_rss.soft},
219     {"VS_RSS_MINIMUM", &slr->vs_rss.min},
220   
221     {"VS_AS_HARD", &slr->vs_as.hard},
222     {"VS_AS_SOFT", &slr->vs_as.soft},
223     {"VS_AS_MINIMUM", &slr->vs_as.min},
224   
225     {"VS_OPENFD_HARD", &slr->vs_openfd.hard},
226     {"VS_OPENFD_SOFT", &slr->vs_openfd.soft},
227     {"VS_OPENFD_MINIMUM", &slr->vs_openfd.min},
228
229     {"VS_WHITELISTED", &slr->vs_whitelisted},
230     {0,0}
231   };
232
233   sprintf(conf, "%s%s.conf", VSERVERCONF, context);
234
235   slr->vs_cpu = VC_LIM_KEEP;
236   slr->vs_cpuguaranteed = 0;
237
238   slr->vs_rss.hard = VC_LIM_KEEP;
239   slr->vs_rss.soft = VC_LIM_KEEP;
240   slr->vs_rss.min = VC_LIM_KEEP;
241
242   slr->vs_as.hard = VC_LIM_KEEP;
243   slr->vs_as.soft = VC_LIM_KEEP;
244   slr->vs_as.min = VC_LIM_KEEP;
245
246
247   slr->vs_nproc.hard = VC_LIM_KEEP;
248   slr->vs_nproc.soft = VC_LIM_KEEP;
249   slr->vs_nproc.min = VC_LIM_KEEP;
250
251   slr->vs_openfd.hard = VC_LIM_KEEP;
252   slr->vs_openfd.soft = VC_LIM_KEEP;
253   slr->vs_openfd.min = VC_LIM_KEEP;
254
255   slr->vs_whitelisted = 1;
256
257   /* open the conf file for reading */
258   fb = fopen(conf,"r");
259   if (fb != NULL) {
260     size_t index;
261     char *buffer = malloc(1000);
262     char *p;
263     
264     /* the conf file exist */ 
265     while((p=fgets(buffer,1000-1,fb))!=NULL) {
266       index = 0;
267       len = strnlen(buffer,1000);
268       WHITESPACE(buffer,index,len);
269       if (buffer[index] == '#') 
270         continue;
271       
272       for (r=&sliver_list[0]; r->name; r++)
273         if ((p=strstr(&buffer[index],r->name))!=NULL) {
274           /* adjust index into buffer */
275           index+= (p-&buffer[index])+strlen(r->name);
276           
277           /* skip over whitespace */
278           WHITESPACE(buffer,index,len);
279           
280           /* expecting to see = sign */
281           if (buffer[index++]!='=') goto out;
282           
283           /* skip over whitespace */
284           WHITESPACE(buffer,index,len);
285           
286           /* expecting to see a digit for number */
287           if (!isdigit((int)buffer[index])) goto out;
288           
289           *r->limit = atoi(&buffer[index]);
290           if (0) /* for debugging only */
291             fprintf(stderr,"pl_get_limits found %s=%ld\n",
292                     r->name,*r->limit);
293           break;
294         }
295     }
296   out:
297     fclose(fb);
298     free(buffer);
299   } else {
300     fprintf(stderr,"cannot open %s\n",conf);
301   }
302   free(conf);
303 }
304
305 int
306 adjust_lim(struct vc_rlimit *vcr, struct rlimit *lim)
307 {
308   int adjusted = 0;
309   if (vcr->min != VC_LIM_KEEP) {
310     if (vcr->min > lim->rlim_cur) {
311       lim->rlim_cur = vcr->min;
312       adjusted = 1;
313     }
314     if (vcr->min > lim->rlim_max) {
315       lim->rlim_max = vcr->min;
316       adjusted = 1;
317     }
318   }
319
320   if (vcr->soft != VC_LIM_KEEP) {
321     switch (vcr->min != VC_LIM_KEEP) {
322     case 1:
323       if (vcr->soft < vcr->min)
324         break;
325     case 0:
326         lim->rlim_cur = vcr->soft;
327         adjusted = 1;
328     }
329   }
330
331   if (vcr->hard != VC_LIM_KEEP) {
332     switch (vcr->min != VC_LIM_KEEP) {
333     case 1:
334       if (vcr->hard < vcr->min)
335         break;
336     case 0:
337         lim->rlim_cur = vcr->hard;
338         adjusted = 1;
339     }
340   }
341   return adjusted;
342 }
343
344
345 void
346 pl_set_limits(xid_t ctx, struct sliver_resources *slr)
347 {
348   struct rlimit lim; /* getrlimit values */
349   unsigned long long vs_cpu;
350   uint32_t cpu_sched_flags;
351
352   if (slr != 0) {
353     /* set memory limits */
354     getrlimit(RLIMIT_RSS,&lim);
355     if (adjust_lim(&slr->vs_rss, &lim)) {
356       setrlimit(RLIMIT_RSS, &lim);
357       if (vc_set_rlimit(ctx, RLIMIT_RSS, &slr->vs_rss))
358         {
359           PERROR("pl_setrlimit(%u, RLIMIT_RSS)", ctx);
360           exit(1);
361         }
362     }
363
364     /* set address space limits */
365     getrlimit(RLIMIT_AS,&lim);
366     if (adjust_lim(&slr->vs_as, &lim)) {
367       setrlimit(RLIMIT_AS, &lim);
368       if (vc_set_rlimit(ctx, RLIMIT_AS, &slr->vs_as))
369         {
370           PERROR("pl_setrlimit(%u, RLIMIT_AS)", ctx);
371           exit(1);
372         }
373     }
374     /* set nrpoc limit */
375     getrlimit(RLIMIT_NPROC,&lim);
376     if (adjust_lim(&slr->vs_nproc, &lim)) {
377       setrlimit(RLIMIT_NPROC, &lim);
378       if (vc_set_rlimit(ctx, RLIMIT_NPROC, &slr->vs_nproc))
379         {
380           PERROR("pl_setrlimit(%u, RLIMIT_NPROC)", ctx);
381           exit(1);
382         }
383     }
384
385     /* set openfd limit */
386     getrlimit(RLIMIT_NOFILE,&lim);
387     if (adjust_lim(&slr->vs_openfd, &lim)) {
388       setrlimit(RLIMIT_NOFILE, &lim);
389       if (vc_set_rlimit(ctx, RLIMIT_NOFILE, &slr->vs_openfd))
390         {
391           PERROR("pl_setrlimit(%u, RLIMIT_NOFILE)", ctx);
392           exit(1);
393         }
394 #ifndef VLIMIT_OPENFD
395 #warning VLIMIT_OPENFD should be defined from standard header
396 #define VLIMIT_OPENFD   17
397 #endif
398       if (vc_set_rlimit(ctx, VLIMIT_OPENFD, &slr->vs_openfd))
399         {
400           PERROR("pl_setrlimit(%u, VLIMIT_OPENFD)", ctx);
401           exit(1);
402         }
403     }
404     vs_cpu = slr->vs_cpu;    
405     cpu_sched_flags = slr->vs_cpuguaranteed & VS_SCHED_CPU_GUARANTEED;
406   } else {
407     vs_cpu = 1;
408     cpu_sched_flags = 0;
409   }
410   
411   if (pl_setsched(ctx, vs_cpu, cpu_sched_flags) < 0) {
412     PERROR("pl_setsched(&u)", ctx);
413     exit(1);
414   }
415 }