diff options
Diffstat (limited to 'kernel/rcutiny_plugin.h')
| -rw-r--r-- | kernel/rcutiny_plugin.h | 203 |
1 files changed, 95 insertions, 108 deletions
diff --git a/kernel/rcutiny_plugin.h b/kernel/rcutiny_plugin.h index 3cb8e362e883..f259c676195f 100644 --- a/kernel/rcutiny_plugin.h +++ b/kernel/rcutiny_plugin.h | |||
| @@ -100,23 +100,28 @@ struct rcu_preempt_ctrlblk { | |||
| 100 | u8 completed; /* Last grace period completed. */ | 100 | u8 completed; /* Last grace period completed. */ |
| 101 | /* If all three are equal, RCU is idle. */ | 101 | /* If all three are equal, RCU is idle. */ |
| 102 | #ifdef CONFIG_RCU_BOOST | 102 | #ifdef CONFIG_RCU_BOOST |
| 103 | s8 boosted_this_gp; /* Has boosting already happened? */ | ||
| 104 | unsigned long boost_time; /* When to start boosting (jiffies) */ | 103 | unsigned long boost_time; /* When to start boosting (jiffies) */ |
| 105 | #endif /* #ifdef CONFIG_RCU_BOOST */ | 104 | #endif /* #ifdef CONFIG_RCU_BOOST */ |
| 106 | #ifdef CONFIG_RCU_TRACE | 105 | #ifdef CONFIG_RCU_TRACE |
| 107 | unsigned long n_grace_periods; | 106 | unsigned long n_grace_periods; |
| 108 | #ifdef CONFIG_RCU_BOOST | 107 | #ifdef CONFIG_RCU_BOOST |
| 109 | unsigned long n_tasks_boosted; | 108 | unsigned long n_tasks_boosted; |
| 109 | /* Total number of tasks boosted. */ | ||
| 110 | unsigned long n_exp_boosts; | 110 | unsigned long n_exp_boosts; |
| 111 | /* Number of tasks boosted for expedited GP. */ | ||
| 111 | unsigned long n_normal_boosts; | 112 | unsigned long n_normal_boosts; |
| 112 | unsigned long n_normal_balk_blkd_tasks; | 113 | /* Number of tasks boosted for normal GP. */ |
| 113 | unsigned long n_normal_balk_gp_tasks; | 114 | unsigned long n_balk_blkd_tasks; |
| 114 | unsigned long n_normal_balk_boost_tasks; | 115 | /* Refused to boost: no blocked tasks. */ |
| 115 | unsigned long n_normal_balk_boosted; | 116 | unsigned long n_balk_exp_gp_tasks; |
| 116 | unsigned long n_normal_balk_notyet; | 117 | /* Refused to boost: nothing blocking GP. */ |
| 117 | unsigned long n_normal_balk_nos; | 118 | unsigned long n_balk_boost_tasks; |
| 118 | unsigned long n_exp_balk_blkd_tasks; | 119 | /* Refused to boost: already boosting. */ |
| 119 | unsigned long n_exp_balk_nos; | 120 | unsigned long n_balk_notyet; |
| 121 | /* Refused to boost: not yet time. */ | ||
| 122 | unsigned long n_balk_nos; | ||
| 123 | /* Refused to boost: not sure why, though. */ | ||
| 124 | /* This can happen due to race conditions. */ | ||
| 120 | #endif /* #ifdef CONFIG_RCU_BOOST */ | 125 | #endif /* #ifdef CONFIG_RCU_BOOST */ |
| 121 | #endif /* #ifdef CONFIG_RCU_TRACE */ | 126 | #endif /* #ifdef CONFIG_RCU_TRACE */ |
| 122 | }; | 127 | }; |
| @@ -201,7 +206,6 @@ static struct list_head *rcu_next_node_entry(struct task_struct *t) | |||
| 201 | 206 | ||
| 202 | #ifdef CONFIG_RCU_BOOST | 207 | #ifdef CONFIG_RCU_BOOST |
| 203 | static void rcu_initiate_boost_trace(void); | 208 | static void rcu_initiate_boost_trace(void); |
| 204 | static void rcu_initiate_exp_boost_trace(void); | ||
| 205 | #endif /* #ifdef CONFIG_RCU_BOOST */ | 209 | #endif /* #ifdef CONFIG_RCU_BOOST */ |
| 206 | 210 | ||
| 207 | /* | 211 | /* |
| @@ -219,41 +223,21 @@ static void show_tiny_preempt_stats(struct seq_file *m) | |||
| 219 | "N."[!rcu_preempt_ctrlblk.gp_tasks], | 223 | "N."[!rcu_preempt_ctrlblk.gp_tasks], |
| 220 | "E."[!rcu_preempt_ctrlblk.exp_tasks]); | 224 | "E."[!rcu_preempt_ctrlblk.exp_tasks]); |
| 221 | #ifdef CONFIG_RCU_BOOST | 225 | #ifdef CONFIG_RCU_BOOST |
| 222 | seq_printf(m, " ttb=%c btg=", | 226 | seq_printf(m, "%sttb=%c ntb=%lu neb=%lu nnb=%lu j=%04x bt=%04x\n", |
| 223 | "B."[!rcu_preempt_ctrlblk.boost_tasks]); | 227 | " ", |
| 224 | switch (rcu_preempt_ctrlblk.boosted_this_gp) { | 228 | "B."[!rcu_preempt_ctrlblk.boost_tasks], |
| 225 | case -1: | ||
| 226 | seq_puts(m, "exp"); | ||
| 227 | break; | ||
| 228 | case 0: | ||
| 229 | seq_puts(m, "no"); | ||
| 230 | break; | ||
| 231 | case 1: | ||
| 232 | seq_puts(m, "begun"); | ||
| 233 | break; | ||
| 234 | case 2: | ||
| 235 | seq_puts(m, "done"); | ||
| 236 | break; | ||
| 237 | default: | ||
| 238 | seq_printf(m, "?%d?", rcu_preempt_ctrlblk.boosted_this_gp); | ||
| 239 | } | ||
| 240 | seq_printf(m, " ntb=%lu neb=%lu nnb=%lu j=%04x bt=%04x\n", | ||
| 241 | rcu_preempt_ctrlblk.n_tasks_boosted, | 229 | rcu_preempt_ctrlblk.n_tasks_boosted, |
| 242 | rcu_preempt_ctrlblk.n_exp_boosts, | 230 | rcu_preempt_ctrlblk.n_exp_boosts, |
| 243 | rcu_preempt_ctrlblk.n_normal_boosts, | 231 | rcu_preempt_ctrlblk.n_normal_boosts, |
| 244 | (int)(jiffies & 0xffff), | 232 | (int)(jiffies & 0xffff), |
| 245 | (int)(rcu_preempt_ctrlblk.boost_time & 0xffff)); | 233 | (int)(rcu_preempt_ctrlblk.boost_time & 0xffff)); |
| 246 | seq_printf(m, " %s: nt=%lu gt=%lu bt=%lu b=%lu ny=%lu nos=%lu\n", | 234 | seq_printf(m, "%s: nt=%lu egt=%lu bt=%lu ny=%lu nos=%lu\n", |
| 247 | "normal balk", | 235 | " balk", |
| 248 | rcu_preempt_ctrlblk.n_normal_balk_blkd_tasks, | 236 | rcu_preempt_ctrlblk.n_balk_blkd_tasks, |
| 249 | rcu_preempt_ctrlblk.n_normal_balk_gp_tasks, | 237 | rcu_preempt_ctrlblk.n_balk_exp_gp_tasks, |
| 250 | rcu_preempt_ctrlblk.n_normal_balk_boost_tasks, | 238 | rcu_preempt_ctrlblk.n_balk_boost_tasks, |
| 251 | rcu_preempt_ctrlblk.n_normal_balk_boosted, | 239 | rcu_preempt_ctrlblk.n_balk_notyet, |
| 252 | rcu_preempt_ctrlblk.n_normal_balk_notyet, | 240 | rcu_preempt_ctrlblk.n_balk_nos); |
| 253 | rcu_preempt_ctrlblk.n_normal_balk_nos); | ||
| 254 | seq_printf(m, " exp balk: bt=%lu nos=%lu\n", | ||
| 255 | rcu_preempt_ctrlblk.n_exp_balk_blkd_tasks, | ||
| 256 | rcu_preempt_ctrlblk.n_exp_balk_nos); | ||
| 257 | #endif /* #ifdef CONFIG_RCU_BOOST */ | 241 | #endif /* #ifdef CONFIG_RCU_BOOST */ |
| 258 | } | 242 | } |
| 259 | 243 | ||
| @@ -271,25 +255,59 @@ static int rcu_boost(void) | |||
| 271 | { | 255 | { |
| 272 | unsigned long flags; | 256 | unsigned long flags; |
| 273 | struct rt_mutex mtx; | 257 | struct rt_mutex mtx; |
| 274 | struct list_head *np; | ||
| 275 | struct task_struct *t; | 258 | struct task_struct *t; |
| 259 | struct list_head *tb; | ||
| 276 | 260 | ||
| 277 | if (rcu_preempt_ctrlblk.boost_tasks == NULL) | 261 | if (rcu_preempt_ctrlblk.boost_tasks == NULL && |
| 262 | rcu_preempt_ctrlblk.exp_tasks == NULL) | ||
| 278 | return 0; /* Nothing to boost. */ | 263 | return 0; /* Nothing to boost. */ |
| 264 | |||
| 279 | raw_local_irq_save(flags); | 265 | raw_local_irq_save(flags); |
| 280 | rcu_preempt_ctrlblk.boosted_this_gp++; | 266 | |
| 281 | t = container_of(rcu_preempt_ctrlblk.boost_tasks, struct task_struct, | 267 | /* |
| 282 | rcu_node_entry); | 268 | * Recheck with irqs disabled: all tasks in need of boosting |
| 283 | np = rcu_next_node_entry(t); | 269 | * might exit their RCU read-side critical sections on their own |
| 270 | * if we are preempted just before disabling irqs. | ||
| 271 | */ | ||
| 272 | if (rcu_preempt_ctrlblk.boost_tasks == NULL && | ||
| 273 | rcu_preempt_ctrlblk.exp_tasks == NULL) { | ||
| 274 | raw_local_irq_restore(flags); | ||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | |||
| 278 | /* | ||
| 279 | * Preferentially boost tasks blocking expedited grace periods. | ||
| 280 | * This cannot starve the normal grace periods because a second | ||
| 281 | * expedited grace period must boost all blocked tasks, including | ||
| 282 | * those blocking the pre-existing normal grace period. | ||
| 283 | */ | ||
| 284 | if (rcu_preempt_ctrlblk.exp_tasks != NULL) { | ||
| 285 | tb = rcu_preempt_ctrlblk.exp_tasks; | ||
| 286 | RCU_TRACE(rcu_preempt_ctrlblk.n_exp_boosts++); | ||
| 287 | } else { | ||
| 288 | tb = rcu_preempt_ctrlblk.boost_tasks; | ||
| 289 | RCU_TRACE(rcu_preempt_ctrlblk.n_normal_boosts++); | ||
| 290 | } | ||
| 291 | RCU_TRACE(rcu_preempt_ctrlblk.n_tasks_boosted++); | ||
| 292 | |||
| 293 | /* | ||
| 294 | * We boost task t by manufacturing an rt_mutex that appears to | ||
| 295 | * be held by task t. We leave a pointer to that rt_mutex where | ||
| 296 | * task t can find it, and task t will release the mutex when it | ||
| 297 | * exits its outermost RCU read-side critical section. Then | ||
| 298 | * simply acquiring this artificial rt_mutex will boost task | ||
| 299 | * t's priority. (Thanks to tglx for suggesting this approach!) | ||
| 300 | */ | ||
| 301 | t = container_of(tb, struct task_struct, rcu_node_entry); | ||
| 284 | rt_mutex_init_proxy_locked(&mtx, t); | 302 | rt_mutex_init_proxy_locked(&mtx, t); |
| 285 | t->rcu_boost_mutex = &mtx; | 303 | t->rcu_boost_mutex = &mtx; |
| 286 | t->rcu_read_unlock_special |= RCU_READ_UNLOCK_BOOSTED; | 304 | t->rcu_read_unlock_special |= RCU_READ_UNLOCK_BOOSTED; |
| 287 | raw_local_irq_restore(flags); | 305 | raw_local_irq_restore(flags); |
| 288 | rt_mutex_lock(&mtx); | 306 | rt_mutex_lock(&mtx); |
| 289 | RCU_TRACE(rcu_preempt_ctrlblk.n_tasks_boosted++); | 307 | rt_mutex_unlock(&mtx); /* Keep lockdep happy. */ |
| 290 | rcu_preempt_ctrlblk.boosted_this_gp++; | 308 | |
| 291 | rt_mutex_unlock(&mtx); | 309 | return rcu_preempt_ctrlblk.boost_tasks != NULL || |
| 292 | return rcu_preempt_ctrlblk.boost_tasks != NULL; | 310 | rcu_preempt_ctrlblk.exp_tasks != NULL; |
| 293 | } | 311 | } |
| 294 | 312 | ||
| 295 | /* | 313 | /* |
| @@ -304,42 +322,25 @@ static int rcu_boost(void) | |||
| 304 | */ | 322 | */ |
| 305 | static int rcu_initiate_boost(void) | 323 | static int rcu_initiate_boost(void) |
| 306 | { | 324 | { |
| 307 | if (!rcu_preempt_blocked_readers_cgp()) { | 325 | if (!rcu_preempt_blocked_readers_cgp() && |
| 308 | RCU_TRACE(rcu_preempt_ctrlblk.n_normal_balk_blkd_tasks++); | 326 | rcu_preempt_ctrlblk.exp_tasks == NULL) { |
| 327 | RCU_TRACE(rcu_preempt_ctrlblk.n_balk_exp_gp_tasks++); | ||
| 309 | return 0; | 328 | |
