commitc291ee6221upstream. Since the rework of the sparse interrupt code to actually free the unused interrupt descriptors there exists a race between the /proc interfaces to the irq subsystem and the code which frees the interrupt descriptor. CPU0 CPU1 show_interrupts() desc = irq_to_desc(X); free_desc(desc) remove_from_radix_tree(); kfree(desc); raw_spinlock_irq(&desc->lock); /proc/interrupts is the only interface which can actively corrupt kernel memory via the lock access. /proc/stat can only read from freed memory. Extremly hard to trigger, but possible. The interfaces in /proc/irq/N/ are not affected by this because the removal of the proc file is serialized in procfs against concurrent readers/writers. The removal happens before the descriptor is freed. For architectures which have CONFIG_SPARSE_IRQ=n this is a non issue as the descriptor is never freed. It's merely cleared out with the irq descriptor lock held. So any concurrent proc access will either see the old correct value or the cleared out ones. Protect the lookup and access to the irq descriptor in show_interrupts() with the sparse_irq_lock. Provide kstat_irqs_usr() which is protecting the lookup and access with sparse_irq_lock and switch /proc/stat to use it. Document the existing kstat_irqs interfaces so it's clear that the caller needs to take care about protection. The users of these interfaces are either not affected due to SPARSE_IRQ=n or already protected against removal. Fixes:1f5a5b87f7"genirq: Implement a sane sparse_irq allocator" Signed-off-by: Thomas Gleixner <tglx@linutronix.de> [bwh: Backported to 3.2: - Adjust context - Handle the CONFIG_GENERIC_HARDIRQS=n case] Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
131 lines
3.0 KiB
C
131 lines
3.0 KiB
C
#ifndef _LINUX_KERNEL_STAT_H
|
|
#define _LINUX_KERNEL_STAT_H
|
|
|
|
#include <linux/smp.h>
|
|
#include <linux/threads.h>
|
|
#include <linux/percpu.h>
|
|
#include <linux/cpumask.h>
|
|
#include <linux/interrupt.h>
|
|
#include <asm/irq.h>
|
|
#include <asm/cputime.h>
|
|
|
|
/*
|
|
* 'kernel_stat.h' contains the definitions needed for doing
|
|
* some kernel statistics (CPU usage, context switches ...),
|
|
* used by rstatd/perfmeter
|
|
*/
|
|
|
|
struct cpu_usage_stat {
|
|
cputime64_t user;
|
|
cputime64_t nice;
|
|
cputime64_t system;
|
|
cputime64_t softirq;
|
|
cputime64_t irq;
|
|
cputime64_t idle;
|
|
cputime64_t iowait;
|
|
cputime64_t steal;
|
|
cputime64_t guest;
|
|
cputime64_t guest_nice;
|
|
};
|
|
|
|
struct kernel_stat {
|
|
struct cpu_usage_stat cpustat;
|
|
#ifndef CONFIG_GENERIC_HARDIRQS
|
|
unsigned int irqs[NR_IRQS];
|
|
#endif
|
|
unsigned long irqs_sum;
|
|
unsigned int softirqs[NR_SOFTIRQS];
|
|
};
|
|
|
|
DECLARE_PER_CPU(struct kernel_stat, kstat);
|
|
|
|
#define kstat_cpu(cpu) per_cpu(kstat, cpu)
|
|
/* Must have preemption disabled for this to be meaningful. */
|
|
#define kstat_this_cpu __get_cpu_var(kstat)
|
|
|
|
extern unsigned long long nr_context_switches(void);
|
|
|
|
#ifndef CONFIG_GENERIC_HARDIRQS
|
|
|
|
struct irq_desc;
|
|
|
|
static inline void kstat_incr_irqs_this_cpu(unsigned int irq,
|
|
struct irq_desc *desc)
|
|
{
|
|
__this_cpu_inc(kstat.irqs[irq]);
|
|
__this_cpu_inc(kstat.irqs_sum);
|
|
}
|
|
|
|
static inline unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)
|
|
{
|
|
return kstat_cpu(cpu).irqs[irq];
|
|
}
|
|
#else
|
|
#include <linux/irq.h>
|
|
extern unsigned int kstat_irqs_cpu(unsigned int irq, int cpu);
|
|
|
|
#define kstat_incr_irqs_this_cpu(irqno, DESC) \
|
|
do { \
|
|
__this_cpu_inc(*(DESC)->kstat_irqs); \
|
|
__this_cpu_inc(kstat.irqs_sum); \
|
|
} while (0)
|
|
|
|
#endif
|
|
|
|
static inline void kstat_incr_softirqs_this_cpu(unsigned int irq)
|
|
{
|
|
__this_cpu_inc(kstat.softirqs[irq]);
|
|
}
|
|
|
|
static inline unsigned int kstat_softirqs_cpu(unsigned int irq, int cpu)
|
|
{
|
|
return kstat_cpu(cpu).softirqs[irq];
|
|
}
|
|
|
|
/*
|
|
* Number of interrupts per specific IRQ source, since bootup
|
|
*/
|
|
#ifndef CONFIG_GENERIC_HARDIRQS
|
|
static inline unsigned int kstat_irqs(unsigned int irq)
|
|
{
|
|
unsigned int sum = 0;
|
|
int cpu;
|
|
|
|
for_each_possible_cpu(cpu)
|
|
sum += kstat_irqs_cpu(irq, cpu);
|
|
|
|
return sum;
|
|
}
|
|
static inline unsigned int kstat_irqs_usr(unsigned int irq)
|
|
{
|
|
return kstat_irqs(irq);
|
|
}
|
|
#else
|
|
extern unsigned int kstat_irqs(unsigned int irq);
|
|
extern unsigned int kstat_irqs_usr(unsigned int irq);
|
|
#endif
|
|
|
|
/*
|
|
* Number of interrupts per cpu, since bootup
|
|
*/
|
|
static inline unsigned int kstat_cpu_irqs_sum(unsigned int cpu)
|
|
{
|
|
return kstat_cpu(cpu).irqs_sum;
|
|
}
|
|
|
|
/*
|
|
* Lock/unlock the current runqueue - to extract task statistics:
|
|
*/
|
|
extern unsigned long long task_delta_exec(struct task_struct *);
|
|
|
|
extern void account_user_time(struct task_struct *, cputime_t, cputime_t);
|
|
extern void account_system_time(struct task_struct *, int, cputime_t, cputime_t);
|
|
extern void account_steal_time(cputime_t);
|
|
extern void account_idle_time(cputime_t);
|
|
|
|
extern void account_process_tick(struct task_struct *, int user);
|
|
extern void account_steal_ticks(unsigned long ticks);
|
|
extern void account_idle_ticks(unsigned long ticks);
|
|
|
|
#endif /* _LINUX_KERNEL_STAT_H */
|