proc.c 9.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9
/*
 * linux/kernel/irq/proc.c
 *
 * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
 *
 * This file contains the /proc/irq/ handling code.
 */

#include <linux/irq.h>
10
#include <linux/gfp.h>
L
Linus Torvalds 已提交
11
#include <linux/proc_fs.h>
12
#include <linux/seq_file.h>
L
Linus Torvalds 已提交
13
#include <linux/interrupt.h>
14
#include <linux/kernel_stat.h>
L
Linus Torvalds 已提交
15

A
Adrian Bunk 已提交
16 17
#include "internals.h"

18
static struct proc_dir_entry *root_irq_dir;
L
Linus Torvalds 已提交
19 20 21

#ifdef CONFIG_SMP

22
static int irq_affinity_proc_show(struct seq_file *m, void *v)
L
Linus Torvalds 已提交
23
{
24
	struct irq_desc *desc = irq_to_desc((long)m->private);
25
	const struct cpumask *mask = desc->irq_data.affinity;
26 27

#ifdef CONFIG_GENERIC_PENDING_IRQ
28
	if (irqd_is_setaffinity_pending(&desc->irq_data))
29
		mask = desc->pending_mask;
30
#endif
31 32 33
	seq_cpumask(m, mask);
	seq_putc(m, '\n');
	return 0;
L
Linus Torvalds 已提交
34 35
}

36 37 38 39 40 41
static int irq_affinity_hint_proc_show(struct seq_file *m, void *v)
{
	struct irq_desc *desc = irq_to_desc((long)m->private);
	unsigned long flags;
	cpumask_var_t mask;

42
	if (!zalloc_cpumask_var(&mask, GFP_KERNEL))
43 44 45 46 47 48 49 50 51 52 53 54 55 56
		return -ENOMEM;

	raw_spin_lock_irqsave(&desc->lock, flags);
	if (desc->affinity_hint)
		cpumask_copy(mask, desc->affinity_hint);
	raw_spin_unlock_irqrestore(&desc->lock, flags);

	seq_cpumask(m, mask);
	seq_putc(m, '\n');
	free_cpumask_var(mask);

	return 0;
}

57 58 59 60
#ifndef is_affinity_mask_valid
#define is_affinity_mask_valid(val) 1
#endif

L
Linus Torvalds 已提交
61
int no_irq_affinity;
62 63
static ssize_t irq_affinity_proc_write(struct file *file,
		const char __user *buffer, size_t count, loff_t *pos)
L
Linus Torvalds 已提交
64
{
65
	unsigned int irq = (int)(long)PDE(file->f_path.dentry->d_inode)->data;
66
	cpumask_var_t new_value;
67
	int err;
L
Linus Torvalds 已提交
68

69
	if (!irq_to_desc(irq)->irq_data.chip->irq_set_affinity || no_irq_affinity ||
70
	    irq_balancing_disabled(irq))
L
Linus Torvalds 已提交
71 72
		return -EIO;

73 74 75 76
	if (!alloc_cpumask_var(&new_value, GFP_KERNEL))
		return -ENOMEM;

	err = cpumask_parse_user(buffer, count, new_value);
L
Linus Torvalds 已提交
77
	if (err)
78
		goto free_cpumask;
L
Linus Torvalds 已提交
79

80
	if (!is_affinity_mask_valid(new_value)) {
81 82 83
		err = -EINVAL;
		goto free_cpumask;
	}
84

L
Linus Torvalds 已提交
85 86 87 88 89
	/*
	 * Do not allow disabling IRQs completely - it's a too easy
	 * way to make the system unusable accidentally :-) At least
	 * one online CPU still has to be targeted.
	 */
90
	if (!cpumask_intersects(new_value, cpu_online_mask)) {
91 92
		/* Special case for empty set - allow the architecture
		   code to set default SMP affinity. */
93
		err = irq_select_affinity_usr(irq, new_value) ? -EINVAL : count;
94 95 96 97 98 99 100 101
	} else {
		irq_set_affinity(irq, new_value);
		err = count;
	}

free_cpumask:
	free_cpumask_var(new_value);
	return err;
L
Linus Torvalds 已提交
102 103
}

104
static int irq_affinity_proc_open(struct inode *inode, struct file *file)
105
{
106
	return single_open(file, irq_affinity_proc_show, PDE(inode)->data);
107 108
}

109 110 111 112 113
static int irq_affinity_hint_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, irq_affinity_hint_proc_show, PDE(inode)->data);
}

114 115 116 117 118 119 120 121
static const struct file_operations irq_affinity_proc_fops = {
	.open		= irq_affinity_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
	.write		= irq_affinity_proc_write,
};

122 123 124 125 126 127 128
static const struct file_operations irq_affinity_hint_proc_fops = {
	.open		= irq_affinity_hint_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

129 130
static int default_affinity_show(struct seq_file *m, void *v)
{
R
Rusty Russell 已提交
131
	seq_cpumask(m, irq_default_affinity);
132 133 134 135 136 137
	seq_putc(m, '\n');
	return 0;
}

static ssize_t default_affinity_write(struct file *file,
		const char __user *buffer, size_t count, loff_t *ppos)
138
{
R
Rusty Russell 已提交
139
	cpumask_var_t new_value;
140
	int err;
141

R
Rusty Russell 已提交
142 143 144 145
	if (!alloc_cpumask_var(&new_value, GFP_KERNEL))
		return -ENOMEM;

	err = cpumask_parse_user(buffer, count, new_value);
146
	if (err)
R
Rusty Russell 已提交
147
		goto out;
148

R
Rusty Russell 已提交
149 150 151 152
	if (!is_affinity_mask_valid(new_value)) {
		err = -EINVAL;
		goto out;
	}
153 154 155 156 157 158

	/*
	 * Do not allow disabling IRQs completely - it's a too easy
	 * way to make the system unusable accidentally :-) At least
	 * one online CPU still has to be targeted.
	 */
R
Rusty Russell 已提交
159 160 161 162
	if (!cpumask_intersects(new_value, cpu_online_mask)) {
		err = -EINVAL;
		goto out;
	}
163

R
Rusty Russell 已提交
164 165
	cpumask_copy(irq_default_affinity, new_value);
	err = count;
166

R
Rusty Russell 已提交
167 168 169
out:
	free_cpumask_var(new_value);
	return err;
170
}
171 172 173

static int default_affinity_open(struct inode *inode, struct file *file)
{
174
	return single_open(file, default_affinity_show, PDE(inode)->data);
175 176 177 178 179 180 181 182 183
}

static const struct file_operations default_affinity_proc_fops = {
	.open		= default_affinity_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
	.write		= default_affinity_write,
};
184 185 186 187 188

static int irq_node_proc_show(struct seq_file *m, void *v)
{
	struct irq_desc *desc = irq_to_desc((long) m->private);

189
	seq_printf(m, "%d\n", desc->irq_data.node);
190 191 192 193 194 195 196 197 198 199 200 201 202 203
	return 0;
}

static int irq_node_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, irq_node_proc_show, PDE(inode)->data);
}

static const struct file_operations irq_node_proc_fops = {
	.open		= irq_node_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};
L
Linus Torvalds 已提交
204 205
#endif

206
static int irq_spurious_proc_show(struct seq_file *m, void *v)
207
{
208 209 210 211 212 213 214 215 216 217
	struct irq_desc *desc = irq_to_desc((long) m->private);

	seq_printf(m, "count %u\n" "unhandled %u\n" "last_unhandled %u ms\n",
		   desc->irq_count, desc->irqs_unhandled,
		   jiffies_to_msecs(desc->last_unhandled));
	return 0;
}

static int irq_spurious_proc_open(struct inode *inode, struct file *file)
{
218
	return single_open(file, irq_spurious_proc_show, PDE(inode)->data);
219 220
}

221 222 223 224 225 226 227
static const struct file_operations irq_spurious_proc_fops = {
	.open		= irq_spurious_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

L
Linus Torvalds 已提交
228 229 230 231
#define MAX_NAMELEN 128

static int name_unique(unsigned int irq, struct irqaction *new_action)
{
232
	struct irq_desc *desc = irq_to_desc(irq);
L
Linus Torvalds 已提交
233
	struct irqaction *action;
234 235
	unsigned long flags;
	int ret = 1;
L
Linus Torvalds 已提交
236

237
	raw_spin_lock_irqsave(&desc->lock, flags);
238
	for (action = desc->action ; action; action = action->next) {
L
Linus Torvalds 已提交
239
		if ((action != new_action) && action->name &&
240 241 242 243 244
				!strcmp(new_action->name, action->name)) {
			ret = 0;
			break;
		}
	}
245
	raw_spin_unlock_irqrestore(&desc->lock, flags);
246
	return ret;
L
Linus Torvalds 已提交
247 248 249 250 251
}

void register_handler_proc(unsigned int irq, struct irqaction *action)
{
	char name [MAX_NAMELEN];
252
	struct irq_desc *desc = irq_to_desc(irq);
L
Linus Torvalds 已提交
253

254
	if (!desc->dir || action->dir || !action->name ||
L
Linus Torvalds 已提交
255 256 257 258 259 260 261
					!name_unique(irq, action))
		return;

	memset(name, 0, MAX_NAMELEN);
	snprintf(name, MAX_NAMELEN, "%s", action->name);

	/* create /proc/irq/1234/handler/ */
262
	action->dir = proc_mkdir(name, desc->dir);
L
Linus Torvalds 已提交
263 264 265 266 267 268
}

#undef MAX_NAMELEN

#define MAX_NAMELEN 10

269
void register_irq_proc(unsigned int irq, struct irq_desc *desc)
L
Linus Torvalds 已提交
270 271 272
{
	char name [MAX_NAMELEN];

273
	if (!root_irq_dir || (desc->irq_data.chip == &no_irq_chip) || desc->dir)
L
Linus Torvalds 已提交
274 275 276 277 278 279
		return;

	memset(name, 0, MAX_NAMELEN);
	sprintf(name, "%d", irq);

	/* create /proc/irq/1234 */
280
	desc->dir = proc_mkdir(name, root_irq_dir);
281 282
	if (!desc->dir)
		return;
L
Linus Torvalds 已提交
283 284

#ifdef CONFIG_SMP
285
	/* create /proc/irq/<irq>/smp_affinity */
286
	proc_create_data("smp_affinity", 0600, desc->dir,
287
			 &irq_affinity_proc_fops, (void *)(long)irq);
288

289 290 291 292
	/* create /proc/irq/<irq>/affinity_hint */
	proc_create_data("affinity_hint", 0400, desc->dir,
			 &irq_affinity_hint_proc_fops, (void *)(long)irq);

293 294
	proc_create_data("node", 0444, desc->dir,
			 &irq_node_proc_fops, (void *)(long)irq);
L
Linus Torvalds 已提交
295
#endif
296

297 298
	proc_create_data("spurious", 0444, desc->dir,
			 &irq_spurious_proc_fops, (void *)(long)irq);
L
Linus Torvalds 已提交
299 300
}

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
void unregister_irq_proc(unsigned int irq, struct irq_desc *desc)
{
	char name [MAX_NAMELEN];

	if (!root_irq_dir || !desc->dir)
		return;
#ifdef CONFIG_SMP
	remove_proc_entry("smp_affinity", desc->dir);
	remove_proc_entry("affinity_hint", desc->dir);
	remove_proc_entry("node", desc->dir);
#endif
	remove_proc_entry("spurious", desc->dir);

	memset(name, 0, MAX_NAMELEN);
	sprintf(name, "%u", irq);
	remove_proc_entry(name, root_irq_dir);
}

L
Linus Torvalds 已提交
319 320 321 322
#undef MAX_NAMELEN

void unregister_handler_proc(unsigned int irq, struct irqaction *action)
{
323 324
	if (action->dir) {
		struct irq_desc *desc = irq_to_desc(irq);
325

326 327
		remove_proc_entry(action->dir->name, desc->dir);
	}
L
Linus Torvalds 已提交
328 329
}

R
roel kluin 已提交
330
static void register_default_affinity_proc(void)
331 332
{
#ifdef CONFIG_SMP
333 334
	proc_create("irq/default_smp_affinity", 0600, NULL,
		    &default_affinity_proc_fops);
335 336 337
#endif
}

L
Linus Torvalds 已提交
338 339
void init_irq_proc(void)
{
340 341
	unsigned int irq;
	struct irq_desc *desc;
L
Linus Torvalds 已提交
342 343 344 345 346 347

	/* create /proc/irq */
	root_irq_dir = proc_mkdir("irq", NULL);
	if (!root_irq_dir)
		return;

348 349
	register_default_affinity_proc();

L
Linus Torvalds 已提交
350 351 352
	/*
	 * Create entries for all existing IRQs.
	 */
353 354 355 356
	for_each_irq_desc(irq, desc) {
		if (!desc)
			continue;

357
		register_irq_proc(irq, desc);
358
	}
L
Linus Torvalds 已提交
359 360
}

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
#ifdef CONFIG_GENERIC_IRQ_SHOW

int __weak arch_show_interrupts(struct seq_file *p, int prec)
{
	return 0;
}

int show_interrupts(struct seq_file *p, void *v)
{
	static int prec;

	unsigned long flags, any_count = 0;
	int i = *(loff_t *) v, j;
	struct irqaction *action;
	struct irq_desc *desc;

	if (i > nr_irqs)
		return 0;

	if (i == nr_irqs)
		return arch_show_interrupts(p, prec);

	/* print header and calculate the width of the first column */
	if (i == 0) {
		for (prec = 3, j = 1000; prec < 10 && j <= nr_irqs; ++prec)
			j *= 10;

		seq_printf(p, "%*s", prec + 8, "");
		for_each_online_cpu(j)
			seq_printf(p, "CPU%-8d", j);
		seq_putc(p, '\n');
	}

	desc = irq_to_desc(i);
	if (!desc)
		return 0;

	raw_spin_lock_irqsave(&desc->lock, flags);
	for_each_online_cpu(j)
		any_count |= kstat_irqs_cpu(i, j);
	action = desc->action;
	if (!action && !any_count)
		goto out;

	seq_printf(p, "%*d: ", prec, i);
	for_each_online_cpu(j)
		seq_printf(p, "%10u ", kstat_irqs_cpu(i, j));
	seq_printf(p, " %8s", desc->irq_data.chip->name);
	seq_printf(p, "-%-8s", desc->name);

	if (action) {
		seq_printf(p, "  %s", action->name);
		while ((action = action->next) != NULL)
			seq_printf(p, ", %s", action->name);
	}

	seq_putc(p, '\n');
out:
	raw_spin_unlock_irqrestore(&desc->lock, flags);
	return 0;
}
#endif