2005-04-17 02:20:36 +04:00
/*
* 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>
# include <linux/proc_fs.h>
2008-08-13 02:09:03 +04:00
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include <linux/interrupt.h>
2006-01-08 12:02:17 +03:00
# include "internals.h"
2006-06-29 13:24:42 +04:00
static struct proc_dir_entry * root_irq_dir ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SMP
2008-08-13 02:09:03 +04:00
static int irq_affinity_proc_show ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
2008-08-20 07:50:05 +04:00
struct irq_desc * desc = irq_to_desc ( ( long ) m - > private ) ;
2009-01-11 08:58:08 +03:00
const struct cpumask * mask = desc - > affinity ;
2007-07-21 19:09:54 +04:00
# ifdef CONFIG_GENERIC_PENDING_IRQ
if ( desc - > status & IRQ_MOVE_PENDING )
2009-01-11 08:58:08 +03:00
mask = desc - > pending_mask ;
2007-07-21 19:09:54 +04:00
# endif
2008-08-13 02:09:03 +04:00
seq_cpumask ( m , mask ) ;
seq_putc ( m , ' \n ' ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-05-11 09:42:44 +04:00
# ifndef is_affinity_mask_valid
# define is_affinity_mask_valid(val) 1
# endif
2005-04-17 02:20:36 +04:00
int no_irq_affinity ;
2008-08-13 02:09:03 +04:00
static ssize_t irq_affinity_proc_write ( struct file * file ,
const char __user * buffer , size_t count , loff_t * pos )
2005-04-17 02:20:36 +04:00
{
2008-08-13 02:09:03 +04:00
unsigned int irq = ( int ) ( long ) PDE ( file - > f_path . dentry - > d_inode ) - > data ;
2008-12-13 13:50:26 +03:00
cpumask_var_t new_value ;
2008-08-13 02:09:03 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2008-08-20 07:50:05 +04:00
if ( ! irq_to_desc ( irq ) - > chip - > set_affinity | | no_irq_affinity | |
2007-02-16 12:27:24 +03:00
irq_balancing_disabled ( irq ) )
2005-04-17 02:20:36 +04:00
return - EIO ;
2008-12-13 13:50:26 +03:00
if ( ! alloc_cpumask_var ( & new_value , GFP_KERNEL ) )
return - ENOMEM ;
err = cpumask_parse_user ( buffer , count , new_value ) ;
2005-04-17 02:20:36 +04:00
if ( err )
2008-12-13 13:50:26 +03:00
goto free_cpumask ;
2005-04-17 02:20:36 +04:00
2009-01-03 14:50:46 +03:00
if ( ! is_affinity_mask_valid ( new_value ) ) {
2008-12-13 13:50:26 +03:00
err = - EINVAL ;
goto free_cpumask ;
}
2007-05-11 09:42:44 +04:00
2005-04-17 02:20:36 +04:00
/*
* 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 .
*/
2008-12-13 13:50:26 +03:00
if ( ! cpumask_intersects ( new_value , cpu_online_mask ) ) {
2006-01-06 11:12:21 +03:00
/* Special case for empty set - allow the architecture
code to set default SMP affinity . */
2008-12-13 13:50:26 +03:00
err = irq_select_affinity_usr ( irq ) ? - EINVAL : count ;
} else {
irq_set_affinity ( irq , new_value ) ;
err = count ;
}
free_cpumask :
free_cpumask_var ( new_value ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
2008-08-13 02:09:03 +04:00
static int irq_affinity_proc_open ( struct inode * inode , struct file * file )
2008-05-29 22:02:52 +04:00
{
2008-08-13 02:09:03 +04:00
return single_open ( file , irq_affinity_proc_show , PDE ( inode ) - > data ) ;
2008-05-29 22:02:52 +04:00
}
2008-08-13 02:09:03 +04:00
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 ,
} ;
static int default_affinity_show ( struct seq_file * m , void * v )
{
2009-01-01 02:42:26 +03:00
seq_cpumask ( m , irq_default_affinity ) ;
2008-08-13 02:09:03 +04:00
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 )
2008-05-29 22:02:52 +04:00
{
2009-01-01 02:42:26 +03:00
cpumask_var_t new_value ;
2008-08-13 02:09:03 +04:00
int err ;
2008-05-29 22:02:52 +04:00
2009-01-01 02:42:26 +03:00
if ( ! alloc_cpumask_var ( & new_value , GFP_KERNEL ) )
return - ENOMEM ;
err = cpumask_parse_user ( buffer , count , new_value ) ;
2008-05-29 22:02:52 +04:00
if ( err )
2009-01-01 02:42:26 +03:00
goto out ;
2008-05-29 22:02:52 +04:00
2009-01-01 02:42:26 +03:00
if ( ! is_affinity_mask_valid ( new_value ) ) {
err = - EINVAL ;
goto out ;
}
2008-05-29 22:02:52 +04:00
/*
* 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 .
*/
2009-01-01 02:42:26 +03:00
if ( ! cpumask_intersects ( new_value , cpu_online_mask ) ) {
err = - EINVAL ;
goto out ;
}
2008-05-29 22:02:52 +04:00
2009-01-01 02:42:26 +03:00
cpumask_copy ( irq_default_affinity , new_value ) ;
err = count ;
2008-05-29 22:02:52 +04:00
2009-01-01 02:42:26 +03:00
out :
free_cpumask_var ( new_value ) ;
return err ;
2008-05-29 22:02:52 +04:00
}
2008-08-13 02:09:03 +04:00
static int default_affinity_open ( struct inode * inode , struct file * file )
{
return single_open ( file , default_affinity_show , NULL ) ;
}
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 ,
} ;
2005-04-17 02:20:36 +04:00
# endif
2008-01-30 15:32:48 +03:00
static int irq_spurious_read ( char * page , char * * start , off_t off ,
int count , int * eof , void * data )
{
2008-08-20 07:50:05 +04:00
struct irq_desc * desc = irq_to_desc ( ( long ) data ) ;
2008-01-30 15:32:48 +03:00
return sprintf ( page , " count %u \n "
" unhandled %u \n "
" last_unhandled %u ms \n " ,
2008-08-20 07:50:05 +04:00
desc - > irq_count ,
desc - > irqs_unhandled ,
jiffies_to_msecs ( desc - > last_unhandled ) ) ;
2008-01-30 15:32:48 +03:00
}
2005-04-17 02:20:36 +04:00
# define MAX_NAMELEN 128
static int name_unique ( unsigned int irq , struct irqaction * new_action )
{
2008-08-20 07:50:05 +04:00
struct irq_desc * desc = irq_to_desc ( irq ) ;
2005-04-17 02:20:36 +04:00
struct irqaction * action ;
2007-05-08 11:27:31 +04:00
unsigned long flags ;
int ret = 1 ;
2005-04-17 02:20:36 +04:00
2007-05-08 11:27:31 +04:00
spin_lock_irqsave ( & desc - > lock , flags ) ;
for ( action = desc - > action ; action ; action = action - > next ) {
2005-04-17 02:20:36 +04:00
if ( ( action ! = new_action ) & & action - > name & &
2007-05-08 11:27:31 +04:00
! strcmp ( new_action - > name , action - > name ) ) {
ret = 0 ;
break ;
}
}
spin_unlock_irqrestore ( & desc - > lock , flags ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
void register_handler_proc ( unsigned int irq , struct irqaction * action )
{
char name [ MAX_NAMELEN ] ;
2008-08-20 07:50:05 +04:00
struct irq_desc * desc = irq_to_desc ( irq ) ;
2005-04-17 02:20:36 +04:00
2008-08-20 07:50:05 +04:00
if ( ! desc - > dir | | action - > dir | | ! action - > name | |
2005-04-17 02:20:36 +04:00
! name_unique ( irq , action ) )
return ;
memset ( name , 0 , MAX_NAMELEN ) ;
snprintf ( name , MAX_NAMELEN , " %s " , action - > name ) ;
/* create /proc/irq/1234/handler/ */
2008-08-20 07:50:05 +04:00
action - > dir = proc_mkdir ( name , desc - > dir ) ;
2005-04-17 02:20:36 +04:00
}
# undef MAX_NAMELEN
# define MAX_NAMELEN 10
2008-08-20 07:50:11 +04:00
void register_irq_proc ( unsigned int irq , struct irq_desc * desc )
2005-04-17 02:20:36 +04:00
{
char name [ MAX_NAMELEN ] ;
2008-01-30 15:32:48 +03:00
struct proc_dir_entry * entry ;
2005-04-17 02:20:36 +04:00
2008-08-20 07:50:05 +04:00
if ( ! root_irq_dir | | ( desc - > chip = = & no_irq_chip ) | | desc - > dir )
2005-04-17 02:20:36 +04:00
return ;
memset ( name , 0 , MAX_NAMELEN ) ;
sprintf ( name , " %d " , irq ) ;
/* create /proc/irq/1234 */
2008-08-20 07:50:05 +04:00
desc - > dir = proc_mkdir ( name , root_irq_dir ) ;
2009-10-26 23:28:11 +03:00
if ( ! desc - > dir )
return ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SMP
2008-08-13 02:09:03 +04:00
/* create /proc/irq/<irq>/smp_affinity */
2008-08-20 07:50:05 +04:00
proc_create_data ( " smp_affinity " , 0600 , desc - > dir ,
2008-08-13 02:09:03 +04:00
& irq_affinity_proc_fops , ( void * ) ( long ) irq ) ;
2005-04-17 02:20:36 +04:00
# endif
2008-01-30 15:32:48 +03:00
2008-08-20 07:50:05 +04:00
entry = create_proc_entry ( " spurious " , 0444 , desc - > dir ) ;
2008-01-30 15:32:48 +03:00
if ( entry ) {
entry - > data = ( void * ) ( long ) irq ;
entry - > read_proc = irq_spurious_read ;
}
2005-04-17 02:20:36 +04:00
}
# undef MAX_NAMELEN
void unregister_handler_proc ( unsigned int irq , struct irqaction * action )
{
2008-08-20 07:50:05 +04:00
if ( action - > dir ) {
struct irq_desc * desc = irq_to_desc ( irq ) ;
2008-10-16 11:55:00 +04:00
2008-08-20 07:50:05 +04:00
remove_proc_entry ( action - > dir - > name , desc - > dir ) ;
}
2005-04-17 02:20:36 +04:00
}
2008-10-22 03:49:09 +04:00
static void register_default_affinity_proc ( void )
2008-05-29 22:02:52 +04:00
{
# ifdef CONFIG_SMP
2008-08-13 02:09:03 +04:00
proc_create ( " irq/default_smp_affinity " , 0600 , NULL ,
& default_affinity_proc_fops ) ;
2008-05-29 22:02:52 +04:00
# endif
}
2005-04-17 02:20:36 +04:00
void init_irq_proc ( void )
{
2008-08-20 07:50:11 +04:00
unsigned int irq ;
struct irq_desc * desc ;
2005-04-17 02:20:36 +04:00
/* create /proc/irq */
root_irq_dir = proc_mkdir ( " irq " , NULL ) ;
if ( ! root_irq_dir )
return ;
2008-05-29 22:02:52 +04:00
register_default_affinity_proc ( ) ;
2005-04-17 02:20:36 +04:00
/*
* Create entries for all existing IRQs .
*/
2008-12-06 05:58:31 +03:00
for_each_irq_desc ( irq , desc ) {
if ( ! desc )
continue ;
2008-08-20 07:50:11 +04:00
register_irq_proc ( irq , desc ) ;
2008-12-06 05:58:31 +03:00
}
2005-04-17 02:20:36 +04:00
}