📄 chandev.c
字号:
/* * drivers/s390/misc/chandev.c * * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) * * Generic channel device initialisation support. */#define TRUE 1#define FALSE 0#define __KERNEL_SYSCALLS__#include <linux/module.h>#include <linux/config.h>#include <linux/types.h>#include <linux/ctype.h>#include <asm/uaccess.h>#include <linux/slab.h>#include <asm/irq.h>#include <linux/init.h>#include <linux/unistd.h>#include <asm/chandev.h>#include <linux/proc_fs.h>#include <linux/vmalloc.h>#include <asm/s390dyn.h>#include <asm/queue.h>#include <linux/kmod.h>#ifndef MIN#define MIN(a,b) ((a<b)?a:b)#endif#ifndef MAX#define MAX(a,b) ((a>b)?a:b)#endiftypedef struct chandev_model_info chandev_model_info;struct chandev_model_info{ struct chandev_model_info *next; chandev_type chan_type; s32 cu_type; /* control unit type -1 = don't care */ s16 cu_model; /* control unit model -1 = don't care */ s32 dev_type; /* device type -1 = don't care */ s16 dev_model; /* device model -1 = don't care */ u8 max_port_no; int auto_msck_recovery; u8 default_checksum_received_ip_pkts; u8 default_use_hw_stats; /* where available e.g. lcs */ devreg_t drinfo;};typedef struct chandev chandev;struct chandev{ struct chandev *next; chandev_model_info *model_info; chandev_subchannel_info sch; int owned;};typedef struct chandev_noauto_range chandev_noauto_range;struct chandev_noauto_range{ struct chandev_noauto_range *next; u16 lo_devno; u16 hi_devno;};typedef struct chandev_force chandev_force;struct chandev_force{ struct chandev_force *next; chandev_type chan_type; s32 devif_num; /* -1 don't care, -2 we are forcing a range e.g. tr0 implies 0 */ u16 read_lo_devno; u16 write_hi_devno; u16 data_devno; /* only used by gigabit ethernet */ s32 memory_usage_in_k; s16 port_protocol_no; /* where available e.g. lcs,-1 don't care */ u8 checksum_received_ip_pkts; u8 use_hw_stats; /* where available e.g. lcs */ /* claw specific stuff */ chandev_claw_info claw;};typedef struct chandev_probelist chandev_probelist;struct chandev_probelist{ struct chandev_probelist *next; chandev_probefunc probefunc; chandev_shutdownfunc shutdownfunc; chandev_msck_notification_func msck_notfunc; chandev_type chan_type; int devices_found;};#define default_msck_bits ((1<<(chandev_status_not_oper-1))|(1<<(chandev_status_no_path-1))|(1<<(chandev_status_revalidate-1))|(1<<(chandev_status_gone-1)))static char *msck_status_strs[]={ "good", "not_operational", "no_path", "revalidate", "device_gone"};typedef struct chandev_msck_range chandev_msck_range;struct chandev_msck_range{ struct chandev_msck_range *next; u16 lo_devno; u16 hi_devno; int auto_msck_recovery;};static chandev_msck_range *chandev_msck_range_head=NULL;typedef struct chandev_irqinfo chandev_irqinfo;struct chandev_irqinfo{ chandev_irqinfo *next; chandev_subchannel_info sch; chandev_msck_status msck_status; void (*handler)(int, void *, struct pt_regs *); unsigned long irqflags; void *dev_id; char devname[0];};chandev_irqinfo *chandev_irqinfo_head=NULL;typedef struct chandev_parms chandev_parms;struct chandev_parms{ chandev_parms *next; chandev_type chan_type; u16 lo_devno; u16 hi_devno; char parmstr[0];};static chandev_type chandev_persistent=0; chandev_parms *chandev_parms_head=NULL;typedef struct chandev_activelist chandev_activelist;struct chandev_activelist{ struct chandev_activelist *next; chandev_irqinfo *read_irqinfo; chandev_irqinfo *write_irqinfo; chandev_irqinfo *data_irqinfo; chandev_probefunc probefunc; chandev_shutdownfunc shutdownfunc; chandev_msck_notification_func msck_notfunc; chandev_unregfunc unreg_dev; chandev_type chan_type; u8 port_no; chandev_category category; s32 memory_usage_in_k; void *dev_ptr; char devname[0];};static chandev_model_info *chandev_models_head=NULL;/* The only reason chandev_head is a queue is so that net devices *//* will be by default named in the order of their irqs */static qheader chandev_head={NULL,NULL};static chandev_noauto_range *chandev_noauto_head=NULL;static chandev_force *chandev_force_head=NULL;static chandev_probelist *chandev_probelist_head=NULL;static chandev_activelist *chandev_activelist_head=NULL;#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)int chandev_use_devno_names=FALSE;#endifstatic int chandev_cautious_auto_detect=TRUE;static atomic_t chandev_conf_read=ATOMIC_INIT(FALSE);static atomic_t chandev_initialised=ATOMIC_INIT(FALSE);static unsigned long chandev_last_machine_check;static struct tq_struct chandev_msck_task_tq;static atomic_t chandev_msck_thread_lock;static atomic_t chandev_new_msck;static unsigned long chandev_last_startmsck_list_update;typedef enum{ chandev_start, chandev_first_tag=chandev_start, chandev_msck, chandev_num_notify_tags} chandev_userland_notify_tag;static char *userland_notify_strs[]={ "start", "machine_check"};typedef struct chandev_userland_notify_list chandev_userland_notify_list;struct chandev_userland_notify_list{ chandev_userland_notify_list *next; chandev_userland_notify_tag tag; chandev_msck_status prev_status; chandev_msck_status curr_status; char devname[0];};static chandev_userland_notify_list *chandev_userland_notify_head=NULL;static void chandev_read_conf_if_necessary(void);static void chandev_read_conf(void);#if LINUX_VERSION_CODE >=KERNEL_VERSION(2,3,0)typedef struct net_device net_device;#elsetypedef struct device net_device;static inline void init_waitqueue_head(wait_queue_head_t *q){ *q=NULL;}#endif#if LINUX_VERSION_CODE<KERNEL_VERSION(2,3,45)static __inline__ void netif_stop_queue(net_device *dev){ dev->tbusy=1;}static __inline__ void netif_start_queue(net_device *dev){ dev->tbusy=0;}#endif#define CHANDEV_INVALID_LOCK_OWNER -1static long chandev_lock_owner;static int chandev_lock_cnt; static spinlock_t chandev_spinlock;#define CHANDEV_LOCK_DEBUG 0#if CHANDEV_LOCK_DEBUG && !defined(CONFIG_ARCH_S390X)#define CHANDEV_BACKTRACE_LOOPCNT 10void *chandev_first_lock_addr[CHANDEV_BACKTRACE_LOOPCNT], *chandev_last_lock_addr[CHANDEV_BACKTRACE_LOOPCNT], *chandev_last_unlock_addr[CHANDEV_BACKTRACE_LOOPCNT];#define CHANDEV_BACKTRACE(variable) \memset((variable),0,sizeof(void *)*CHANDEV_BACKTRACE_LOOPCNT); \(variable)[0]=__builtin_return_address(0); \if(((long)variable[0])&0x80000000) \{ \(variable)[1]=__builtin_return_address(1); \if(((long)variable[1])&0x80000000) \{ \(variable)[2]=__builtin_return_address(2); \if(((long)variable[2])&0x80000000) \{ \(variable)[3]=__builtin_return_address(3); \if(((long)variable[3])&0x80000000) \{ \(variable)[4]=__builtin_return_address(4); \if(((long)variable[4])&0x80000000) \{ \(variable)[5]=__builtin_return_address(5); \if(((long)variable[5])&0x80000000) \{ \(variable)[6]=__builtin_return_address(6); \if(((long)variable[6])&0x80000000) \{ \(variable)[7]=__builtin_return_address(7); \if(((long)variable[7])&0x80000000) \{ \(variable)[8]=__builtin_return_address(8); \if(((long)variable[8])&0x80000000) \{ \(variable)[9]=__builtin_return_address(9); \} \} \} \} \} \} \} \} \}#else#define CHANDEV_BACKTRACE(variable)#endiftypedef struct chandev_not_oper_struct chandev_not_oper_struct;struct chandev_not_oper_struct{ chandev_not_oper_struct *next; int irq; int status;};/* May as well try to keep machine checks in the order they happen so * we use qheader for chandev_not_oper_head instead of list. */static qheader chandev_not_oper_head={NULL,NULL};static spinlock_t chandev_not_oper_spinlock;#define chandev_interrupt_check() \if(in_interrupt()) \ printk(KERN_WARNING __FUNCTION__ " called under interrupt this shouldn't happen\n")#define for_each(variable,head) \for((variable)=(head);(variable)!=NULL;(variable)=(variable)->next)#define for_each_allow_delete(variable,nextmember,head) \for((variable)=(head),(nextmember)=((head) ? (head)->next:NULL); \(variable)!=NULL; (variable)=(nextmember),(nextmember)=((nextmember) ? (nextmember->next) : NULL))#define for_each_allow_delete2(variable,nextmember,head) \for((variable)=(head);(variable)!=NULL;(variable)=(nextmember))static void chandev_lock(void){ eieio(); chandev_interrupt_check(); if(chandev_lock_owner!=(long)current) { while(!spin_trylock(&chandev_spinlock)) schedule(); chandev_lock_cnt=1; chandev_lock_owner=(long)current; CHANDEV_BACKTRACE(chandev_first_lock_addr) } else { chandev_lock_cnt++; CHANDEV_BACKTRACE(chandev_last_lock_addr) } if(chandev_lock_cnt<0||chandev_lock_cnt>100) { printk("odd lock_cnt %d lcs_chan_lock",chandev_lock_cnt); chandev_lock_cnt=1; }}static int chandev_full_unlock(void){ int ret_lock_cnt=chandev_lock_cnt; chandev_lock_cnt=0; chandev_lock_owner=CHANDEV_INVALID_LOCK_OWNER; spin_unlock(&chandev_spinlock); return(ret_lock_cnt);}static void chandev_unlock(void){ if(chandev_lock_owner!=(long)current) printk("chandev_unlock: current=%lx" " chandev_lock_owner=%lx chandev_lock_cnt=%d\n", (long)current, chandev_lock_owner, chandev_lock_cnt); CHANDEV_BACKTRACE(chandev_last_unlock_addr) if(--chandev_lock_cnt==0) { chandev_lock_owner=CHANDEV_INVALID_LOCK_OWNER; spin_unlock(&chandev_spinlock); } if(chandev_lock_cnt<0) { printk("odd lock_cnt=%d in chan_unlock",chandev_lock_cnt); chandev_full_unlock(); }}void *chandev_alloc(size_t size){ void *mem=kmalloc(size,GFP_ATOMIC); if(mem) memset(mem,0,size); return(mem);}static void chandev_add_to_list(list **listhead,void *member){ chandev_lock(); add_to_list(listhead,member); chandev_unlock();}static void chandev_queuemember(qheader *qhead,void *member){ chandev_lock(); enqueue_tail(qhead,(queue *)member); chandev_unlock();}static int chandev_remove_from_list(list **listhead,list *member){ int retval; chandev_lock(); retval=remove_from_list(listhead,member); chandev_unlock(); return(retval);}static int chandev_remove_from_queue(qheader *qhead,queue *member){ int retval; chandev_lock(); retval=remove_from_queue(qhead,member); chandev_unlock(); return(retval);}void chandev_free_listmember(list **listhead,list *member){ chandev_lock(); if(member) { if(chandev_remove_from_list(listhead,member)) kfree(member); else printk(KERN_CRIT"chandev_free_listmember detected nonexistant" "listmember listhead=%p member %p\n",listhead,member); } chandev_unlock();}void chandev_free_queuemember(qheader *qhead,queue *member){ chandev_lock(); if(member) { if(chandev_remove_from_queue(qhead,member)) kfree(member); else printk(KERN_CRIT"chandev_free_queuemember detected nonexistant" "queuemember qhead=%p member %p\n",qhead,member); } chandev_unlock();}void chandev_free_all_list(list **listhead){ list *head; chandev_lock(); while((head=remove_listhead(listhead))) kfree(head); chandev_unlock();}void chandev_free_all_queue(qheader *qhead){ chandev_lock(); while(qhead->head) chandev_free_queuemember(qhead,qhead->head); chandev_unlock();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -