8bef6dc2c4057807d0bb97cfd373073b178548d5
[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 void
307 pl_set_limits(xid_t ctx, struct sliver_resources *slr)
308 {
309   struct rlimit olim; /* current limit values */
310   struct rlimit nlim; /* new limit values */
311
312   if (slr != 0) {
313     /* set memory limits */
314     getrlimit(RLIMIT_RSS,&olim);
315     if (0) /* for debugging only */
316       fprintf(stderr,"rss cur = %ld, max = %ld, vs_rss min = %ld\n",olim.rlim_cur,olim.rlim_max,slr->vs_rss.min);
317     if ((slr->vs_rss.min != VC_LIM_KEEP) && (slr->vs_rss.min > olim.rlim_cur)) {
318       nlim.rlim_cur = slr->vs_rss.min;
319       if (slr->vs_rss.min > olim.rlim_max) {
320         nlim.rlim_max = slr->vs_rss.min;
321       } else {
322         nlim.rlim_max = olim.rlim_max;
323       }
324       setrlimit(RLIMIT_RSS, &nlim);
325     }
326     if (vc_set_rlimit(ctx, RLIMIT_RSS, &slr->vs_rss))
327       {
328         PERROR("pl_setrlimit(%u, RLIMIT_RSS)", ctx);
329         exit(1);
330       }
331     
332     /* set address space limits */
333     getrlimit(RLIMIT_AS,&olim);
334     if (0) /* for debugging only */
335       fprintf(stderr,"as cur = %ld, max = %ld, vs_as min = %ld\n",olim.rlim_cur,olim.rlim_max,slr->vs_as.min);
336     if ((slr->vs_as.min != VC_LIM_KEEP) && (slr->vs_as.min > olim.rlim_cur)) {
337       nlim.rlim_cur = slr->vs_as.min;
338       if (slr->vs_as.min > olim.rlim_max) {
339         nlim.rlim_max = slr->vs_as.min;
340       } else {
341         nlim.rlim_max = olim.rlim_max;
342       }
343       setrlimit(RLIMIT_AS, &nlim);
344     }
345     if (vc_set_rlimit(ctx, RLIMIT_AS, &slr->vs_as))
346       {
347         PERROR("pl_setrlimit(%u, RLIMIT_AS)", ctx);
348         exit(1);
349       }
350
351     /* set nrpoc limit */
352     getrlimit(RLIMIT_NPROC,&olim);
353     if (0) /* for debugging only */
354       fprintf(stderr,"nproc cur = %ld, max = %ld, vs_nproc min = %ld\n",olim.rlim_cur,olim.rlim_max,slr->vs_nproc.min);
355     if ((slr->vs_nproc.min != VC_LIM_KEEP) && (slr->vs_nproc.min > olim.rlim_cur)) {
356       nlim.rlim_cur = slr->vs_nproc.min;
357       if (slr->vs_nproc.min > olim.rlim_max) {
358         nlim.rlim_max = slr->vs_nproc.min;
359       } else {
360         nlim.rlim_max = olim.rlim_max;
361       }
362       setrlimit(RLIMIT_NPROC, &nlim);
363     }
364     if (vc_set_rlimit(ctx, RLIMIT_NPROC, &slr->vs_nproc))
365       {
366         PERROR("pl_setrlimit(%u, RLIMIT_NPROC)", ctx);
367         exit(1);
368       }
369     
370     /* set openfd limit */
371     getrlimit(RLIMIT_NOFILE,&olim);
372     if (0) /* for debugging only */
373       fprintf(stderr,"NOFILE cur = %ld, max = %ld, vs_openfd min = %ld\n",olim.rlim_cur,olim.rlim_max,slr->vs_openfd.min);
374     if ((slr->vs_openfd.min != VC_LIM_KEEP) && (slr->vs_openfd.min > olim.rlim_cur)) {
375       nlim.rlim_cur = slr->vs_openfd.min;
376       if (slr->vs_openfd.min > olim.rlim_max) {
377         nlim.rlim_max = slr->vs_openfd.min;
378       } else {
379         nlim.rlim_max = olim.rlim_max;
380       }
381       setrlimit(RLIMIT_NOFILE, &nlim);
382       if (vc_set_rlimit(ctx, RLIMIT_NOFILE, &slr->vs_openfd))
383         {
384           PERROR("pl_setrlimit(%u, RLIMIT_NOFILE)", ctx);
385           exit(1);
386         }
387     }
388 #ifndef VLIMIT_OPENFD
389 #warning VLIMIT_OPENFD should be defined from standard header
390 #define VLIMIT_OPENFD   17
391 #endif
392     if (vc_set_rlimit(ctx, VLIMIT_OPENFD, &slr->vs_openfd))
393       {
394         PERROR("pl_setrlimit(%u, VLIMIT_OPENFD)", ctx);
395       exit(1);
396       }
397   }
398     
399   if (pl_setsched(ctx, slr ? slr->vs_cpu : 1, slr ? (slr->vs_cpuguaranteed & VS_SCHED_CPU_GUARANTEED) : 0 ) < 0)
400     {
401       PERROR("pl_setsched(&u)", ctx);
402       exit(1);
403     }
404 }