vserver 2.0-rc4
[linux-2.6.git] / kernel / vserver / sched.c
1 /*
2  *  linux/kernel/vserver/sched.c
3  *
4  *  Virtual Server: Scheduler Support
5  *
6  *  Copyright (C) 2004-2005  Herbert Pƶtzl
7  *
8  *  V0.01  adapted Sam Vilains version to 2.6.3
9  *  V0.02  removed legacy interface
10  *
11  */
12
13 #include <linux/config.h>
14 #include <linux/sched.h>
15 #include <linux/vs_context.h>
16 #include <linux/vs_sched.h>
17 #include <linux/vserver/sched_cmd.h>
18
19 #include <asm/errno.h>
20 #include <asm/uaccess.h>
21
22
23 /*
24  * recalculate the context's scheduling tokens
25  *
26  * ret > 0 : number of tokens available
27  * ret = 0 : context is paused
28  * ret < 0 : number of jiffies until new tokens arrive
29  *
30  */
31 int vx_tokens_recalc(struct vx_info *vxi)
32 {
33         long delta, tokens = 0;
34
35         if (vx_info_flags(vxi, VXF_SCHED_PAUSE, 0))
36                 /* we are paused */
37                 return 0;
38
39         delta = jiffies - vxi->sched.jiffies;
40
41         if (delta >= vxi->sched.interval) {
42                 /* lockdown scheduler info */
43                 spin_lock(&vxi->sched.tokens_lock);
44
45                 /* calc integral token part */
46                 delta = jiffies - vxi->sched.jiffies;
47                 tokens = delta / vxi->sched.interval;
48                 delta = tokens * vxi->sched.interval;
49                 tokens *= vxi->sched.fill_rate;
50
51                 atomic_add(tokens, &vxi->sched.tokens);
52                 vxi->sched.jiffies += delta;
53                 tokens = atomic_read(&vxi->sched.tokens);
54
55                 if (tokens > vxi->sched.tokens_max) {
56                         tokens = vxi->sched.tokens_max;
57                         atomic_set(&vxi->sched.tokens, tokens);
58                 }
59                 spin_unlock(&vxi->sched.tokens_lock);
60         } else {
61                 /* no new tokens */
62                 tokens = vx_tokens_avail(vxi);
63                 if (tokens <= 0)
64                         vxi->vx_state |= VXS_ONHOLD;
65                 if (tokens < vxi->sched.tokens_min) {
66                         /* enough tokens will be available in */
67                         if (vxi->sched.tokens_min == 0)
68                                 return delta - vxi->sched.interval;
69                         return delta - vxi->sched.interval *
70                                 vxi->sched.tokens_min / vxi->sched.fill_rate;
71                 }
72         }
73
74         /* we have some tokens left */
75         if (vx_info_state(vxi, VXS_ONHOLD) &&
76                 (tokens >= vxi->sched.tokens_min))
77                 vxi->vx_state &= ~VXS_ONHOLD;
78         if (vx_info_state(vxi, VXS_ONHOLD))
79                 tokens -= vxi->sched.tokens_min;
80
81         return tokens;
82 }
83
84 /*
85  * effective_prio - return the priority that is based on the static
86  * priority but is modified by bonuses/penalties.
87  *
88  * We scale the actual sleep average [0 .... MAX_SLEEP_AVG]
89  * into a -4 ... 0 ... +4 bonus/penalty range.
90  *
91  * Additionally, we scale another amount based on the number of
92  * CPU tokens currently held by the context, if the process is
93  * part of a context (and the appropriate SCHED flag is set).
94  * This ranges from -5 ... 0 ... +15, quadratically.
95  *
96  * So, the total bonus is -9 .. 0 .. +19
97  * We use ~50% of the full 0...39 priority range so that:
98  *
99  * 1) nice +19 interactive tasks do not preempt nice 0 CPU hogs.
100  * 2) nice -20 CPU hogs do not get preempted by nice 0 tasks.
101  *    unless that context is far exceeding its CPU allocation.
102  *
103  * Both properties are important to certain workloads.
104  */
105 int vx_effective_vavavoom(struct vx_info *vxi, int max_prio)
106 {
107         int vavavoom, max;
108
109         /* lots of tokens = lots of vavavoom
110          *      no tokens = no vavavoom      */
111         if ((vavavoom = atomic_read(&vxi->sched.tokens)) >= 0) {
112                 max = vxi->sched.tokens_max;
113                 vavavoom = max - vavavoom;
114                 max = max * max;
115                 vavavoom = max_prio * VAVAVOOM_RATIO / 100
116                         * (vavavoom*vavavoom - (max >> 2)) / max;
117         } else
118                 vavavoom = 0;
119
120         vxi->sched.vavavoom = vavavoom;
121         return vavavoom;
122 }
123
124
125 int vc_set_sched_v2(uint32_t xid, void __user *data)
126 {
127         struct vcmd_set_sched_v2 vc_data;
128         struct vx_info *vxi;
129
130         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
131                 return -EFAULT;
132
133         vxi = locate_vx_info(xid);
134         if (!vxi)
135                 return -EINVAL;
136
137         spin_lock(&vxi->sched.tokens_lock);
138
139         if (vc_data.interval != SCHED_KEEP)
140                 vxi->sched.interval = vc_data.interval;
141         if (vc_data.fill_rate != SCHED_KEEP)
142                 vxi->sched.fill_rate = vc_data.fill_rate;
143         if (vc_data.tokens_min != SCHED_KEEP)
144                 vxi->sched.tokens_min = vc_data.tokens_min;
145         if (vc_data.tokens_max != SCHED_KEEP)
146                 vxi->sched.tokens_max = vc_data.tokens_max;
147         if (vc_data.tokens != SCHED_KEEP)
148                 atomic_set(&vxi->sched.tokens, vc_data.tokens);
149
150         /* Sanity check the resultant values */
151         if (vxi->sched.fill_rate <= 0)
152                 vxi->sched.fill_rate = 1;
153         if (vxi->sched.interval <= 0)
154                 vxi->sched.interval = HZ;
155         if (vxi->sched.tokens_max == 0)
156                 vxi->sched.tokens_max = 1;
157         if (atomic_read(&vxi->sched.tokens) > vxi->sched.tokens_max)
158                 atomic_set(&vxi->sched.tokens, vxi->sched.tokens_max);
159         if (vxi->sched.tokens_min > vxi->sched.tokens_max)
160                 vxi->sched.tokens_min = vxi->sched.tokens_max;
161
162         spin_unlock(&vxi->sched.tokens_lock);
163         put_vx_info(vxi);
164         return 0;
165 }
166
167
168 int vc_set_sched(uint32_t xid, void __user *data)
169 {
170         struct vcmd_set_sched_v3 vc_data;
171         struct vx_info *vxi;
172         unsigned int set_mask;
173
174         if (copy_from_user (&vc_data, data, sizeof(vc_data)))
175                 return -EFAULT;
176
177         vxi = locate_vx_info(xid);
178         if (!vxi)
179                 return -EINVAL;
180
181         set_mask = vc_data.set_mask;
182
183         spin_lock(&vxi->sched.tokens_lock);
184
185         if (set_mask & VXSM_FILL_RATE)
186                 vxi->sched.fill_rate = vc_data.fill_rate;
187         if (set_mask & VXSM_INTERVAL)
188                 vxi->sched.interval = vc_data.interval;
189         if (set_mask & VXSM_TOKENS)
190                 atomic_set(&vxi->sched.tokens, vc_data.tokens);
191         if (set_mask & VXSM_TOKENS_MIN)
192                 vxi->sched.tokens_min = vc_data.tokens_min;
193         if (set_mask & VXSM_TOKENS_MAX)
194                 vxi->sched.tokens_max = vc_data.tokens_max;
195         if (set_mask & VXSM_PRIO_BIAS)
196                 vxi->sched.priority_bias = vc_data.priority_bias;
197
198         /* Sanity check the resultant values */
199         if (vxi->sched.fill_rate <= 0)
200                 vxi->sched.fill_rate = 1;
201         if (vxi->sched.interval <= 0)
202                 vxi->sched.interval = HZ;
203         if (vxi->sched.tokens_max == 0)
204                 vxi->sched.tokens_max = 1;
205         if (atomic_read(&vxi->sched.tokens) > vxi->sched.tokens_max)
206                 atomic_set(&vxi->sched.tokens, vxi->sched.tokens_max);
207         if (vxi->sched.tokens_min > vxi->sched.tokens_max)
208                 vxi->sched.tokens_min = vxi->sched.tokens_max;
209         if (vxi->sched.priority_bias > MAX_PRIO_BIAS)
210                 vxi->sched.priority_bias = MAX_PRIO_BIAS;
211         if (vxi->sched.priority_bias < MIN_PRIO_BIAS)
212                 vxi->sched.priority_bias = MIN_PRIO_BIAS;
213
214         spin_unlock(&vxi->sched.tokens_lock);
215         put_vx_info(vxi);
216         return 0;
217 }
218