]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/blob - rt-patches/0101-hotplug-Lightweight-get-online-cpus.patch
Fix memguard and related syscalls
[hercules2020/nv-tegra/linux-4.4.git] / rt-patches / 0101-hotplug-Lightweight-get-online-cpus.patch
1 From 710fa174c245e3567701d8558e63b3f23e7d083a Mon Sep 17 00:00:00 2001
2 From: Thomas Gleixner <tglx@linutronix.de>
3 Date: Wed, 15 Jun 2011 12:36:06 +0200
4 Subject: [PATCH 101/366] hotplug: Lightweight get online cpus
5
6 get_online_cpus() is a heavy weight function which involves a global
7 mutex. migrate_disable() wants a simpler construct which prevents only
8 a CPU from going doing while a task is in a migrate disabled section.
9
10 Implement a per cpu lockless mechanism, which serializes only in the
11 real unplug case on a global mutex. That serialization affects only
12 tasks on the cpu which should be brought down.
13
14 Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
15 ---
16  include/linux/cpu.h |   7 ++--
17  kernel/cpu.c        | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++-
18  2 files changed, 122 insertions(+), 4 deletions(-)
19
20 diff --git a/include/linux/cpu.h b/include/linux/cpu.h
21 index 06c4b6f..493ef08 100644
22 --- a/include/linux/cpu.h
23 +++ b/include/linux/cpu.h
24 @@ -222,9 +222,6 @@ static inline void smpboot_thread_init(void)
25  #endif /* CONFIG_SMP */
26  extern struct bus_type cpu_subsys;
27  
28 -static inline void pin_current_cpu(void) { }
29 -static inline void unpin_current_cpu(void) { }
30 -
31  #ifdef CONFIG_HOTPLUG_CPU
32  /* Stop CPUs going up and down. */
33  
34 @@ -234,6 +231,8 @@ extern void get_online_cpus(void);
35  extern void put_online_cpus(void);
36  extern void cpu_hotplug_disable(void);
37  extern void cpu_hotplug_enable(void);
38 +extern void pin_current_cpu(void);
39 +extern void unpin_current_cpu(void);
40  #define hotcpu_notifier(fn, pri)       cpu_notifier(fn, pri)
41  #define __hotcpu_notifier(fn, pri)     __cpu_notifier(fn, pri)
42  #define register_hotcpu_notifier(nb)   register_cpu_notifier(nb)
43 @@ -251,6 +250,8 @@ static inline void cpu_hotplug_done(void) {}
44  #define put_online_cpus()      do { } while (0)
45  #define cpu_hotplug_disable()  do { } while (0)
46  #define cpu_hotplug_enable()   do { } while (0)
47 +static inline void pin_current_cpu(void) { }
48 +static inline void unpin_current_cpu(void) { }
49  #define hotcpu_notifier(fn, pri)       do { (void)(fn); } while (0)
50  #define __hotcpu_notifier(fn, pri)     do { (void)(fn); } while (0)
51  /* These aren't inline functions due to a GCC bug. */
52 diff --git a/kernel/cpu.c b/kernel/cpu.c
53 index 35bb9c6..cfac14c 100644
54 --- a/kernel/cpu.c
55 +++ b/kernel/cpu.c
56 @@ -91,6 +91,100 @@ static struct {
57  #define cpuhp_lock_acquire()      lock_map_acquire(&cpu_hotplug.dep_map)
58  #define cpuhp_lock_release()      lock_map_release(&cpu_hotplug.dep_map)
59  
60 +struct hotplug_pcp {
61 +       struct task_struct *unplug;
62 +       int refcount;
63 +       struct completion synced;
64 +};
65 +
66 +static DEFINE_PER_CPU(struct hotplug_pcp, hotplug_pcp);
67 +
68 +/**
69 + * pin_current_cpu - Prevent the current cpu from being unplugged
70 + *
71 + * Lightweight version of get_online_cpus() to prevent cpu from being
72 + * unplugged when code runs in a migration disabled region.
73 + *
74 + * Must be called with preemption disabled (preempt_count = 1)!
75 + */
76 +void pin_current_cpu(void)
77 +{
78 +       struct hotplug_pcp *hp = this_cpu_ptr(&hotplug_pcp);
79 +
80 +retry:
81 +       if (!hp->unplug || hp->refcount || preempt_count() > 1 ||
82 +           hp->unplug == current) {
83 +               hp->refcount++;
84 +               return;
85 +       }
86 +       preempt_enable();
87 +       mutex_lock(&cpu_hotplug.lock);
88 +       mutex_unlock(&cpu_hotplug.lock);
89 +       preempt_disable();
90 +       goto retry;
91 +}
92 +
93 +/**
94 + * unpin_current_cpu - Allow unplug of current cpu
95 + *
96 + * Must be called with preemption or interrupts disabled!
97 + */
98 +void unpin_current_cpu(void)
99 +{
100 +       struct hotplug_pcp *hp = this_cpu_ptr(&hotplug_pcp);
101 +
102 +       WARN_ON(hp->refcount <= 0);
103 +
104 +       /* This is safe. sync_unplug_thread is pinned to this cpu */
105 +       if (!--hp->refcount && hp->unplug && hp->unplug != current)
106 +               wake_up_process(hp->unplug);
107 +}
108 +
109 +/*
110 + * FIXME: Is this really correct under all circumstances ?
111 + */
112 +static int sync_unplug_thread(void *data)
113 +{
114 +       struct hotplug_pcp *hp = data;
115 +
116 +       preempt_disable();
117 +       hp->unplug = current;
118 +       set_current_state(TASK_UNINTERRUPTIBLE);
119 +       while (hp->refcount) {
120 +               schedule_preempt_disabled();
121 +               set_current_state(TASK_UNINTERRUPTIBLE);
122 +       }
123 +       set_current_state(TASK_RUNNING);
124 +       preempt_enable();
125 +       complete(&hp->synced);
126 +       return 0;
127 +}
128 +
129 +/*
130 + * Start the sync_unplug_thread on the target cpu and wait for it to
131 + * complete.
132 + */
133 +static int cpu_unplug_begin(unsigned int cpu)
134 +{
135 +       struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu);
136 +       struct task_struct *tsk;
137 +
138 +       init_completion(&hp->synced);
139 +       tsk = kthread_create(sync_unplug_thread, hp, "sync_unplug/%d\n", cpu);
140 +       if (IS_ERR(tsk))
141 +               return (PTR_ERR(tsk));
142 +       kthread_bind(tsk, cpu);
143 +       wake_up_process(tsk);
144 +       wait_for_completion(&hp->synced);
145 +       return 0;
146 +}
147 +
148 +static void cpu_unplug_done(unsigned int cpu)
149 +{
150 +       struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu);
151 +
152 +       hp->unplug = NULL;
153 +}
154  
155  void get_online_cpus(void)
156  {
157 @@ -340,13 +434,14 @@ static int take_cpu_down(void *_param)
158  /* Requires cpu_add_remove_lock to be held */
159  static int _cpu_down(unsigned int cpu, int tasks_frozen)
160  {
161 -       int err, nr_calls = 0;
162 +       int mycpu, err, nr_calls = 0;
163         void *hcpu = (void *)(long)cpu;
164         unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
165         struct take_cpu_down_param tcd_param = {
166                 .mod = mod,
167                 .hcpu = hcpu,
168         };
169 +       cpumask_var_t cpumask;
170  
171         if (num_online_cpus() == 1)
172                 return -EBUSY;
173 @@ -354,7 +449,27 @@ static int _cpu_down(unsigned int cpu, int tasks_frozen)
174         if (!cpu_online(cpu))
175                 return -EINVAL;
176  
177 +       /* Move the downtaker off the unplug cpu */
178 +       if (!alloc_cpumask_var(&cpumask, GFP_KERNEL))
179 +               return -ENOMEM;
180 +       cpumask_andnot(cpumask, cpu_online_mask, cpumask_of(cpu));
181 +       set_cpus_allowed_ptr(current, cpumask);
182 +       free_cpumask_var(cpumask);
183 +       preempt_disable();
184 +       mycpu = smp_processor_id();
185 +       if (mycpu == cpu) {
186 +               printk(KERN_ERR "Yuck! Still on unplug CPU\n!");
187 +               preempt_enable();
188 +               return -EBUSY;
189 +       }
190 +       preempt_enable();
191 +
192         cpu_hotplug_begin();
193 +       err = cpu_unplug_begin(cpu);
194 +       if (err) {
195 +               printk("cpu_unplug_begin(%d) failed\n", cpu);
196 +               goto out_cancel;
197 +       }
198  
199         err = __cpu_notify(CPU_DOWN_PREPARE | mod, hcpu, -1, &nr_calls);
200         if (err) {
201 @@ -427,6 +542,8 @@ static int _cpu_down(unsigned int cpu, int tasks_frozen)
202         check_for_tasks(cpu);
203  
204  out_release:
205 +       cpu_unplug_done(cpu);
206 +out_cancel:
207         cpu_hotplug_done();
208         trace_sched_cpu_hotplug(cpu, err, 0);
209         if (!err)
210 -- 
211 1.9.1
212