Commit 4ca6eaac authored by Robert Love's avatar Robert Love Committed by Linus Torvalds

[PATCH] misc. kernel preemption bits

Misc. kernel preemption-related bits.  Specifically,

 - update to Documentation/preempt-locking.txt (me)

 - preempt-safe arch/i386/kernel/ioport.c :: sys_ioperm()
   (George Anzinger)

 - remove "kernel_lock()" cruft in include/linux/smp.h
   (Andrew Morton)

 - we have a debug check in preempt_schedule that, even
   on detecting a schedule with irqs disabled, still goes
   ahead and reschedules.  We should return. (me)

 - preempt-safe net/core/dev.c :: netif_rx() (George Anzinger)
parent ac3f75f4
Proper Locking Under a Preemptible Kernel: Proper Locking Under a Preemptible Kernel:
Keeping Kernel Code Preempt-Safe Keeping Kernel Code Preempt-Safe
Robert Love <rml@tech9.net> Robert Love <rml@tech9.net>
Last Updated: 22 Jan 2002 Last Updated: 28 Aug 2002
INTRODUCTION INTRODUCTION
...@@ -112,3 +112,24 @@ critical variables. Another example: ...@@ -112,3 +112,24 @@ critical variables. Another example:
This code is not preempt-safe, but see how easily we can fix it by simply This code is not preempt-safe, but see how easily we can fix it by simply
moving the spin_lock up two lines. moving the spin_lock up two lines.
PREVENTING PREEMPTION USING INTERRUPT DISABLING
It is possible to prevent a preemption event using local_irq_disable and
local_irq_save. Note, when doing so, you must be very careful to not cause
an event that would set need_resched and result in a preemption check. When
in doubt, rely on locking or explicit preemption disabling.
Note in 2.5 interrupt disabling is now only per-CPU (e.g. local).
An additional concern is proper usage of local_irq_disable and local_irq_save.
These may be used to protect from preemption, however, on exit, if preemption
may be enabled, a test to see if preemption is required should be done. If
these are called from the spin_lock and read/write lock macros, the right thing
is done. They may also be called within a spin-lock protected region, however,
if they are ever called outside of this context, a test for preemption should
be made. Do note that calls from interrupt context or bottom half/ tasklets
are also protected by preemption locks and so may use the versions which do
not check preemption.
...@@ -55,12 +55,16 @@ static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_ ...@@ -55,12 +55,16 @@ static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_
asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
{ {
struct thread_struct * t = &current->thread; struct thread_struct * t = &current->thread;
struct tss_struct * tss = init_tss + smp_processor_id(); struct tss_struct * tss;
int ret = 0;
if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32)) if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32))
return -EINVAL; return -EINVAL;
if (turn_on && !capable(CAP_SYS_RAWIO)) if (turn_on && !capable(CAP_SYS_RAWIO))
return -EPERM; return -EPERM;
tss = init_tss + get_cpu();
/* /*
* If it's the first ioperm() call in this thread's lifetime, set the * If it's the first ioperm() call in this thread's lifetime, set the
* IO bitmap up. ioperm() is much less timing critical than clone(), * IO bitmap up. ioperm() is much less timing critical than clone(),
...@@ -69,8 +73,11 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) ...@@ -69,8 +73,11 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
if (!t->ts_io_bitmap) { if (!t->ts_io_bitmap) {
unsigned long *bitmap; unsigned long *bitmap;
bitmap = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL); bitmap = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL);
if (!bitmap) if (!bitmap) {
return -ENOMEM; ret = -ENOMEM;
goto out;
}
/* /*
* just in case ... * just in case ...
*/ */
...@@ -88,7 +95,9 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) ...@@ -88,7 +95,9 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
set_bitmap(t->ts_io_bitmap, from, num, !turn_on); set_bitmap(t->ts_io_bitmap, from, num, !turn_on);
set_bitmap(tss->io_bitmap, from, num, !turn_on); set_bitmap(tss->io_bitmap, from, num, !turn_on);
return 0; out:
put_cpu();
return ret;
} }
/* /*
......
...@@ -87,9 +87,6 @@ int cpu_up(unsigned int cpu); ...@@ -87,9 +87,6 @@ int cpu_up(unsigned int cpu);
#define smp_processor_id() 0 #define smp_processor_id() 0
#define hard_smp_processor_id() 0 #define hard_smp_processor_id() 0
#define smp_threads_ready 1 #define smp_threads_ready 1
#ifndef CONFIG_PREEMPT
#define kernel_lock()
#endif
#define smp_call_function(func,info,retry,wait) ({ 0; }) #define smp_call_function(func,info,retry,wait) ({ 0; })
static inline void smp_send_reschedule(int cpu) { } static inline void smp_send_reschedule(int cpu) { }
static inline void smp_send_reschedule_all(void) { } static inline void smp_send_reschedule_all(void) { }
......
...@@ -1039,6 +1039,7 @@ asmlinkage void preempt_schedule(void) ...@@ -1039,6 +1039,7 @@ asmlinkage void preempt_schedule(void)
printk("bad: schedule() with irqs disabled!\n"); printk("bad: schedule() with irqs disabled!\n");
show_stack(NULL); show_stack(NULL);
preempt_enable_no_resched(); preempt_enable_no_resched();
return;
} }
need_resched: need_resched:
......
...@@ -1229,19 +1229,20 @@ static void sample_queue(unsigned long dummy) ...@@ -1229,19 +1229,20 @@ static void sample_queue(unsigned long dummy)
int netif_rx(struct sk_buff *skb) int netif_rx(struct sk_buff *skb)
{ {
int this_cpu = smp_processor_id(); int this_cpu;
struct softnet_data *queue; struct softnet_data *queue;
unsigned long flags; unsigned long flags;
if (!skb->stamp.tv_sec) if (!skb->stamp.tv_sec)
do_gettimeofday(&skb->stamp); do_gettimeofday(&skb->stamp);
/* The code is rearranged so that the path is the most /*
short when CPU is congested, but is still operating. * The code is rearranged so that the path is the most
* short when CPU is congested, but is still operating.
*/ */
queue = &softnet_data[this_cpu];
local_irq_save(flags); local_irq_save(flags);
this_cpu = smp_processor_id();
queue = &softnet_data[this_cpu];
netdev_rx_stat[this_cpu].total++; netdev_rx_stat[this_cpu].total++;
if (queue->input_pkt_queue.qlen <= netdev_max_backlog) { if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
...@@ -1252,10 +1253,10 @@ int netif_rx(struct sk_buff *skb) ...@@ -1252,10 +1253,10 @@ int netif_rx(struct sk_buff *skb)
enqueue: enqueue:
dev_hold(skb->dev); dev_hold(skb->dev);
__skb_queue_tail(&queue->input_pkt_queue, skb); __skb_queue_tail(&queue->input_pkt_queue, skb);
local_irq_restore(flags);
#ifndef OFFLINE_SAMPLE #ifndef OFFLINE_SAMPLE
get_sample_stats(this_cpu); get_sample_stats(this_cpu);
#endif #endif
local_irq_restore(flags);
return queue->cng_level; return queue->cng_level;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment