patch-2.4.8 linux/drivers/s390/misc/chandev.c
Next file: linux/drivers/s390/net/Makefile
Previous file: linux/drivers/s390/idals.c
Back to the patch index
Back to the overall index
- Lines: 2935
- Date:
Wed Jul 25 14:12:02 2001
- Orig file:
v2.4.7/linux/drivers/s390/misc/chandev.c
- Orig date:
Wed Apr 11 19:02:28 2001
diff -u --recursive --new-file v2.4.7/linux/drivers/s390/misc/chandev.c linux/drivers/s390/misc/chandev.c
@@ -24,6 +24,14 @@
#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)
+#endif
+
+
typedef struct chandev_model_info chandev_model_info;
struct chandev_model_info
@@ -35,7 +43,9 @@
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;
+ int auto_msck_recovery;
+ u8 default_checksum_received_ip_pkts;
+ u8 default_use_hw_stats; /* where available e.g. lcs */
devreg_t drinfo;
};
@@ -44,12 +54,7 @@
{
struct chandev *next;
chandev_model_info *model_info;
- u16 cu_type; /* control unit type */
- u8 cu_model; /* control unit model */
- u16 dev_type; /* device type */
- u8 dev_model; /* device model */
- u16 devno;
- unsigned int irq;
+ chandev_subchannel_info sch;
int owned;
};
@@ -66,28 +71,32 @@
{
struct chandev_force *next;
chandev_type chan_type;
- s32 devif_num; /* -1 don't care e.g. tr0 implies 0 */
- u16 read_devno;
- u16 write_devno;
+ 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_reoperfunc reoperfunc;
- chandev_type chan_type;
- int devices_found;
+ 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<<(not_oper-1))|(1<<(no_path-1))|(1<<(revalidate-1))|(1<<(gone-1)))
+#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[]=
@@ -113,14 +122,13 @@
typedef struct chandev_irqinfo chandev_irqinfo;
struct chandev_irqinfo
{
- chandev_irqinfo *next;
- chandev_msck_status msck_status;
- u16 devno;
- unsigned int irq;
- void (*handler)(int, void *, struct pt_regs *);
- unsigned long irqflags;
- void *dev_id;
- char devname[0];
+ 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];
};
@@ -131,34 +139,33 @@
{
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;
- u16 cu_type; /* control unit type */
- u8 cu_model; /* control unit model */
- u16 dev_type; /* device type */
- u8 dev_model; /* device model */
- chandev_probefunc probefunc;
- chandev_shutdownfunc shutdownfunc;
- chandev_reoperfunc reoperfunc;
- chandev_unregfunc unreg_dev;
- chandev_type chan_type;
- u8 port_no;
- chandev_category category;
- int saved_busy_flag;
-
-
- void *dev_ptr;
- char devname[0];
+ 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];
};
@@ -172,10 +179,11 @@
static chandev_probelist *chandev_probelist_head=NULL;
static chandev_activelist *chandev_activelist_head=NULL;
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
-static int use_devno_names=FALSE;
+int chandev_use_devno_names=FALSE;
#endif
-static int chandev_conf_read=FALSE;
-static int chandev_initialised=FALSE;
+static 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;
@@ -186,22 +194,38 @@
static atomic_t chandev_new_msck;
static unsigned long chandev_last_startmsck_list_update;
-typedef struct chandev_startmsck_list chandev_startmsck_list;
-struct chandev_startmsck_list
+
+typedef enum
+{
+ chandev_start,
+ chandev_first_tag=chandev_start,
+ chandev_msck,
+ chandev_num_notify_tags
+} chandev_userland_notify_tag;
+
+static char *userland_notify_strs[]=
{
- chandev_startmsck_list *next;
- chandev_msck_status pre_recovery_action_status;
- chandev_msck_status post_recovery_action_status;
+ "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_startmsck_list *startlist_head=NULL;
-static chandev_startmsck_list *mscklist_head=NULL;
+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)
@@ -233,7 +257,56 @@
static long chandev_lock_owner;
static int chandev_lock_cnt;
static spinlock_t chandev_spinlock;
-void *chandev_firstlock_addr,*chandev_lastlock_addr;
+#define CHANDEV_LOCK_DEBUG 0
+#if CHANDEV_LOCK_DEBUG && !defined(CONFIG_ARCH_S390X)
+#define CHANDEV_BACKTRACE_LOOPCNT 10
+void *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)
+#endif
+
+
typedef struct chandev_not_oper_struct chandev_not_oper_struct;
@@ -250,7 +323,6 @@
*/
static qheader chandev_not_oper_head={NULL,NULL};
static spinlock_t chandev_not_oper_spinlock;
-static char exec_script[]="/bin/chandev";
#define chandev_interrupt_check() \
if(in_interrupt()) \
@@ -260,22 +332,30 @@
#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)
{
- chandev_interrupt_check();
eieio();
+ chandev_interrupt_check();
if(chandev_lock_owner!=(long)current)
{
- spin_lock(&chandev_spinlock);
+ while(!spin_trylock(&chandev_spinlock))
+ schedule();
chandev_lock_cnt=1;
chandev_lock_owner=(long)current;
- chandev_firstlock_addr=__builtin_return_address(0);
+ CHANDEV_BACKTRACE(chandev_first_lock_addr)
}
else
{
chandev_lock_cnt++;
- chandev_lastlock_addr=__builtin_return_address(0);
+ CHANDEV_BACKTRACE(chandev_last_lock_addr)
}
if(chandev_lock_cnt<0||chandev_lock_cnt>100)
{
@@ -301,6 +381,7 @@
(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;
@@ -315,13 +396,6 @@
}
-void chandev_relock(int saved_lock_cnt)
-{
-
- chandev_lock();
- chandev_lock_cnt=saved_lock_cnt;
-}
-
void *chandev_alloc(size_t size)
{
@@ -369,6 +443,7 @@
void chandev_free_listmember(list **listhead,list *member)
{
+ chandev_lock();
if(member)
{
if(chandev_remove_from_list(listhead,member))
@@ -377,18 +452,21 @@
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_listmember detected nonexistant"
- "listmember qhead=%p member %p\n",qhead,member);
+ printk(KERN_CRIT"chandev_free_queuemember detected nonexistant"
+ "queuemember qhead=%p member %p\n",qhead,member);
}
+ chandev_unlock();
}
@@ -397,27 +475,45 @@
{
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();
}
+static void chandev_wait_for_root_fs(void)
+{
+ wait_queue_head_t wait;
+ init_waitqueue_head(&wait);
+ /* We need to wait till there is a root filesystem */
+ while(init_task.fs->root==NULL)
+ {
+ sleep_on_timeout(&wait,HZ);
+ }
+}
+/* We are now hotplug compliant i.e. */
+/* we typically get called in /sbin/hotplug chandev our parameters */
static int chandev_exec_start_script(void *unused)
{
char **argv,*tempname;
int retval=-ENOMEM;
- int loopcnt,argc;
+ int argc,loopcnt;
size_t allocsize;
- chandev_startmsck_list *member;
+ chandev_userland_notify_list *member;
wait_queue_head_t wait;
+ int have_tag[chandev_num_notify_tags]={FALSE,};
+ chandev_userland_notify_tag tagidx;
static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
init_waitqueue_head(&wait);
@@ -427,17 +523,33 @@
{
sleep_on_timeout(&wait,HZ);
}
+ if(!chandev_userland_notify_head)
+ return(0);
chandev_lock();
- argc=1;
- if(startlist_head)
- argc++;
- for_each(member,startlist_head)
- argc++;
- if(mscklist_head)
- argc+=3;
- for_each(member,mscklist_head)
- argc++;
+ argc=2;
+ for(tagidx=chandev_first_tag;tagidx<chandev_num_notify_tags;tagidx++)
+ {
+ for_each(member,chandev_userland_notify_head)
+ {
+ if(member->tag==tagidx)
+ {
+ switch(tagidx)
+ {
+ case chandev_start:
+ argc++;
+ break;
+ case chandev_msck:
+ argc+=3;
+ break;
+ default:
+ }
+ if(have_tag[tagidx]==FALSE)
+ argc++;
+ have_tag[tagidx]=TRUE;
+ }
+ }
+ }
allocsize=(argc+1)*sizeof(char *);
/* Warning possible stack overflow */
/* We can't kmalloc the parameters here as execve will */
@@ -446,58 +558,51 @@
if(argv)
{
memset(argv,0,allocsize);
- argc=0;
- argv[argc++]=exec_script;
- if(startlist_head)
- argv[argc++]="start";
- for_each(member,startlist_head)
- {
- tempname=alloca(strlen(member->devname)+1);
- if(tempname)
- {
- strcpy(tempname,member->devname);
- argv[argc++]=tempname;
- }
- else
- goto Fail;
- }
- if(mscklist_head)
- argv[argc++]="machine_check";
- for_each(member,mscklist_head)
+ argv[0]=hotplug_path;
+ argv[1]="chandev";
+ argc=2;
+ for(tagidx=chandev_first_tag;tagidx<chandev_num_notify_tags;tagidx++)
{
- tempname=alloca(strlen(member->devname)+1);
- if(tempname)
+ if(have_tag[tagidx])
{
- strcpy(tempname,member->devname);
- argv[argc++]=tempname;
+ argv[argc++]=userland_notify_strs[tagidx];
+ for_each(member,chandev_userland_notify_head)
+ {
+ if(member->tag==tagidx)
+ {
+ tempname=alloca(strlen(member->devname)+1);
+ if(tempname)
+ {
+ strcpy(tempname,member->devname);
+ argv[argc++]=tempname;
+ }
+ else
+ goto Fail;
+ if(member->tag==chandev_msck)
+ {
+ argv[argc++]=msck_status_strs[member->prev_status];
+ argv[argc++]=msck_status_strs[member->curr_status];
+ }
+ }
+ }
}
- else
- goto Fail;
- argv[loopcnt++]=msck_status_strs[member->pre_recovery_action_status];
- argv[loopcnt++]=msck_status_strs[member->post_recovery_action_status];
}
- chandev_free_all_list((list **)&startlist_head);
- chandev_free_all_list((list **)&mscklist_head);
+ chandev_free_all_list((list **)&chandev_userland_notify_head);
chandev_unlock();
-
- /* We need to wait till there is a root filesystem */
- while(init_task.fs->root==NULL)
- {
- sleep_on_timeout(&wait,HZ);
- }
+ chandev_wait_for_root_fs();
/* We are basically execve'ing here there normally is no */
/* return */
- retval=exec_usermodehelper(exec_script, argv, envp);
+ retval=exec_usermodehelper(hotplug_path, argv, envp);
goto Fail2;
}
Fail:
chandev_unlock();
Fail2:
- /* We don't really need to report /bin/chandev not existing */
+ /* We don't really need to report /sbin/hotplug not existing */
if(retval!=-ENOENT)
printk("chandev_exec_start_script failed retval=%d\n",retval);
- return(0);
+ return(retval);
}
@@ -513,27 +618,25 @@
}
-static int chandev_add_to_startmsck_list(chandev_startmsck_list **listhead,char *devname,
-chandev_msck_status pre_recovery_action_status,chandev_msck_status post_recovery_action_status)
+static int chandev_add_to_userland_notify_list(chandev_userland_notify_tag tag,
+char *devname, chandev_msck_status prev_status,chandev_msck_status curr_status)
{
- chandev_startmsck_list *member;
+ chandev_userland_notify_list *member,*nextmember;
int pid;
chandev_lock();
/* remove operations still outstanding for this device */
- for_each(member,startlist_head)
+ for_each_allow_delete(member,nextmember,chandev_userland_notify_head)
if(strcmp(member->devname,devname)==0)
- chandev_remove_from_list((list **)&startlist_head,(list *)member);
- for_each(member,mscklist_head)
- if(strcmp(member->devname,devname)==0)
- chandev_remove_from_list((list **)&mscklist_head,(list *)member);
+ chandev_free_listmember((list **)&chandev_userland_notify_head,(list *)member);
- if((member=chandev_allocstr(devname,offsetof(chandev_startmsck_list,devname))))
+ if((member=chandev_allocstr(devname,offsetof(chandev_userland_notify_list,devname))))
{
- member->pre_recovery_action_status=pre_recovery_action_status;
- member->post_recovery_action_status=post_recovery_action_status;
- add_to_list((list **)listhead,(list *)member);
+ member->tag=tag;
+ member->prev_status=prev_status;
+ member->curr_status=curr_status;
+ add_to_list((list **)&chandev_userland_notify_head,(list *)member);
chandev_last_startmsck_list_update=jiffies;
chandev_unlock();
pid = kernel_thread(chandev_exec_start_script,NULL,SIGCHLD);
@@ -596,7 +699,7 @@
{
chandev_irqinfo *curr_irqinfo;
for_each(curr_irqinfo,chandev_irqinfo_head)
- if(irq==curr_irqinfo->irq)
+ if(irq==curr_irqinfo->sch.irq)
return(curr_irqinfo);
return(NULL);
}
@@ -606,7 +709,7 @@
chandev *curr_chandev;
for_each(curr_chandev,(chandev *)chandev_head.head)
- if(curr_chandev->irq==irq)
+ if(curr_chandev->sch.irq==irq)
{
return(curr_chandev);
}
@@ -619,8 +722,9 @@
for_each(curr_device,chandev_activelist_head)
{
- if(curr_device->read_irqinfo->irq==irq||
- curr_device->write_irqinfo->irq==irq)
+ if(curr_device->read_irqinfo->sch.irq==irq||
+ curr_device->write_irqinfo->sch.irq==irq||
+ (curr_device->data_irqinfo&&curr_device->data_irqinfo->sch.irq==irq))
return(curr_device);
}
return(NULL);
@@ -630,16 +734,50 @@
void chandev_remove_irqinfo_by_irq(unsigned int irq)
{
chandev_irqinfo *remove_irqinfo;
+ chandev_activelist *curr_device;
chandev_lock();
/* remove any orphan irqinfo left lying around. */
if((remove_irqinfo=chandev_get_irqinfo_by_irq(irq)))
- chandev_remove_from_list((list **)&chandev_irqinfo_head,
+ {
+ for_each(curr_device,chandev_activelist_head)
+ {
+ if(curr_device->read_irqinfo==remove_irqinfo)
+ {
+ curr_device->read_irqinfo=NULL;
+ break;
+ }
+ if(curr_device->write_irqinfo==remove_irqinfo)
+ {
+ curr_device->write_irqinfo=NULL;
+ break;
+ }
+ if(curr_device->data_irqinfo&&curr_device->data_irqinfo==remove_irqinfo)
+ {
+ curr_device->data_irqinfo=NULL;
+ break;
+ }
+ }
+ chandev_free_listmember((list **)&chandev_irqinfo_head,
(list *)remove_irqinfo);
+ }
chandev_unlock();
}
+int chandev_add_schib_info(int irq,chandev_subchannel_info *sch)
+{
+ schib_t *new_schib;
+
+ if((new_schib=s390_get_schib(irq)))
+ {
+ sch->pim=new_schib->pmcw.pim;
+ memcpy(&sch->chpid,&new_schib->pmcw.chpid,sizeof(sch->chpid));
+ return(0);
+ }
+ return(-ENODEV);
+}
+
int chandev_request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
@@ -674,10 +812,16 @@
kfree(new_irqinfo);
else
{
- new_irqinfo->irq=irq;
+ new_irqinfo->msck_status=chandev_status_good;
+ new_irqinfo->sch.devno=devinfo.devno;
+ new_irqinfo->sch.irq=irq;
+ new_irqinfo->sch.cu_type=devinfo.sid_data.cu_type; /* control unit type */
+ new_irqinfo->sch.cu_model=devinfo.sid_data.cu_model; /* control unit model */
+ new_irqinfo->sch.dev_type=devinfo.sid_data.dev_type; /* device type */
+ new_irqinfo->sch.dev_model=devinfo.sid_data.dev_model; /* device model */
+ chandev_add_schib_info(irq,&new_irqinfo->sch);
new_irqinfo->handler=handler;
new_irqinfo->dev_id=dev_id;
- new_irqinfo->devno=devinfo.devno;
chandev_add_to_list((list **)&chandev_irqinfo_head,new_irqinfo);
}
}
@@ -689,13 +833,32 @@
return(retval);
}
+/* This should be safe to call even multiple times. */
void chandev_free_irq(unsigned int irq, void *dev_id)
{
+ s390_dev_info_t devinfo;
+ int err;
+
/* remove any orphan irqinfo left lying around. */
chandev_remove_irqinfo_by_irq(irq);
- free_irq(irq,dev_id);
+ if((err=get_dev_info_by_irq(irq,&devinfo)))
+ {
+ printk("chandev_free_irq get_dev_info_by_irq reported err=%X on irq %d\n"
+ "should not happen\n",err,irq);
+ return;
+ }
+ if(devinfo.status&DEVSTAT_DEVICE_OWNED)
+ free_irq(irq,dev_id);
}
+/* This should be safe even if chandev_free_irq is already called by the device */
+void chandev_free_irq_by_irqinfo(chandev_irqinfo *irqinfo)
+{
+ if(irqinfo)
+ chandev_free_irq(irqinfo->sch.irq,irqinfo->dev_id);
+}
+
+
void chandev_sprint_type_model(char *buff,s32 type,s16 model)
{
@@ -716,37 +879,66 @@
chandev_sprint_type_model(&buff[strlen(buff)],dev_type,dev_model);
}
-void chandev_remove_parms(chandev_type chan_type,int exact_match)
+void chandev_remove_parms(chandev_type chan_type,int exact_match,int lo_devno)
{
- chandev_parms *curr_parms;
+ chandev_parms *curr_parms,*next_parms;
chandev_lock();
- for_each(curr_parms,chandev_parms_head)
+ for_each_allow_delete(curr_parms,next_parms,chandev_parms_head)
{
- if((chan_type&(curr_parms->chan_type)&&!exact_match)||
- (chan_type==(curr_parms->chan_type)&&exact_match))
- chandev_free_listmember((list **)&chandev_parms_head,(list *)curr_parms);
+ if(((chan_type&(curr_parms->chan_type)&&!exact_match)||
+ (chan_type==(curr_parms->chan_type)&&exact_match))&&
+ (lo_devno==-1||lo_devno==curr_parms->lo_devno))
+ chandev_free_listmember((list **)&chandev_parms_head,(list *)curr_parms);
}
chandev_unlock();
}
-void chandev_add_parms(chandev_type chan_type,char *parmstr)
+
+void chandev_add_parms(chandev_type chan_type,u16 lo_devno,u16 hi_devno,char *parmstr)
{
- chandev_parms *new_parms;
-
- if((new_parms=chandev_allocstr(parmstr,offsetof(chandev_parms,parmstr))))
+ chandev_parms *parms;
+
+ if(lo_devno>hi_devno)
{
- chandev_remove_parms(chan_type,TRUE);
- new_parms->chan_type=chan_type;
- chandev_add_to_list((list **)&chandev_parms_head,(void *)new_parms);
+ printk("chandev_add_parms detected bad device range lo_devno=0x%04x hi_devno=0x%04x\n,",
+ (int)lo_devno,(int)hi_devno);
+ return;
+ }
+ chandev_lock();
+ for_each(parms,chandev_parms_head)
+ {
+ if(chan_type&(parms->chan_type))
+ {
+ u16 lomax=MAX(parms->lo_devno,lo_devno),
+ himin=MIN(parms->hi_devno,lo_devno);
+ if(lomax<=himin)
+ {
+ chandev_unlock();
+ printk("chandev_add_parms detected overlapping "
+ "parameter definitions for chan_type=0x%02x"
+ " lo_devno=0x%04x hi_devno=0x%04x\n,"
+ " do a del_parms.",chan_type,(int)lo_devno,(int)hi_devno);
+ return;
+ }
+ }
+ }
+ chandev_unlock();
+ if((parms=chandev_allocstr(parmstr,offsetof(chandev_parms,parmstr))))
+ {
+ parms->chan_type=chan_type;
+ parms->lo_devno=lo_devno;
+ parms->hi_devno=hi_devno;
+ chandev_add_to_list((list **)&chandev_parms_head,(void *)parms);
}
else
- printk("chandev_add_parmstr memory request failed\n");
+ printk("chandev_add_parms memory request failed\n");
}
void chandev_add_model(chandev_type chan_type,s32 cu_type,s16 cu_model,
- s32 dev_type,s16 dev_model,u8 max_port_no,int auto_msck_recovery)
+ s32 dev_type,s16 dev_model,u8 max_port_no,int auto_msck_recovery,
+ u8 default_checksum_received_ip_pkts,u8 default_use_hw_stats)
{
chandev_model_info *newmodel;
int err;
@@ -762,7 +954,8 @@
newmodel->dev_model=dev_model;
newmodel->max_port_no=max_port_no;
newmodel->auto_msck_recovery=auto_msck_recovery;
-
+ newmodel->default_checksum_received_ip_pkts=default_checksum_received_ip_pkts;
+ newmodel->default_use_hw_stats=default_use_hw_stats; /* where available e.g. lcs */
if(cu_type==-1&&dev_type==-1)
{
chandev_sprint_devinfo(buff,newmodel->cu_type,newmodel->cu_model,
@@ -771,17 +964,15 @@
kfree(newmodel);
return;
}
- /* We ignore errors as they are likely to
- occur owing to incompatibilities with
- Ingos layer
- */
drinfo->flag=DEVREG_TYPE_DEVCHARS;
- if(dev_model==-1)
- drinfo->flag|=(dev_type==-1 ? DEVREG_NO_DEV_INFO:DEVREG_MATCH_DEV_TYPE);
- if(cu_model==-1)
- drinfo->flag|=(cu_type==-1 ? DEVREG_NO_CU_INFO:DEVREG_MATCH_CU_TYPE);
- else if(dev_model!=-1&&cu_type!=-1)
- drinfo->flag|=DEVREG_EXACT_MATCH;
+ if(cu_type!=-1)
+ drinfo->flag|=DEVREG_MATCH_CU_TYPE;
+ if(cu_model!=-1)
+ drinfo->flag|=DEVREG_MATCH_CU_MODEL;
+ if(dev_type!=-1)
+ drinfo->flag|=DEVREG_MATCH_DEV_TYPE;
+ if(dev_model!=-1)
+ drinfo->flag|=DEVREG_MATCH_DEV_MODEL;
drinfo->ci.hc.ctype=cu_type;
drinfo->ci.hc.cmode=cu_model;
drinfo->ci.hc.dtype=dev_type;
@@ -813,10 +1004,10 @@
void chandev_remove_model(chandev_model_info *model)
{
- chandev *curr_chandev;
+ chandev *curr_chandev,*next_chandev;
chandev_lock();
- for_each(curr_chandev,(chandev *)chandev_head.head)
+ for_each_allow_delete(curr_chandev,next_chandev,(chandev *)chandev_head.head)
if(curr_chandev->model_info==model)
chandev_remove(curr_chandev);
if(model->drinfo.oper_func)
@@ -835,10 +1026,10 @@
void chandev_del_model(s32 cu_type,s16 cu_model,s32 dev_type,s16 dev_model)
{
- chandev_model_info *curr_model;
+ chandev_model_info *curr_model,*next_model;
chandev_lock();
- for_each(curr_model,chandev_models_head)
+ for_each_allow_delete(curr_model,next_model,chandev_models_head)
if((curr_model->cu_type==cu_type||cu_type==-1)&&
(curr_model->cu_model==cu_model||cu_model==-1)&&
(curr_model->dev_type==dev_type||dev_type==-1)&&
@@ -849,31 +1040,34 @@
static void chandev_init_default_models(void)
{
- /* P390/Planter 3172 emulation assume maximum 16 to be safe. */
- chandev_add_model(lcs,0x3088,0x1,-1,-1,15,default_msck_bits);
+ /* Usually P390/Planter 3172 emulation assume maximum 16 to be safe. */
+ chandev_add_model(chandev_type_lcs,0x3088,0x1,-1,-1,15,default_msck_bits,FALSE,FALSE);
/* 3172/2216 Paralell the 2216 allows 16 ports per card the */
/* the original 3172 only allows 4 we will assume the max of 16 */
- chandev_add_model(lcs|ctc,0x3088,0x8,-1,-1,15,default_msck_bits);
+ chandev_add_model(chandev_type_lcs|chandev_type_ctc,0x3088,0x8,-1,-1,15,default_msck_bits,FALSE,FALSE);
/* 3172/2216 Escon serial the 2216 allows 16 ports per card the */
/* the original 3172 only allows 4 we will assume the max of 16 */
- chandev_add_model(lcs|escon,0x3088,0x1F,-1,-1,15,default_msck_bits);
+ chandev_add_model(chandev_type_lcs|chandev_type_escon,0x3088,0x1F,-1,-1,15,default_msck_bits,FALSE,FALSE);
/* Only 2 ports allowed on OSA2 cards model 0x60 */
- chandev_add_model(lcs,0x3088,0x60,-1,-1,1,default_msck_bits);
- /* qeth has relative adapter concept so we give it 16 */
- chandev_add_model(qeth,0x1731,0x1,0x1732,0x1,15,default_msck_bits);
+ chandev_add_model(chandev_type_lcs,0x3088,0x60,-1,-1,1,default_msck_bits,FALSE,FALSE);
+ /* qeth gigabit ethernet */
+ chandev_add_model(chandev_type_qeth,0x1731,0x1,0x1732,0x1,0,default_msck_bits,FALSE,FALSE);
+ chandev_add_model(chandev_type_qeth,0x1731,0x5,0x1732,0x5,0,default_msck_bits,FALSE,FALSE);
/* Osa-D we currently aren't too emotionally involved with this */
- chandev_add_model(osad,0x3088,0x62,-1,-1,0,default_msck_bits);
+ chandev_add_model(chandev_type_osad,0x3088,0x62,-1,-1,0,default_msck_bits,FALSE,FALSE);
+ /* claw */
+ chandev_add_model(chandev_type_claw,0x3088,0x61,-1,-1,0,default_msck_bits,FALSE,FALSE);
}
void chandev_del_noauto(u16 devno)
{
- chandev_noauto_range *curr_noauto;
+ chandev_noauto_range *curr_noauto,*next_noauto;
chandev_lock();
- for_each(curr_noauto,chandev_noauto_head)
+ for_each_allow_delete(curr_noauto,next_noauto,chandev_noauto_head)
if(curr_noauto->lo_devno<=devno&&curr_noauto->hi_devno>=devno)
chandev_free_listmember((list **)&chandev_noauto_head,(list *)curr_noauto);
chandev_unlock();
@@ -881,9 +1075,9 @@
void chandev_del_msck(u16 devno)
{
- chandev_msck_range *curr_msck_range;
+ chandev_msck_range *curr_msck_range,*next_msck_range;
chandev_lock();
- for_each(curr_msck_range,chandev_msck_range_head)
+ for_each_allow_delete(curr_msck_range,next_msck_range,chandev_msck_range_head)
if(curr_msck_range->lo_devno<=devno&&curr_msck_range->hi_devno>=devno)
chandev_free_listmember((list **)&chandev_msck_range_head,(list *)curr_msck_range);
chandev_unlock();
@@ -897,12 +1091,13 @@
if((new_chandev=chandev_alloc(sizeof(chandev))))
{
new_chandev->model_info=newmodelinfo;
- new_chandev->cu_type=newdevinfo->sid_data.cu_type; /* control unit type */
- new_chandev->cu_model=newdevinfo->sid_data.cu_model; /* control unit model */
- new_chandev->dev_type=newdevinfo->sid_data.dev_type; /* device type */
- new_chandev->dev_model=newdevinfo->sid_data.dev_model; /* device model */
- new_chandev->devno=newdevinfo->devno;
- new_chandev->irq=newdevinfo->irq;
+ new_chandev->sch.devno=newdevinfo->devno;
+ new_chandev->sch.irq=newdevinfo->irq;
+ new_chandev->sch.cu_type=newdevinfo->sid_data.cu_type; /* control unit type */
+ new_chandev->sch.cu_model=newdevinfo->sid_data.cu_model; /* control unit model */
+ new_chandev->sch.dev_type=newdevinfo->sid_data.dev_type; /* device type */
+ new_chandev->sch.dev_model=newdevinfo->sid_data.dev_model; /* device model */
+ chandev_add_schib_info(newdevinfo->irq,&new_chandev->sch);
new_chandev->owned=(newdevinfo->status&DEVSTAT_DEVICE_OWNED ? TRUE:FALSE);
chandev_queuemember(&chandev_head,new_chandev);
}
@@ -910,16 +1105,29 @@
void chandev_unregister_probe(chandev_probefunc probefunc)
{
- chandev_probelist *curr_probe;
+ chandev_probelist *curr_probe,*next_probe;
chandev_lock();
- for_each(curr_probe,chandev_probelist_head)
+ for_each_allow_delete(curr_probe,next_probe,chandev_probelist_head)
if(curr_probe->probefunc==probefunc)
chandev_free_listmember((list **)&chandev_probelist_head,
(list *)curr_probe);
chandev_unlock();
}
+void chandev_unregister_probe_by_chan_type(chandev_type chan_type)
+{
+ chandev_probelist *curr_probe,*next_probe;
+
+ chandev_lock();
+ for_each_allow_delete(curr_probe,next_probe,chandev_probelist_head)
+ if(curr_probe->chan_type==chan_type)
+ chandev_free_listmember((list **)&chandev_probelist_head,
+ (list *)curr_probe);
+ chandev_unlock();
+}
+
+
void chandev_reset(void)
{
@@ -928,36 +1136,62 @@
chandev_free_all_list((list **)&chandev_noauto_head);
chandev_free_all_list((list **)&chandev_msck_range_head);
chandev_free_all_list((list **)&chandev_force_head);
- chandev_remove_parms(-1,FALSE);
+ chandev_remove_parms(-1,FALSE,-1);
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- use_devno_names=FALSE;
+ chandev_use_devno_names=FALSE;
#endif
- chandev_conf_read=FALSE;
chandev_unlock();
}
-chandev_model_info *chandev_is_chandev(int irq,s390_dev_info_t *devinfo)
+int chandev_is_chandev(int irq,s390_dev_info_t *devinfo,chandev_force **forceinfo,chandev_model_info **ret_model)
{
+ chandev_force *curr_force;
chandev_model_info *curr_model=NULL;
int err;
+ int retval=FALSE;
+
+ if(forceinfo)
+ *forceinfo=NULL;
+ if(ret_model)
+ *ret_model=NULL;
if((err=get_dev_info_by_irq(irq,devinfo)))
{
printk("chandev_is_chandev get_dev_info_by_irq reported err=%X on irq %d\n"
"should not happen\n",err,irq);
- return(NULL);
+ return FALSE;
}
chandev_lock();
+
for_each(curr_model,chandev_models_head)
{
if(((curr_model->cu_type==devinfo->sid_data.cu_type)||(curr_model->cu_type==-1))&&
((curr_model->cu_model==devinfo->sid_data.cu_model)||(curr_model->cu_model==-1))&&
((curr_model->dev_type==devinfo->sid_data.dev_type)||(curr_model->dev_type==-1))&&
((curr_model->dev_model==devinfo->sid_data.dev_model)||(curr_model->dev_model==-1)))
+ {
+ retval=TRUE;
+ if(ret_model)
+ *ret_model=curr_model;
break;
+ }
+ }
+ for_each(curr_force,chandev_force_head)
+ {
+ if(((curr_force->read_lo_devno==devinfo->devno)&&
+ (curr_force->write_hi_devno==devinfo->devno)&&
+ (curr_force->devif_num!=-2))||
+ ((curr_force->read_lo_devno>=devinfo->devno)&&
+ (curr_force->write_hi_devno<=devinfo->devno)&&
+ (curr_force->devif_num==-2)))
+ {
+ if(forceinfo)
+ *forceinfo=curr_force;
+ break;
+ }
}
chandev_unlock();
- return(curr_model);
+ return(retval);
}
void chandev_collect_devices(void)
@@ -982,39 +1216,70 @@
break;
}
chandev_lock();
- if((curr_model=chandev_is_chandev(curr_irq,&curr_devinfo)))
+ if(chandev_is_chandev(curr_irq,&curr_devinfo,NULL,&curr_model))
chandev_add(&curr_devinfo,curr_model);
chandev_unlock();
}
}
-void chandev_add_force(chandev_type chan_type,s32 devif_num,u16 read_devno,
-u16 write_devno,s16 port_protocol_no,u8 checksum_received_ip_pkts,u8 use_hw_stats)
-
+int chandev_add_force(chandev_type chan_type,s32 devif_num,u16 read_lo_devno,
+u16 write_hi_devno,u16 data_devno,s32 memory_usage_in_k,s16 port_protocol_no,u8 checksum_received_ip_pkts,
+u8 use_hw_stats,char *host_name,char *adapter_name,char *api_type)
{
chandev_force *new_chandev_force;
-
+
+ if(devif_num==-2&&read_lo_devno>write_hi_devno)
+ {
+ printk("chandev_add_force detected bad device range lo_devno=0x%04x hi_devno=0x%04x\n,",
+ (int)read_lo_devno,(int)write_hi_devno);
+ return(-1);
+ }
+ if(memory_usage_in_k<0)
+ {
+ printk("chandev_add_force memory_usage_in_k is bad\n");
+ return(-1);
+ }
+ if(chan_type==chandev_type_claw)
+ {
+ int host_name_len=strlen(host_name),
+ adapter_name_len=strlen(adapter_name),
+ api_type_len=strlen(api_type);
+ if(host_name_len>=CLAW_NAMELEN||host_name_len==0||
+ adapter_name_len>=CLAW_NAMELEN||adapter_name_len==0||
+ api_type_len>=CLAW_NAMELEN||api_type_len==0)
+ return(-1);
+ }
if((new_chandev_force=chandev_alloc(sizeof(chandev_force))))
{
new_chandev_force->chan_type=chan_type;
new_chandev_force->devif_num=devif_num;
- new_chandev_force->read_devno=read_devno;
- new_chandev_force->write_devno=write_devno;
+ new_chandev_force->read_lo_devno=read_lo_devno;
+ new_chandev_force->write_hi_devno=write_hi_devno;
+ new_chandev_force->data_devno=data_devno;
+ new_chandev_force->memory_usage_in_k=memory_usage_in_k;
new_chandev_force->port_protocol_no=port_protocol_no;
new_chandev_force->checksum_received_ip_pkts=checksum_received_ip_pkts;
new_chandev_force->use_hw_stats=use_hw_stats;
+
+ if(chan_type==chandev_type_claw)
+ {
+ strcpy(new_chandev_force->claw.host_name,host_name);
+ strcpy(new_chandev_force->claw.adapter_name,adapter_name);
+ strcpy(new_chandev_force->claw.api_type,api_type);
+ }
chandev_add_to_list((list **)&chandev_force_head,new_chandev_force);
}
+ return(0);
}
-void chandev_del_force(u16 read_devno)
+void chandev_del_force(int read_lo_devno)
{
- chandev_force *curr_force;
+ chandev_force *curr_force,*next_force;
chandev_lock();
- for_each(curr_force,chandev_force_head)
+ for_each_allow_delete(curr_force,next_force,chandev_force_head)
{
- if(curr_force->read_devno==read_devno)
+ if(curr_force->read_lo_devno==read_lo_devno||read_lo_devno==-1)
chandev_free_listmember((list **)&chandev_force_head,
(list *)curr_force);
}
@@ -1024,26 +1289,35 @@
void chandev_shutdown(chandev_activelist *curr_device)
{
- int saved_lock_cnt;
+ int err=0;
chandev_lock();
- if(curr_device->category==network_device)
+
+ /* unregister_netdev calls the dev->close so we shouldn't do this */
+ /* this otherwise we crash */
+ if(curr_device->unreg_dev)
{
- /* unregister_netdev calls the dev->close so we shouldn't do this */
- /* this otherwise we crash */
- if(curr_device->unreg_dev)
- {
- saved_lock_cnt=chandev_full_unlock();
- curr_device->unreg_dev(curr_device->dev_ptr);
- chandev_relock(saved_lock_cnt);
- }
+ curr_device->unreg_dev(curr_device->dev_ptr);
+ curr_device->unreg_dev=NULL;
+ }
+ if(curr_device->shutdownfunc)
+ {
+ err=curr_device->shutdownfunc(curr_device->dev_ptr);
}
- saved_lock_cnt=chandev_full_unlock();
- curr_device->shutdownfunc(curr_device->dev_ptr);
- chandev_relock(saved_lock_cnt);
- kfree(curr_device->dev_ptr);
- chandev_free_listmember((list **)&chandev_activelist_head,
+ if(err)
+ printk("chandev_shutdown unable to fully shutdown & unload %s err=%d\n"
+ "probably some upper layer still requires the device to exist\n",
+ curr_device->devname,err);
+ else
+ {
+
+ chandev_free_irq_by_irqinfo(curr_device->read_irqinfo);
+ chandev_free_irq_by_irqinfo(curr_device->write_irqinfo);
+ if(curr_device->data_irqinfo)
+ chandev_free_irq_by_irqinfo(curr_device->data_irqinfo);
+ chandev_free_listmember((list **)&chandev_activelist_head,
(list *)curr_device);
+ }
chandev_unlock();
}
@@ -1071,8 +1345,9 @@
chandev_activelist *curr_device;
for_each(curr_device,chandev_activelist_head)
- if(curr_device->read_irqinfo->devno==devno||
- curr_device->write_irqinfo->devno==devno)
+ if(curr_device->read_irqinfo->sch.devno==devno||
+ curr_device->write_irqinfo->sch.devno==devno||
+ (curr_device->data_irqinfo&&curr_device->data_irqinfo->sch.devno==devno))
{
return(curr_device);
}
@@ -1105,7 +1380,7 @@
str++;
continue;
}
- if(isspace(*str)||((*str)=='-'))
+ if(isspace(*str))
{
*str=',';
goto pack_dn;
@@ -1159,7 +1434,7 @@
int chandev_initdevice(chandev_probeinfo *probeinfo,void *dev_ptr,u8 port_no,char *devname,chandev_category category,chandev_unregfunc unreg_dev)
{
- chandev_activelist *newdevice;
+ chandev_activelist *newdevice,*curr_device;
chandev_interrupt_check();
if(probeinfo->newdevice!=NULL)
@@ -1167,70 +1442,117 @@
printk("probeinfo->newdevice!=NULL in chandev_initdevice for %s",devname);
return(-EPERM);
}
-
+
+ chandev_lock();
+ for_each(curr_device,chandev_activelist_head)
+ {
+ if(strcmp(curr_device->devname,devname)==0)
+ {
+ printk("chandev_initdevice detected duplicate devicename %s\n",devname);
+ chandev_unlock();
+ return(-EPERM);
+ }
+ }
if((newdevice=chandev_allocstr(devname,offsetof(chandev_activelist,devname))))
{
- probeinfo->newdevice=newdevice;
- chandev_lock();
- newdevice->read_irqinfo=chandev_get_irqinfo_by_irq(probeinfo->read_irq);
- newdevice->write_irqinfo=chandev_get_irqinfo_by_irq(probeinfo->write_irq);
+ newdevice->read_irqinfo=chandev_get_irqinfo_by_irq(probeinfo->read.irq);
+ newdevice->write_irqinfo=chandev_get_irqinfo_by_irq(probeinfo->write.irq);
+ if(probeinfo->data_exists)
+ newdevice->data_irqinfo=chandev_get_irqinfo_by_irq(probeinfo->data.irq);
chandev_unlock();
- if(newdevice->read_irqinfo==NULL||newdevice->write_irqinfo==NULL)
+ if(newdevice->read_irqinfo==NULL||newdevice->write_irqinfo==NULL||
+ (probeinfo->data_exists&&newdevice->data_irqinfo==NULL))
{
printk("chandev_initdevice, it appears that chandev_request_irq was not "
- "called for devname=%s read_irq=%d write_irq=%d\n",devname,probeinfo->read_irq,probeinfo->write_irq);
+ "called for devname=%s read_irq=%d write_irq=%d data_irq=%d\n",
+ devname,probeinfo->read.irq,probeinfo->write.irq,probeinfo->data.irq);
kfree(newdevice);
return(-EPERM);
}
- newdevice->cu_type=probeinfo->cu_type;
- newdevice->cu_model=probeinfo->cu_model;
- newdevice->dev_type=probeinfo->dev_type;
- newdevice->dev_model=probeinfo->dev_model;
newdevice->chan_type=probeinfo->chan_type;
newdevice->dev_ptr=dev_ptr;
newdevice->port_no=port_no;
+ newdevice->memory_usage_in_k=probeinfo->memory_usage_in_k;
newdevice->category=category;
newdevice->unreg_dev=unreg_dev;
+ probeinfo->newdevice=newdevice;
return(0);
}
+ chandev_unlock();
return(-ENOMEM);
}
+
+char *chandev_build_device_name(chandev_probeinfo *probeinfo,char *destnamebuff,char *basename,int buildfullname)
+{
+ if (chandev_use_devno_names&&(!probeinfo->device_forced||probeinfo->devif_num==-1))
+ sprintf(destnamebuff,"%s%04x",basename,(int)probeinfo->read.devno);
+ else
+ {
+ if(probeinfo->devif_num==-1)
+ {
+ if(buildfullname)
+ {
+ int idx,len=strlen(basename);
+
+ chandev_activelist *curr_device;
+ for(idx=0;idx<0xffff;idx++)
+ {
+ for_each(curr_device,chandev_activelist_head)
+ {
+ if(strncmp(curr_device->devname,basename,len)==0)
+ {
+ char numbuff[10];
+ sprintf(numbuff,"%d",idx);
+ if(strcmp(&curr_device->devname[len],numbuff)==0)
+ goto next_idx;
+ }
+ }
+ sprintf(destnamebuff,"%s%d",basename,idx);
+ return(destnamebuff);
+ next_idx:
+ }
+ printk("chandev_build_device_name was usable to build a unique name for %s\n",basename);
+ return(NULL);
+ }
+ else
+ sprintf(destnamebuff,"%s%%d",basename);
+ }
+ else
+ {
+ sprintf(destnamebuff,"%s%d",basename,(int)probeinfo->devif_num);
+ }
+ }
+ return(destnamebuff);
+}
+
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
-struct net_device *chandev_initnetdevice(chandev_probeinfo *probeinfo,u8 port_no,
-struct net_device *dev, int sizeof_priv, char *basename,
-struct net_device *(*init_netdevfunc)(struct net_device *dev, int sizeof_priv),
-void (*unreg_netdevfunc)(struct net_device *dev))
+struct net_device *chandev_init_netdev(chandev_probeinfo *probeinfo,char *basename,
+struct net_device *dev, int sizeof_priv,
+struct net_device *(*init_netdevfunc)(struct net_device *dev, int sizeof_priv))
#else
-struct device *chandev_initnetdevice(chandev_probeinfo *probeinfo,u8 port_no,
-struct device *dev, int sizeof_priv, char *basename,
-struct device *(*init_netdevfunc)(struct device *dev, int sizeof_priv),
-void (*unreg_netdevfunc)(struct device *dev))
+struct device *chandev_init_netdev(chandev_probeinfo *probeinfo,char *basename,
+struct device *dev, int sizeof_priv,
+struct device *(*init_netdevfunc)(struct device *dev, int sizeof_priv))
#endif
{
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
struct net_device *retdevice=NULL;
- int new_device = 0;
+ int new_device = FALSE;
#else
struct device *retdevice=NULL;
#endif
+
+ chandev_interrupt_check();
if (!init_netdevfunc)
{
- printk("init_netdevfunc=NULL in chandev_initnetdevice, it should not be valid.\n");
- return NULL;
- }
- if (!unreg_netdevfunc)
- {
- printk("unreg_netdevfunc=NULL in chandev_initnetdevice, it should not be valid.\n");
+ printk("init_netdevfunc=NULL in chandev_init_netdev, it should not be valid.\n");
return NULL;
}
-
- chandev_interrupt_check();
-
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- /* Allocate a device if one is not provided. */
+ /* Allocate a device if one is not provided. */
if (dev == NULL)
{
/* ensure 32-byte alignment of the private area */
@@ -1247,48 +1569,101 @@
if (sizeof_priv)
dev->priv = (void *) (((long)(dev + 1) + 31) & ~31);
-
- if (probeinfo->devif_num != -1)
- sprintf(dev->name,"%s%d",basename,(int)probeinfo->devif_num);
- else if (use_devno_names)
- sprintf(dev->name,"%s0x%04x",basename,(int)probeinfo->read_devno);
-
- new_device = 1;
+ new_device=TRUE;
}
+ chandev_build_device_name(probeinfo,dev->name,basename,FALSE);
#endif
-
retdevice=init_netdevfunc(dev,sizeof_priv);
-
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
/* Register device if necessary */
+ /* we need to do this as init_netdev doesn't call register_netdevice */
+ /* for already allocated devices */
if (retdevice && new_device)
register_netdev(retdevice);
#endif
+#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
+ /* We allocated it, so we should free it on error */
+ if (!retdevice && new_device)
+ kfree(dev);
+#endif
+ return retdevice;
+}
+
+
+
+
+#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
+struct net_device *chandev_initnetdevice(chandev_probeinfo *probeinfo,u8 port_no,
+struct net_device *dev, int sizeof_priv, char *basename,
+struct net_device *(*init_netdevfunc)(struct net_device *dev, int sizeof_priv),
+void (*unreg_netdevfunc)(struct net_device *dev))
+#else
+struct device *chandev_initnetdevice(chandev_probeinfo *probeinfo,u8 port_no,
+struct device *dev, int sizeof_priv, char *basename,
+struct device *(*init_netdevfunc)(struct device *dev, int sizeof_priv),
+void (*unreg_netdevfunc)(struct device *dev))
+#endif
+{
+#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
+ struct net_device *retdevice=NULL;
+ int new_device=(dev==NULL);
+#else
+ struct device *retdevice=NULL;
+#endif
+ if (!unreg_netdevfunc)
+ {
+ printk("unreg_netdevfunc=NULL in chandev_initnetdevice, it should not be valid.\n");
+ return NULL;
+ }
+ chandev_interrupt_check();
+ retdevice=chandev_init_netdev(probeinfo,basename,dev,sizeof_priv,init_netdevfunc);
if (retdevice)
{
if (chandev_initdevice(probeinfo,retdevice,port_no,retdevice->name,
- network_device,(chandev_unregfunc)unreg_netdevfunc))
+ chandev_category_network_device,(chandev_unregfunc)unreg_netdevfunc))
{
unreg_netdevfunc(retdevice);
+#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
+ /* We allocated it, so we should free it on error */
+ if(new_device)
+ kfree(dev);
+#endif
+
retdevice = NULL;
}
}
+ return retdevice;
+}
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- /* We allocated it, so we should free it on error */
- if (!retdevice && new_device)
- kfree(dev);
-#endif
- return retdevice;
+int chandev_compare_chpid_info(chandev_subchannel_info *chan1,chandev_subchannel_info *chan2)
+{
+ return (chan1->pim!=chan2->pim || *chan1->chpid!=*chan2->chpid);
}
+int chandev_compare_cu_dev_info(chandev_subchannel_info *chan1,chandev_subchannel_info *chan2)
+{
+ return ((chan1->cu_type != chan2->cu_type)||
+ (chan1->cu_model != chan2->cu_model)||
+ (chan1->dev_type != chan2->dev_type)||
+ (chan1->dev_model != chan2->dev_model));
+}
+int chandev_compare_subchannel_info(chandev_subchannel_info *chan1,chandev_subchannel_info *chan2)
+{
+ return((chan1->devno == chan2->devno) &&
+ (chan1->cu_type == chan2->cu_type) &&
+ (chan1->cu_model == chan2->cu_model) &&
+ (chan1->dev_type == chan2->dev_type) &&
+ (chan1->dev_model == chan2->dev_model) &&
+ (chan1->pim == chan2->pim) &&
+ (*chan1->chpid == *chan2->chpid));
+}
-int chandev_doprobe(chandev_force *force,chandev *read_chandev,
-chandev *write_chandev)
+int chandev_doprobe(chandev_force *force,chandev *read,
+chandev *write,chandev *data)
{
chandev_probelist *probe;
chandev_model_info *model_info;
@@ -1296,41 +1671,60 @@
int rc=-1,hint=-1;
chandev_activelist *newdevice;
chandev_probefunc probefunc;
- int saved_lock_cnt;
chandev_parms *curr_parms;
+ chandev_model_info dummy_model_info;
memset(&probeinfo,0,sizeof(probeinfo));
- model_info=read_chandev->model_info;
- if(read_chandev->model_info!=write_chandev->model_info||
- (force&&((force->chan_type&model_info->chan_type)==0))||
- (!force&&((read_chandev->cu_type!=write_chandev->cu_type)||
- (read_chandev->cu_model!=write_chandev->cu_model)||
- (read_chandev->dev_type!=write_chandev->dev_type)||
- (read_chandev->dev_model!=write_chandev->dev_model))))
- return(-1); /* inconsistent */
+ memset(&dummy_model_info,0,sizeof(dummy_model_info));
+ probeinfo.device_forced=(force!=NULL);
+ probeinfo.chpid_info_inconsistent=chandev_compare_chpid_info(&read->sch,&write->sch)||
+ (data&&chandev_compare_chpid_info(&read->sch,&data->sch));
+ probeinfo.cu_dev_info_inconsistent=chandev_compare_cu_dev_info(&read->sch,&write->sch)||
+ (data&&chandev_compare_cu_dev_info(&read->sch,&data->sch));
+ if(read->model_info)
+ model_info=read->model_info;
+ else
+ {
+ dummy_model_info.chan_type=chandev_type_none;
+ dummy_model_info.max_port_no=16;
+ model_info=&dummy_model_info;
+ }
for_each(probe,chandev_probelist_head)
{
- probeinfo.chan_type=(probe->chan_type&model_info->chan_type);
- if(probeinfo.chan_type)
+ if(force)
+ probeinfo.chan_type = ( probe->chan_type & force->chan_type );
+ else
+ {
+ if(chandev_cautious_auto_detect)
+ probeinfo.chan_type = ( probe->chan_type == model_info->chan_type ?
+ probe->chan_type : chandev_type_none );
+ else
+ probeinfo.chan_type = ( probe->chan_type & model_info->chan_type );
+ }
+ if(probeinfo.chan_type && (force || ( !probeinfo.cu_dev_info_inconsistent &&
+ ((probe->chan_type&(chandev_type_ctc|chandev_type_escon)) ||
+ !probeinfo.chpid_info_inconsistent))))
{
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- if(use_devno_names)
- probeinfo.devif_num=read_chandev->devno;
+ if(chandev_use_devno_names)
+ probeinfo.devif_num=read->sch.devno;
else
#endif
probeinfo.devif_num=-1;
- probeinfo.read_irq=read_chandev->irq;
- probeinfo.write_irq=write_chandev->irq;
- probeinfo.read_devno=read_chandev->devno;
- probeinfo.write_devno=write_chandev->devno;
- probeinfo.max_port_no=model_info->max_port_no;
- probeinfo.cu_type=read_chandev->cu_type;
- probeinfo.cu_model=read_chandev->cu_model;
- probeinfo.dev_type=read_chandev->dev_type;
- probeinfo.dev_model=read_chandev->dev_model;
+ probeinfo.read=read->sch;
+ probeinfo.write=write->sch;
+ if(data)
+ {
+ probeinfo.data=data->sch;
+ probeinfo.data_exists=TRUE;
+ }
+ probeinfo.max_port_no=(force&&(force->port_protocol_no!=-1) ?
+ force->port_protocol_no : model_info->max_port_no);
for_each(curr_parms,chandev_parms_head)
{
- if(probe->chan_type==curr_parms->chan_type)
+ if(probe->chan_type==curr_parms->chan_type&&
+ read->sch.devno>=curr_parms->lo_devno&&
+ read->sch.devno<=curr_parms->hi_devno)
{
probeinfo.parmstr=curr_parms->parmstr;
break;
@@ -1338,25 +1732,26 @@
}
if(force)
{
+ if(force->chan_type==chandev_type_claw)
+ memcpy(&probeinfo.claw,&force->claw,sizeof(chandev_claw_info));
probeinfo.port_protocol_no=force->port_protocol_no;
- if(force->devif_num!=-1)
+ if(force->devif_num==-1&&force->devif_num==-2)
+ probeinfo.devif_num=-1;
+ else
probeinfo.devif_num=force->devif_num;
+ probeinfo.memory_usage_in_k=force->memory_usage_in_k;
probeinfo.checksum_received_ip_pkts=force->checksum_received_ip_pkts;
probeinfo.use_hw_stats=force->use_hw_stats;
-
}
else
{
- probeinfo.port_protocol_no=-1;
- probeinfo.checksum_received_ip_pkts=FALSE;
- probeinfo.use_hw_stats=FALSE;
- if(probe->chan_type&lcs)
- {
- if((probeinfo.read_devno&1)||
- ((probeinfo.read_devno|1)!=
- (probeinfo.write_devno)))
- return(-1);
- hint=(read_chandev->devno&0xFF)>>1;
+ probeinfo.port_protocol_no=0;
+ probeinfo.checksum_received_ip_pkts=model_info->default_checksum_received_ip_pkts;
+ probeinfo.use_hw_stats=model_info->default_use_hw_stats;
+ probeinfo.memory_usage_in_k=0;
+ if(probe->chan_type&chandev_type_lcs)
+ {
+ hint=(read->sch.devno&0xFF)>>1;
if(hint>model_info->max_port_no)
{
/* The card is possibly emulated e.g P/390 */
@@ -1368,12 +1763,7 @@
}
probeinfo.hint_port_no=hint;
probefunc=probe->probefunc;
- saved_lock_cnt=chandev_full_unlock();
- /* We have to leave the lock go here */
- /* as probefunctions can call schedule & */
- /* reenter to do a kernel thread & we may deadlock */
rc=probefunc(&probeinfo);
- chandev_relock(saved_lock_cnt);
if(rc==0)
{
newdevice=probeinfo.newdevice;
@@ -1381,47 +1771,174 @@
{
newdevice->probefunc=probe->probefunc;
newdevice->shutdownfunc=probe->shutdownfunc;
- newdevice->reoperfunc=probe->reoperfunc;
+ newdevice->msck_notfunc=probe->msck_notfunc;
probe->devices_found++;
chandev_add_to_list((list **)&chandev_activelist_head,
newdevice);
- chandev_add_to_startmsck_list(&startlist_head,
- newdevice->devname,good,good);
-
+ chandev_add_to_userland_notify_list(chandev_start,
+ newdevice->devname,chandev_status_good,chandev_status_good);
}
else
{
- printk("chandev_initdevice either failed or wasn't called for device read_irq=0x%04x\n",probeinfo.read_irq);
+ printk("chandev_initdevice either failed or wasn't called for device read_irq=0x%04x\n",probeinfo.read.irq);
}
break;
+
}
}
}
+ chandev_remove(read);
+ chandev_remove(write);
+ if(data)
+ chandev_remove(data);
return(rc);
}
int chandev_request_irq_from_irqinfo(chandev_irqinfo *irqinfo,chandev *this_chandev)
{
- int retval=s390_request_irq_special(irqinfo->irq,
+ int retval=s390_request_irq_special(irqinfo->sch.irq,
irqinfo->handler,
chandev_not_oper_handler,
irqinfo->irqflags,
irqinfo->devname,
irqinfo->dev_id);
if(retval==0)
+ {
+ irqinfo->msck_status=chandev_status_good;
this_chandev->owned=TRUE;
+ }
return(retval);
}
void chandev_irqallocerr(chandev_irqinfo *irqinfo,int err)
{
- printk("chandev_probe failed to realloc irq=%d for %s err=%d\n",irqinfo->irq,irqinfo->devname,err);
+ printk("chandev_probe failed to realloc irq=%d for %s err=%d\n",irqinfo->sch.irq,irqinfo->devname,err);
+}
+
+
+void chandev_call_notification_func(chandev_activelist *curr_device,chandev_irqinfo *curr_irqinfo,
+chandev_msck_status prevstatus)
+{
+ if(curr_irqinfo->msck_status!=prevstatus)
+ {
+ chandev_msck_status new_msck_status=curr_irqinfo->msck_status;
+ if(curr_irqinfo->msck_status==chandev_status_good)
+ {
+ if(curr_device->read_irqinfo->msck_status==chandev_status_good&&
+ curr_device->write_irqinfo->msck_status==chandev_status_good)
+ {
+ if(curr_device->data_irqinfo)
+ {
+ if(curr_device->data_irqinfo->msck_status==chandev_status_good)
+ new_msck_status=chandev_status_all_chans_good;
+ }
+ else
+ new_msck_status=chandev_status_all_chans_good;
+ }
+ }
+ if(curr_device->msck_notfunc)
+ {
+ curr_device->msck_notfunc(curr_device->dev_ptr,
+ curr_irqinfo->sch.irq,
+ prevstatus,new_msck_status);
+ }
+ if(new_msck_status!=chandev_status_good)
+ {
+ /* No point in sending a machine check if only one channel is good */
+ chandev_add_to_userland_notify_list(chandev_msck,curr_device->devname,
+ prevstatus,curr_irqinfo->msck_status);
+ }
+ }
+}
+
+int chandev_find_eligible_channels(chandev *first_chandev_to_check,
+ chandev **read,chandev **write,chandev **data,chandev **next,
+ chandev_type chan_type)
+{
+ chandev *curr_chandev;
+ int eligible_found=FALSE,changed;
+
+ *next=first_chandev_to_check->next;
+ *read=*write=*data=NULL;
+ for_each(curr_chandev,first_chandev_to_check)
+ if((curr_chandev->sch.devno&1)==0&&curr_chandev->model_info->chan_type!=chandev_type_claw)
+ {
+ *read=curr_chandev;
+ if(chan_type==chandev_type_none)
+ chan_type=(*read)->model_info->chan_type;
+ break;
+ }
+ if(*read)
+ {
+ for_each(curr_chandev,(chandev *)chandev_head.head)
+ if((((*read)->sch.devno|1)==curr_chandev->sch.devno)&&
+ (chandev_compare_cu_dev_info(&(*read)->sch,&curr_chandev->sch)==0)&&
+ ((chan_type&(chandev_type_ctc|chandev_type_escon))||
+ chandev_compare_chpid_info(&(*read)->sch,&curr_chandev->sch)==0))
+ {
+ *write=curr_chandev;
+ break;
+ }
+ }
+ if((chan_type&chandev_type_qeth))
+ {
+ if(*write)
+ {
+ for_each(curr_chandev,(chandev *)chandev_head.head)
+ if((curr_chandev!=*read&&curr_chandev!=*write)&&
+ (chandev_compare_cu_dev_info(&(*read)->sch,&curr_chandev->sch)==0)&&
+ (chandev_compare_chpid_info(&(*read)->sch,&curr_chandev->sch)==0))
+ {
+ *data=curr_chandev;
+ break;
+ }
+ if(*data)
+ eligible_found=TRUE;
+ }
+
+ }
+ else
+ if(*write)
+ eligible_found=TRUE;
+ if(eligible_found)
+ {
+ do
+ {
+ changed=FALSE;
+ if(*next&&
+ ((*read&&(*read==*next))||
+ (*write&&(*write==*next))||
+ (*data&&(*data==*next))))
+ {
+ *next=(*next)->next;
+ changed=TRUE;
+ }
+ }while(changed==TRUE);
+ }
+ return(eligible_found);
+}
+
+chandev *chandev_get_free_chandev_by_devno(int devno)
+{
+ chandev *curr_chandev;
+ if(devno==-1)
+ return(NULL);
+ for_each(curr_chandev,(chandev *)chandev_head.head)
+ if(curr_chandev->sch.devno==devno)
+ {
+ if(chandev_active(devno))
+ return(NULL);
+ else
+ return(curr_chandev);
+ }
+ return(NULL);
+
}
void chandev_probe(void)
{
- chandev *read_chandev,*write_chandev,*curr_chandev;
+ chandev *read_chandev,*write_chandev,*data_chandev,*curr_chandev,*next_chandev;
chandev_force *curr_force;
chandev_noauto_range *curr_noauto;
chandev_activelist *curr_device;
@@ -1434,16 +1951,17 @@
chandev_interrupt_check();
+ chandev_read_conf_if_necessary();
chandev_collect_devices();
chandev_lock();
for_each(curr_irqinfo,chandev_irqinfo_head)
{
- if((curr_device=chandev_get_activelist_by_irq(curr_irqinfo->irq)))
+ if((curr_device=chandev_get_activelist_by_irq(curr_irqinfo->sch.irq)))
{
prevstatus=curr_irqinfo->msck_status;
- if(curr_irqinfo->msck_status!=good)
+ if(curr_irqinfo->msck_status!=chandev_status_good)
{
- curr_chandev=chandev_get_by_irq(curr_irqinfo->irq);
+ curr_chandev=chandev_get_by_irq(curr_irqinfo->sch.irq);
if(curr_chandev)
{
auto_msck_recovery=curr_chandev->model_info->
@@ -1454,9 +1972,9 @@
for_each(curr_msck_range,chandev_msck_range_head)
{
if(curr_msck_range->lo_devno<=
- curr_irqinfo->devno&&
+ curr_irqinfo->sch.devno&&
curr_msck_range->hi_devno>=
- curr_irqinfo->devno)
+ curr_irqinfo->sch.devno)
{
auto_msck_recovery=
curr_msck_range->
@@ -1466,28 +1984,32 @@
}
if((1<<(curr_irqinfo->msck_status-1))&auto_msck_recovery)
{
- if(curr_irqinfo->msck_status==revalidate)
+ if(curr_irqinfo->msck_status==chandev_status_revalidate)
{
- if((get_dev_info_by_irq(curr_irqinfo->irq,&curr_devinfo)==0))
+ if((get_dev_info_by_irq(curr_irqinfo->sch.irq,&curr_devinfo)==0))
{
- curr_irqinfo->devno=curr_devinfo.devno;
- curr_irqinfo->msck_status=good;
- goto remove;
+ curr_irqinfo->sch.devno=curr_devinfo.devno;
+ curr_irqinfo->msck_status=chandev_status_good;
}
}
else
{
- if((curr_chandev=chandev_get_by_irq(curr_irqinfo->irq)))
+ if(curr_chandev)
{
/* Has the device reappeared */
- if(curr_chandev->cu_type==curr_device->cu_type&&
- curr_chandev->cu_model==curr_device->cu_model&&
- curr_chandev->dev_type==curr_device->dev_type&&
- curr_chandev->dev_model==curr_device->dev_model&&
- curr_chandev->devno==curr_irqinfo->devno)
+ if(chandev_compare_subchannel_info(
+ &curr_chandev->sch,
+ &curr_device->read_irqinfo->sch)||
+ chandev_compare_subchannel_info(
+ &curr_chandev->sch,
+ &curr_device->write_irqinfo->sch)||
+ (curr_device->data_irqinfo&&
+ chandev_compare_subchannel_info(
+ &curr_chandev->sch,
+ &curr_device->data_irqinfo->sch)))
{
if((err=chandev_request_irq_from_irqinfo(curr_irqinfo,curr_chandev))==0)
- curr_irqinfo->msck_status=good;
+ curr_irqinfo->msck_status=chandev_status_good;
else
chandev_irqallocerr(curr_irqinfo,err);
}
@@ -1496,86 +2018,89 @@
}
}
}
- if(curr_irqinfo->msck_status==good&&prevstatus!=good)
- {
- if(curr_device->reoperfunc)
- {
- int saved_lock_cnt=chandev_full_unlock();
- curr_device->reoperfunc(curr_device->dev_ptr,
- (curr_device->read_irqinfo==curr_irqinfo),
- prevstatus);
- chandev_relock(saved_lock_cnt);
- }
- if(curr_device->category==network_device&&
- curr_device->write_irqinfo==curr_irqinfo)
- {
- net_device *dev=(net_device *)curr_device->dev_ptr;
- if(dev->flags&IFF_UP)
- netif_start_queue(dev);
- }
- chandev_add_to_startmsck_list(&mscklist_head,curr_device->devname,
- prevstatus,curr_irqinfo->msck_status);
- }
+ chandev_call_notification_func(curr_device,curr_irqinfo,prevstatus);
}
/* This is required because the device can go & come back */
/* even before we realize it is gone owing to the waits in our kernel threads */
/* & the device will be marked as not owned but its status will be good */
/* & an attempt to accidently reprobe it may be done. */
remove:
- chandev_remove(chandev_get_by_irq(curr_irqinfo->irq));
+ chandev_remove(chandev_get_by_irq(curr_irqinfo->sch.irq));
}
/* extra sanity */
- for_each(curr_chandev,(chandev *)chandev_head.head)
+ for_each_allow_delete(curr_chandev,next_chandev,(chandev *)chandev_head.head)
if(curr_chandev->owned)
chandev_remove(curr_chandev);
for_each(curr_force,chandev_force_head)
{
- for_each(read_chandev,(chandev *)chandev_head.head)
- if(read_chandev->devno==curr_force->read_devno&&
- !chandev_active(curr_force->read_devno))
- {
- for_each(write_chandev,(chandev *)chandev_head.head)
- if(write_chandev->devno==
- curr_force->write_devno&&
- !chandev_active(curr_force->write_devno))
+ if(curr_force->devif_num==-2)
+ {
+ for_each_allow_delete2(curr_chandev,next_chandev,(chandev *)chandev_head.head)
+ {
+ if(chandev_find_eligible_channels(curr_chandev,&read_chandev,
+ &write_chandev,&data_chandev,
+ &next_chandev,
+ curr_force->chan_type));
+ {
+ if((curr_force->read_lo_devno>=read_chandev->sch.devno)&&
+ (curr_force->write_hi_devno<=read_chandev->sch.devno)&&
+ (curr_force->read_lo_devno>=write_chandev->sch.devno)&&
+ (curr_force->write_hi_devno<=write_chandev->sch.devno)&&
+ (!data_chandev||(data_chandev&&
+ (curr_force->read_lo_devno>=data_chandev->sch.devno)&&
+ (curr_force->write_hi_devno<=data_chandev->sch.devno))))
+ chandev_doprobe(curr_force,read_chandev,write_chandev,
+ data_chandev);
+ }
+ }
+ }
+ else
+ {
+ read_chandev=chandev_get_free_chandev_by_devno(curr_force->read_lo_devno);
+ if(read_chandev)
+ {
+ write_chandev=chandev_get_free_chandev_by_devno(curr_force->write_hi_devno);
+ if(write_chandev)
+ {
+ if(curr_force->chan_type==chandev_type_qeth)
{
- if(chandev_doprobe(curr_force,
- read_chandev,
- write_chandev)==0)
- {
- chandev_remove(read_chandev);
- chandev_remove(write_chandev);
- goto chandev_probe_skip;
- }
+
+ data_chandev=chandev_get_free_chandev_by_devno(curr_force->data_devno);
+ if(data_chandev==NULL)
+ printk("chandev_probe unable to force gigabit_ethernet driver invalid device no 0x%04x given\n",curr_force->data_devno);
}
+ else
+ data_chandev=NULL;
+ chandev_doprobe(curr_force,read_chandev,write_chandev,
+ data_chandev);
+ }
}
- chandev_probe_skip:
+ }
}
- for_each(curr_chandev,(chandev *)chandev_head.head)
+ for_each_allow_delete(curr_chandev,next_chandev,(chandev *)chandev_head.head)
{
for_each(curr_noauto,chandev_noauto_head)
{
- if(curr_chandev->devno>=curr_noauto->lo_devno&&
- curr_chandev->devno<=curr_noauto->hi_devno)
+ if(curr_chandev->sch.devno>=curr_noauto->lo_devno&&
+ curr_chandev->sch.devno<=curr_noauto->hi_devno)
{
chandev_remove(curr_chandev);
break;
}
}
}
- for_each(curr_chandev,(chandev *)chandev_head.head)
+ for_each_allow_delete2(curr_chandev,next_chandev,(chandev *)chandev_head.head)
{
- if(curr_chandev->next&&curr_chandev->model_info==
- curr_chandev->next->model_info)
- {
-
- chandev_doprobe(NULL,curr_chandev,curr_chandev->next);
- curr_chandev=curr_chandev->next;
- }
+ if(chandev_find_eligible_channels(curr_chandev,&read_chandev,
+ &write_chandev,&data_chandev,
+ &next_chandev,
+ chandev_type_none))
+ chandev_doprobe(NULL,read_chandev,write_chandev,
+ data_chandev);
}
- chandev_unlock();
chandev_remove_all();
+ chandev_unlock();
}
static void chandev_not_oper_func(int irq,int status)
@@ -1585,49 +2110,40 @@
chandev_lock();
for_each(curr_irqinfo,chandev_irqinfo_head)
- if(curr_irqinfo->irq==irq)
+ if(curr_irqinfo->sch.irq==irq)
{
+ chandev_msck_status prevstatus=curr_irqinfo->msck_status;
switch(status)
{
/* Currently defined but not used in kernel */
/* Despite being in specs */
case DEVSTAT_NOT_OPER:
- curr_irqinfo->msck_status=not_oper;
+ curr_irqinfo->msck_status=chandev_status_not_oper;
break;
#ifdef DEVSTAT_NO_PATH
/* Kernel hasn't this defined currently. */
/* Despite being in specs */
case DEVSTAT_NO_PATH:
- curr_irqinfo->msck_status=no_path;
+ curr_irqinfo->msck_status=chandev_status_no_path;
break;
#endif
case DEVSTAT_REVALIDATE:
- curr_irqinfo->msck_status=revalidate;
+ curr_irqinfo->msck_status=chandev_status_revalidate;
break;
case DEVSTAT_DEVICE_GONE:
- curr_irqinfo->msck_status=gone;
- break;
- }
- for_each(curr_device,chandev_activelist_head)
- {
- if(curr_device->write_irqinfo==curr_irqinfo)
- {
- if(curr_device->category==network_device)
- {
- net_device *dev=(net_device *)curr_device->dev_ptr;
- if(dev->flags&IFF_UP)
- netif_stop_queue(dev);
- }
- }
+ curr_irqinfo->msck_status=chandev_status_gone;
break;
- }
- break;
+ }
+ if((curr_device=chandev_get_activelist_by_irq(irq)))
+ chandev_call_notification_func(curr_device,curr_irqinfo,prevstatus);
+ else
+ printk("chandev_not_oper_func received channel check for unowned irq %d",irq);
}
chandev_unlock();
}
-static void chandev_msck_task(void *unused)
+static int chandev_msck_thread(void *unused)
{
int loopcnt,not_oper_probe_required=FALSE;
wait_queue_head_t wait;
@@ -1663,9 +2179,17 @@
}
if(not_oper_probe_required)
chandev_probe();
+ return(0);
}
-
+static void chandev_msck_task(void *unused)
+{
+ if(kernel_thread(chandev_msck_thread,NULL,SIGCHLD)<0)
+ {
+ atomic_set(&chandev_msck_thread_lock,1);
+ printk("error making chandev_msck_thread kernel thread\n");
+ }
+}
@@ -1686,6 +2210,8 @@
"use_devno_names",
"dont_use_devno_names",
#endif
+ "cautious_auto_detect",
+ "non_cautious_auto_detect",
"add_model",
"del_model",
"auto_msck",
@@ -1696,8 +2222,10 @@
"shutdown",
"reprobe",
"unregister_probe",
+ "unregister_probe_by_chan_type",
"read_conf",
"dont_read_conf",
+ "persistent"
};
typedef enum
@@ -1719,6 +2247,8 @@
use_devno_names_stridx,
dont_use_devno_names_stridx,
#endif
+ cautious_auto_detect_stridx,
+ non_cautious_auto_detect_stridx,
add_model_stridx,
del_model_stridx,
auto_msck_stridx,
@@ -1729,8 +2259,10 @@
shutdown_stridx,
reprobe_stridx,
unregister_probe_stridx,
+ unregister_probe_by_chan_type_stridx,
read_conf_stridx,
dont_read_conf_stridx,
+ persistent_stridx,
last_stridx,
} chandev_str_enum;
@@ -1799,16 +2331,18 @@
return 1;
}
+
static char *chandev_get_options(char *str, int nints, chandev_int *ints)
{
int res,i=1;
- while (i<nints) {
- res = chandev_get_option(&str, ints+i);
- if (res==0) break;
- i++;
- if (res==1) break;
- }
+ while (i<nints)
+ {
+ res = chandev_get_option(&str, ints+i);
+ if (res==0) break;
+ i++;
+ if (res==1) break;
+ }
ints[0] = i-1;
return(str);
}
@@ -1816,6 +2350,43 @@
#define chandev_get_option get_option
#define chandev_get_options get_options
#endif
+/*
+ * Read an string from an option string; if available accept a subsequent
+ * comma as well & set this comma to a null character when returning the string.
+ *
+ * Return values:
+ * 0 : no string found
+ * 1 : string found, no subsequent comma
+ * 2 : string found including a subsequent comma
+ */
+static int chandev_get_string(char **instr,char **outstr)
+{
+ char *cur = *instr;
+
+ if (!cur ||*cur==0)
+ {
+ *outstr=NULL;
+ return 0;
+ }
+ *outstr=*instr;
+ for(;;)
+ {
+ if(*(++cur)==',')
+ {
+ *cur=0;
+ *instr=cur+1;
+ return 2;
+ }
+ else if(*cur==0)
+ {
+ *instr=cur+1;
+ return 1;
+ }
+ }
+}
+
+
+
static int chandev_setup(char *instr,char *errstr,int lineno)
{
@@ -1826,15 +2397,15 @@
char *str,*currstr,*interpretstr=NULL;
int cnt,strcnt;
int retval=0;
-#define CHANDEV_MAX_EXTRA_INTS 8
+#define CHANDEV_MAX_EXTRA_INTS 12
chandev_int ints[CHANDEV_MAX_EXTRA_INTS+1];
- memset(ints,0,sizeof(ints));
currstr=alloca(strlen(instr)+1);
strcpy(currstr,instr);
strcnt=chandev_pack_args(currstr);
for(cnt=1;cnt<=strcnt;cnt++)
{
interpretstr=currstr;
+ memset(ints,0,sizeof(ints));
for(stridx=first_stridx;stridx<last_stridx;stridx++)
{
str=currstr;
@@ -1844,29 +2415,36 @@
currstr=str;
if(val)
{
- if(val&iscomma)
+ val=(((chandev_strval)stridx)*stridx_mult)+(val&~isstr);
+ switch(val)
{
- if(stridx==add_parms_stridx&&(val==(isstr|iscomma)))
+ case (add_parms_stridx*stridx_mult)|iscomma:
+ currstr=chandev_get_options(currstr,4,ints);
+ if(*currstr&&ints[0]>=1)
{
- str=currstr;
- if(chandev_get_option(&str,&ints[0])==2)
+ if(ints[0]==1)
{
- chandev_add_parms(ints[0],str);
- currstr=str+strlen(str)+1;
- continue;
+ ints[2]=0;
+ ints[3]=0xffff;
}
- else
- goto BadArgs;
+ else if(ints[0]==2)
+ ints[3]=ints[2];
+ chandev_add_parms(ints[1],ints[2],ints[3],currstr);
+// currstr=currstr+strlen(currstr)+1;
+ continue;
}
else
- currstr=chandev_get_options(str,CHANDEV_MAX_EXTRA_INTS,ints)+1;
- }
- else
- {
- ints[0]=0;
- currstr++;
+ goto BadArgs;
+ break;
+ case (claw_stridx*stridx_mult)|isnum|iscomma:
+ case (claw_stridx*stridx_mult)|iscomma:
+ currstr=chandev_get_options(str,6,ints);
+ break;
+ default:
+ if(val&iscomma)
+ currstr=chandev_get_options(str,CHANDEV_MAX_EXTRA_INTS,ints);
+ break;
}
- val=(((chandev_strval)stridx)*stridx_mult)+(val&~isstr);
switch(val)
{
case noauto_stridx*stridx_mult:
@@ -1924,59 +2502,105 @@
else
goto BadArgs;
break;
+ case (qeth_stridx*stridx_mult)|isnum|iscomma:
+ if(ints[0]<3||ints[0]>7)
+ goto BadArgs;
+ chandev_add_force(chandev_type_qeth,endlong,ints[1],ints[2],
+ ints[3],ints[4],ints[5],ints[6],ints[7],
+ NULL,NULL,NULL);
+ break;
case (ctc_stridx*stridx_mult)|isnum|iscomma:
case (escon_stridx*stridx_mult)|isnum|iscomma:
case (lcs_stridx*stridx_mult)|isnum|iscomma:
case (osad_stridx*stridx_mult)|isnum|iscomma:
- case (qeth_stridx*stridx_mult)|isnum|iscomma:
- case (claw_stridx*stridx_mult)|isnum|iscomma:
- switch(val)
+ case (ctc_stridx*stridx_mult)|iscomma:
+ case (escon_stridx*stridx_mult)|iscomma:
+ case (lcs_stridx*stridx_mult)|iscomma:
+ case (osad_stridx*stridx_mult)|iscomma:
+ switch(val&~(isnum|iscomma))
{
- case (ctc_stridx*stridx_mult)|isnum|iscomma:
- chan_type=ctc;
- break;
- case (escon_stridx*stridx_mult)|isnum|iscomma:
- chan_type=escon;
+ case (ctc_stridx*stridx_mult):
+ chan_type=chandev_type_ctc;
break;
- case (lcs_stridx*stridx_mult)|isnum|iscomma:
- chan_type=lcs;
+ case (escon_stridx*stridx_mult):
+ chan_type=chandev_type_escon;
break;
- case (osad_stridx*stridx_mult)|isnum|iscomma:
- chan_type=osad;
+ case (lcs_stridx*stridx_mult):
+ chan_type=chandev_type_lcs;
break;
- case (qeth_stridx*stridx_mult)|isnum|iscomma:
- chan_type=qeth;
+ case (osad_stridx*stridx_mult):
+ chan_type=chandev_type_osad;
break;
- case (claw_stridx*stridx_mult)|isnum|iscomma:
- chan_type=claw;
+ case (qeth_stridx*stridx_mult):
+ chan_type=chandev_type_qeth;
break;
default:
goto BadArgs;
}
+ if((val&isnum)==0)
+ endlong=-2;
+ if(ints[0]<2||ints[0]>6)
+ goto BadArgs;
chandev_add_force(chan_type,endlong,ints[1],ints[2],
- ints[3],ints[4],ints[5]);
+ 0,ints[3],ints[4],ints[5],ints[6],
+ NULL,NULL,NULL);
+ break;
+ case (claw_stridx*stridx_mult)|isnum|iscomma:
+ case (claw_stridx*stridx_mult)|iscomma:
+ if(ints[0]>=2&&ints[0]<=5)
+ {
+ char *host_name,*adapter_name,*api_type;
+ char *clawstr=alloca(strlen(currstr)+1);
+
+ strcpy(clawstr,currstr);
+ if(!(chandev_get_string(&clawstr,&host_name)==2&&
+ chandev_get_string(&clawstr,&adapter_name)==2&&
+ chandev_get_string(&clawstr,&api_type)==1&&
+ chandev_add_force(chandev_type_claw,
+ endlong,ints[1],ints[2],0,
+ ints[3],0,ints[4],ints[5],
+ host_name,adapter_name,api_type)==0))
+ goto BadArgs;
+
+ }
+ else
+ goto BadArgs;
break;
case (del_parms_stridx*stridx_mult):
ints[1]=-1;
case (del_parms_stridx*stridx_mult)|iscomma:
- if(ints[0]==1)
+ if(ints[0]==0)
+ ints[1]=-1;
+ if(ints[0]<=1)
ints[2]=FALSE;
- if(ints[0]>2)
+ if(ints[0]<=2)
+ ints[3]=-1;
+ if(ints[0]>3)
goto BadArgs;
- chandev_remove_parms(ints[1],ints[2]);
+ chandev_remove_parms(ints[1],ints[2],ints[3]);
break;
case (del_force_stridx*stridx_mult)|iscomma:
if(ints[0]!=1)
goto BadArgs;
chandev_del_force(ints[1]);
break;
+ case (del_force_stridx*stridx_mult):
+ chandev_del_force(-1);
+ break;
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
case (use_devno_names_stridx*stridx_mult):
- use_devno_names=1;
+ chandev_use_devno_names=TRUE;
break;
case (dont_use_devno_names_stridx*stridx_mult):
- use_devno_names=0;
+ chandev_use_devno_names=FALSE;
+ break;
#endif
+ case (cautious_auto_detect_stridx*stridx_mult):
+ chandev_cautious_auto_detect=TRUE;
+ break;
+ case (non_cautious_auto_detect_stridx*stridx_mult):
+ chandev_cautious_auto_detect=FALSE;
+ break;
case (add_model_stridx*stridx_mult)|iscomma:
if(ints[0]<3)
goto BadArgs;
@@ -1988,9 +2612,13 @@
ints[6]=-1;
if(ints[0]<=6)
ints[7]=default_msck_bits;
+ if(ints[0]<=7)
+ ints[8]=FALSE;
+ if(ints[0]<=8)
+ ints[9]=FALSE;
ints[0]=7;
chandev_add_model(ints[1],ints[2],ints[3],
- ints[4],ints[5],ints[6],ints[7]);
+ ints[4],ints[5],ints[6],ints[7],ints[8],ints[9]);
break;
case (del_model_stridx*stridx_mult)|iscomma:
if(ints[0]<2||ints[0]>4)
@@ -2042,41 +2670,53 @@
goto BadArgs;
chandev_unregister_probe((chandev_probefunc)ints[1]);
break;
+ case (unregister_probe_by_chan_type_stridx*stridx_mult)|iscomma:
+ if(ints[0]!=1)
+ goto BadArgs;
+ chandev_unregister_probe_by_chan_type((chandev_type)ints[1]);
+ break;
case read_conf_stridx*stridx_mult:
chandev_read_conf();
break;
case dont_read_conf_stridx*stridx_mult:
- chandev_conf_read=TRUE;
+ atomic_set(&chandev_conf_read,TRUE);
+ break;
+ case (persistent_stridx*stridx_mult)|iscomma:
+ if(ints[0]==1)
+ chandev_persistent=ints[1];
+ else
+ goto BadArgs;
break;
default:
goto BadArgs;
- }
+ }
}
else
goto BadArgs;
+ if(cnt<strcnt)
+ {
+ /* eat up stuff till next string */
+ while(*(currstr++));
+ }
}
retval=1;
BadArgs:
if(!retval)
{
- printk("chandev_setup bad argument %s",instr);
+ printk("chandev_setup %s %s",(val==0 ? "unknown verb":"bad argument"),instr);
if(errstr)
{
printk("%s %d interpreted as %s",errstr,lineno,interpretstr);
if(strcnt>1)
- printk(" before semicolon no %d",cnt);
+ {
+ if(cnt==strcnt)
+ printk(" after the last semicolon\n");
+ else
+ printk(" before semicolon no %d",cnt);
+ }
}
printk(".\n Type man chandev for more info.\n\n");
}
- eieio();
- if(chandev_lock_owner==(long)current)
- {
- printk("chandev_setup bug chandev_lock_cnt=%d lock_owner=%lx\n"
- "firstlock_retaddr=%p last_lock_returnaddr=%p\n",
- chandev_lock_cnt,chandev_lock_owner,chandev_firstlock_addr,
- chandev_lastlock_addr);
- chandev_full_unlock();
- }
return(retval);
}
#define CHANDEV_KEYWORD "chandev="
@@ -2166,9 +2806,15 @@
char *buff;
int curr,left,len,fd;
+ /* if called from chandev_register_and_probe &
+ the driver is compiled into the kernel the
+ parameters will need to be passed in from
+ the kernel boot parameter line as the root
+ fs is not mounted yet, we can't wait here.
+ */
if(in_interrupt()||current->fs->root==NULL)
return;
- chandev_conf_read=TRUE;
+ atomic_set(&chandev_conf_read,TRUE);
set_fs(KERNEL_DS);
if(stat(CHANDEV_FILE,&statbuf)==0)
{
@@ -2198,7 +2844,9 @@
static void chandev_read_conf_if_necessary(void)
{
- if(!chandev_conf_read)
+ if(in_interrupt()||current->fs->root==NULL)
+ return;
+ if(!atomic_compare_and_swap(FALSE,TRUE,&chandev_conf_read))
chandev_read_conf();
}
@@ -2218,7 +2866,7 @@
chandev_msck_status idx;
int first_time=TRUE;
buff[0]=0;
- for(idx=first_msck;idx<last_msck;idx++)
+ for(idx=chandev_status_first_msck;idx<chandev_status_last_msck;idx++)
{
if((1<<(idx-1))&auto_msck_recovery)
{
@@ -2243,34 +2891,39 @@
chandev_msck_range *curr_msck_range;
s390_dev_info_t curr_devinfo;
int pass,chandevs_detected,curr_irq,loopcnt;
- chandev_irqinfo *read_irqinfo,*write_irqinfo;
- char buff[40],buff2[80];
+ chandev_irqinfo *read_irqinfo,*write_irqinfo,*data_irqinfo;
+ char buff[3][80];
chandev_lock();
- chandev_read_conf_if_necessary();
chandev_printf(chan_exit,"\n%s\n"
"*'s for cu/dev type/models indicate don't cares\n",chandev_keydescript);
+ chandev_printf(chan_exit,"\ncautious_auto_detect: %s\n",chandev_cautious_auto_detect ? "on":"off");
+ chandev_printf(chan_exit,"\nchandev_persistent = 0x%02x\n",chandev_persistent);
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- chandev_printf(chan_exit,"\nuse_devno_names: %s\n\n",use_devno_names ? "on":"off");
+ chandev_printf(chan_exit,"\nuse_devno_names: %s\n\n",chandev_use_devno_names ? "on":"off");
#endif
+
if(chandev_models_head)
{
chandev_printf(chan_exit,"Channels enabled for detection\n");
- chandev_printf(chan_exit," chan cu cu dev dev max auto recovery\n");
- chandev_printf(chan_exit," type type model type model port_no. type \n");
- chandev_printf(chan_exit,"============================================================\n");
+ chandev_printf(chan_exit," chan cu cu dev dev max checksum use hw auto recovery\n");
+ chandev_printf(chan_exit," type type model type model port_no. received stats type\n");
+ chandev_printf(chan_exit,"==============================================================================\n");
for_each(curr_model,chandev_models_head)
{
- chandev_sprint_devinfo(buff,curr_model->cu_type,
+ chandev_sprint_devinfo(buff[0],curr_model->cu_type,
curr_model->cu_model,
curr_model->dev_type,
curr_model->dev_model);
- sprintf_msck(buff2,curr_model->auto_msck_recovery);
- chandev_printf(chan_exit," 0x%02x %s%3d %s\n",
- curr_model->chan_type,buff,
- (int)curr_model->max_port_no,buff2);
+ sprintf_msck(buff[1],curr_model->auto_msck_recovery);
+ chandev_printf(chan_exit," 0x%02x %s%3d %s %s %s\n",
+ curr_model->chan_type,buff[0],
+ (int)curr_model->max_port_no,
+ curr_model->default_checksum_received_ip_pkts ? "yes":"no ",
+ curr_model->default_use_hw_stats ? "yes":"no ",
+ buff[1]);
}
}
@@ -2294,65 +2947,78 @@
chandev_printf(chan_exit,"===========================================\n");
for_each(curr_msck_range,chandev_msck_range_head)
{
- sprintf_msck(buff2,curr_msck_range->auto_msck_recovery);
+ sprintf_msck(buff[0],curr_msck_range->auto_msck_recovery);
chandev_printf(chan_exit," 0x%04x 0x%04x %s\n",
curr_msck_range->lo_devno,
- curr_msck_range->hi_devno,buff2)
+ curr_msck_range->hi_devno,buff[0])
}
}
if(chandev_force_head)
{
chandev_printf(chan_exit,"\nForced devices\n");
- chandev_printf(chan_exit," chan defif read write port ip hw\n");
- chandev_printf(chan_exit," type num devno devno protocol no. chksum stats\n");
- chandev_printf(chan_exit,"======================================================\n");
+ chandev_printf(chan_exit," chan defif read write data memory port ip hw host adapter api\n");
+ chandev_printf(chan_exit," type num devno devno devno usage(k) protocol no. chksum stats name name name\n");
+ chandev_printf(chan_exit,"===============================================================================================\n");
for_each(curr_force,chandev_force_head)
{
- chandev_printf(chan_exit," 0x%02x %3d 0x%04x 0x%04x %3d %1d %1d\n",
- curr_force->chan_type,curr_force->devif_num,
- curr_force->read_devno,curr_force->write_devno,
- curr_force->port_protocol_no,curr_force->checksum_received_ip_pkts,
- curr_force->use_hw_stats);
+ if(curr_force->memory_usage_in_k==0)
+ strcpy(buff[0],"default");
+ else
+ sprintf(buff[0],"%6d",curr_force->memory_usage_in_k);
+ chandev_printf(chan_exit," 0x%02x %3d 0x%04x 0x%04x 0x%04x %7s %3d %1d %1d%s",
+ (int)curr_force->chan_type,(int)curr_force->devif_num,
+ (int)curr_force->read_lo_devno,(int)curr_force->write_hi_devno,
+ (int)curr_force->data_devno,buff[0],
+ (int)curr_force->port_protocol_no,(int)curr_force->checksum_received_ip_pkts,
+ (int)curr_force->use_hw_stats,curr_force->chan_type==chandev_type_claw ? "":"\n");
+ if(curr_force->chan_type==chandev_type_claw)
+ {
+ chandev_printf(chan_exit," %9s %9s %9s\n",
+ curr_force->claw.host_name,
+ curr_force->claw.adapter_name,
+ curr_force->claw.api_type);
+ }
+
}
}
if(chandev_probelist_head)
{
#if CONFIG_ARCH_S390X
chandev_printf(chan_exit,"\nRegistered probe functions\n"
- "probefunc shutdownfunc reoperfunc chan devices\n"
- " type found\n"
- "==========================================================================\n");
+ "probefunc shutdownfunc msck_notfunc chan devices devices\n"
+ " type found active\n"
+ "==================================================================================\n");
#else
chandev_printf(chan_exit,"\nRegistered probe functions\n"
- "probefunc shutdownfunc reoperfunc chan devices\n"
- " type found\n"
- "==================================================\n");
+ "probefunc shutdownfunc msck_notfunc chan devices devices\n"
+ " type found active\n"
+ "===============================================================\n");
#endif
for_each(curr_probe,chandev_probelist_head)
{
- chandev_printf(chan_exit,"0x%p 0x%p 0x%p 0x%02x %d\n",
+ int devices_active=0;
+ for_each(curr_device,chandev_activelist_head)
+ {
+ if(curr_device->probefunc==curr_probe->probefunc)
+ devices_active++;
+ }
+ chandev_printf(chan_exit,"0x%p 0x%p 0x%p 0x%02x %d %d\n",
curr_probe->probefunc,
curr_probe->shutdownfunc,
- curr_probe->reoperfunc,
+ curr_probe->msck_notfunc,
curr_probe->chan_type,
- curr_probe->devices_found);
+ curr_probe->devices_found,
+ devices_active);
}
}
if(chandev_activelist_head)
{
-#if CONFIG_ARCH_S390X
+ unsigned long long total_memory_usage_in_k=0;
chandev_printf(chan_exit,
"\nInitialised Devices\n"
- " read write read write chan port dev dev read msck write msck\n"
- " irq irq devno devno type no. ptr name status status \n"
- "========================================================================================\n");
-#else
- chandev_printf(chan_exit,
- "\nInitialised Devices\n"
- " read write read write chan port dev dev read msck write msck\n"
- " irq irq devno devno type no. ptr name status status \n"
- "================================================================================\n");
-#endif
+ " read write data read write data chan port dev dev memory read msck write msck data msck\n"
+ " irq irq irq devno devno devno type no. ptr name usage(k) status status status\n"
+ "=====================================================================================================================\n");
/* We print this list backwards for cosmetic reasons */
for(curr_device=chandev_activelist_head;
curr_device->next!=NULL;curr_device=curr_device->next);
@@ -2360,19 +3026,44 @@
{
read_irqinfo=curr_device->read_irqinfo;
write_irqinfo=curr_device->write_irqinfo;
+ data_irqinfo=curr_device->data_irqinfo;
+ if(data_irqinfo)
+ {
+ sprintf(buff[0],"0x%04x",data_irqinfo->sch.irq);
+ sprintf(buff[1],"0x%04x",(int)data_irqinfo->sch.devno);
+ }
+ else
+ {
+ strcpy(buff[0]," n/a ");
+ strcpy(buff[1]," n/a ");
+ }
+ if(curr_device->memory_usage_in_k<0)
+ {
+ sprintf(buff[2],"%d",(int)-curr_device->memory_usage_in_k);
+ total_memory_usage_in_k-=curr_device->memory_usage_in_k;
+ }
+ else
+ strcpy(buff[2]," n/a ");
chandev_printf(chan_exit,
- "0x%04x 0x%04x 0x%04x 0x%04x 0x%02x %2d 0x%p %-10s %-12s %-12s\n",
- curr_device->read_irqinfo->irq,curr_device->write_irqinfo->irq,
- (int)read_irqinfo->devno,
- (int)write_irqinfo->devno,
+ "0x%04x 0x%04x %s 0x%04x 0x%04x %s 0x%02x %2d 0x%p %-10s %6s %-12s %-12s %-12s\n",
+ read_irqinfo->sch.irq,
+ write_irqinfo->sch.irq,
+ buff[0],
+ (int)read_irqinfo->sch.devno,
+ (int)write_irqinfo->sch.devno,
+ buff[1],
curr_device->chan_type,(int)curr_device->port_no,
curr_device->dev_ptr,curr_device->devname,
+ buff[2],
msck_status_strs[read_irqinfo->msck_status],
- msck_status_strs[write_irqinfo->msck_status]);
+ msck_status_strs[write_irqinfo->msck_status],
+ data_irqinfo ? msck_status_strs[data_irqinfo->msck_status] :
+ "not applicable");
get_prev((list *)chandev_activelist_head,
(list *)curr_device,
(list **)&curr_device);
}
+ chandev_printf(chan_exit,"\nTotal device memory usage %Luk.\n",total_memory_usage_in_k);
}
chandevs_detected=FALSE;
for(pass=FALSE;pass<=TRUE;pass++)
@@ -2380,9 +3071,9 @@
if(pass&&chandevs_detected)
{
chandev_printf(chan_exit,"\nchannels detected\n");
- chandev_printf(chan_exit," chan cu cu dev dev in chandev\n");
- chandev_printf(chan_exit," irq devno type type model type model use reg.\n");
- chandev_printf(chan_exit,"==========================================================\n");
+ chandev_printf(chan_exit," chan cu cu dev dev in chandev\n");
+ chandev_printf(chan_exit," irq devno type type model type model pim chpids use reg.\n");
+ chandev_printf(chan_exit,"===============================================================================\n");
}
for(curr_irq=get_irq_first(),loopcnt=0;curr_irq>=0; curr_irq=get_irq_next(curr_irq),loopcnt++)
{
@@ -2391,20 +3082,26 @@
printk(KERN_ERR"chandev_read_proc detected infinite loop bug in get_irq_next\n");
goto chan_error;
}
- if((curr_model=chandev_is_chandev(curr_irq,&curr_devinfo)))
+ if(chandev_is_chandev(curr_irq,&curr_devinfo,&curr_force,&curr_model))
{
+ schib_t *curr_schib;
+ curr_schib=s390_get_schib(curr_irq);
chandevs_detected=TRUE;
if(pass)
{
- chandev_printf(chan_exit,"0x%04x 0x%04x 0x%02x 0x%04x 0x%02x 0x%04x 0x%02x %-5s %-5s\n",
+ chandev_printf(chan_exit,"0x%04x 0x%04x 0x%02x 0x%04x 0x%02x 0x%04x 0x%02x 0x%02x 0x%016Lx %-5s %-5s\n",
curr_irq,curr_devinfo.devno,
- curr_model->chan_type,
+ ( curr_force ? curr_force->chan_type :
+ ( curr_model ? curr_model->chan_type :
+ chandev_type_none )),
(int)curr_devinfo.sid_data.cu_type,
(int)curr_devinfo.sid_data.cu_model,
(int)curr_devinfo.sid_data.dev_type,
(int)curr_devinfo.sid_data.dev_model,
- (curr_devinfo.status&DEVSTAT_DEVICE_OWNED) ? "yes":"no",
- (chandev_get_irqinfo_by_irq(curr_irq) ? "yes":"no"));
+ (int)(curr_schib ? curr_schib->pmcw.pim : 0),
+ *(long long *)(curr_schib ? &curr_schib->pmcw.chpid[0] : 0),
+ (curr_devinfo.status&DEVSTAT_DEVICE_OWNED) ? "yes":"no ",
+ (chandev_get_irqinfo_by_irq(curr_irq) ? "yes":"no "));
}
@@ -2418,14 +3115,14 @@
chandev_parms *curr_parms;
chandev_printf(chan_exit,"\n driver specific parameters\n");
- chandev_printf(chan_exit,"chan driver\n");
- chandev_printf(chan_exit,"type parameters\n");
+ chandev_printf(chan_exit,"chan lo hi driver\n");
+ chandev_printf(chan_exit,"type devno devno parameters\n");
chandev_printf(chan_exit,"=============================================================================\n");
for_each(curr_parms,chandev_parms_head)
{
- chandev_printf(chan_exit,"0x%02x %s\n",
- curr_parms->chan_type,
- curr_parms->parmstr);
+ chandev_printf(chan_exit,"0x%02x 0x%04x 0x%04x %s\n",
+ curr_parms->chan_type,(int)curr_parms->lo_devno,
+ (int)curr_parms->hi_devno,curr_parms->parmstr);
}
}
chan_error:
@@ -2448,7 +3145,6 @@
int rc;
char *buff;
- chandev_read_conf_if_necessary();
buff=vmalloc(count+1);
if(buff)
{
@@ -2485,29 +3181,26 @@
#endif
int __init chandev_init(void)
{
- if(!chandev_initialised)
- {
- chandev_parse_args();
- chandev_init_default_models();
+ atomic_set(&chandev_initialised,TRUE);
+ chandev_parse_args();
+ chandev_init_default_models();
#if CONFIG_PROC_FS
- chandev_create_proc();
+ chandev_create_proc();
#endif
- chandev_msck_task_tq.routine=
+ chandev_msck_task_tq.routine=
chandev_msck_task;
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- INIT_LIST_HEAD(&chandev_msck_task_tq.list);
- chandev_msck_task_tq.sync=0;
+ INIT_LIST_HEAD(&chandev_msck_task_tq.list);
+ chandev_msck_task_tq.sync=0;
#endif
- chandev_msck_task_tq.data=NULL;
- chandev_last_startmsck_list_update=chandev_last_machine_check=jiffies-HZ;
- atomic_set(&chandev_msck_thread_lock,1);
- chandev_lock_owner=CHANDEV_INVALID_LOCK_OWNER;
- chandev_lock_cnt=0;
- spin_lock_init(&chandev_spinlock);
- spin_lock_init(&chandev_not_oper_spinlock);
- chandev_initialised=TRUE;
- atomic_set(&chandev_new_msck,FALSE);
- }
+ chandev_msck_task_tq.data=NULL;
+ chandev_last_startmsck_list_update=chandev_last_machine_check=jiffies-HZ;
+ atomic_set(&chandev_msck_thread_lock,1);
+ chandev_lock_owner=CHANDEV_INVALID_LOCK_OWNER;
+ chandev_lock_cnt=0;
+ spin_lock_init(&chandev_spinlock);
+ spin_lock_init(&chandev_not_oper_spinlock);
+ atomic_set(&chandev_new_msck,FALSE);
return(0);
}
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
@@ -2516,7 +3209,7 @@
int chandev_register_and_probe(chandev_probefunc probefunc,
chandev_shutdownfunc shutdownfunc,
- chandev_reoperfunc reoperfunc,
+ chandev_msck_notification_func msck_notfunc,
chandev_type chan_type)
{
chandev_probelist *new_probe;
@@ -2524,14 +3217,13 @@
/* are initialised. */
chandev_interrupt_check();
- if(!chandev_initialised)
+ if(!atomic_compare_and_swap(FALSE,TRUE,&chandev_initialised))
chandev_init();
- chandev_read_conf_if_necessary();
if((new_probe=chandev_alloc(sizeof(chandev_probelist))))
{
new_probe->probefunc=probefunc;
new_probe->shutdownfunc=shutdownfunc;
- new_probe->reoperfunc=reoperfunc;
+ new_probe->msck_notfunc=msck_notfunc;
new_probe->chan_type=chan_type;
new_probe->devices_found=0;
chandev_add_to_list((list **)&chandev_probelist_head,new_probe);
@@ -2543,7 +3235,7 @@
void chandev_unregister(chandev_probefunc probefunc,int call_shutdown)
{
chandev_probelist *curr_probe=NULL;
- chandev_activelist *curr_device;
+ chandev_activelist *curr_device,*next_device;
chandev_interrupt_check();
chandev_lock();
@@ -2551,27 +3243,33 @@
{
if(curr_probe->probefunc==probefunc)
{
- for_each(curr_device,chandev_activelist_head)
- if(curr_device->probefunc==probefunc)
- {
- if(call_shutdown)
- {
- chandev_shutdown(curr_device);
- }
- }
+ for_each_allow_delete(curr_device,next_device,chandev_activelist_head)
+ if(curr_device->probefunc==probefunc&&call_shutdown)
+ chandev_shutdown(curr_device);
chandev_free_listmember((list **)&chandev_probelist_head,
(list *)curr_probe);
+ break;
}
}
chandev_unlock();
}
+
+int chandev_persist(chandev_type chan_type)
+{
+ return((chandev_persistent&chan_type) ? TRUE:FALSE);
+}
+
EXPORT_SYMBOL(chandev_register_and_probe);
EXPORT_SYMBOL(chandev_request_irq);
-EXPORT_SYMBOL(chandev_free_irq);
EXPORT_SYMBOL(chandev_unregister);
EXPORT_SYMBOL(chandev_initdevice);
+EXPORT_SYMBOL(chandev_build_device_name);
EXPORT_SYMBOL(chandev_initnetdevice);
-
-
+EXPORT_SYMBOL(chandev_init_netdev);
+EXPORT_SYMBOL(chandev_use_devno_names);
+EXPORT_SYMBOL(chandev_free_irq);
+EXPORT_SYMBOL(chandev_add_model);
+EXPORT_SYMBOL(chandev_del_model);
+EXPORT_SYMBOL(chandev_persist);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)