📄 chandev.c
字号:
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 argc,loopcnt; size_t allocsize; 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); strcpy(current->comm,"chandev_script"); for(loopcnt=0;loopcnt<10&&(jiffies-chandev_last_startmsck_list_update)<HZ;loopcnt++) { sleep_on_timeout(&wait,HZ); } if(!chandev_userland_notify_head) return(0); chandev_lock(); 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 */ /* not return if successful */ argv=alloca(allocsize); if(argv) { memset(argv,0,allocsize); argv[0]=hotplug_path; argv[1]="chandev"; argc=2; for(tagidx=chandev_first_tag;tagidx<chandev_num_notify_tags;tagidx++) { if(have_tag[tagidx]) { 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]; } } } } } chandev_free_all_list((list **)&chandev_userland_notify_head); chandev_unlock(); chandev_wait_for_root_fs(); /* We are basically execve'ing here there normally is no */ /* return */ retval=exec_usermodehelper(hotplug_path, argv, envp); goto Fail2; } Fail: chandev_unlock(); Fail2: return(retval);}void *chandev_allocstr(const char *str,size_t offset){ char *member; if((member=chandev_alloc(offset+strlen(str)+1))) { strcpy(&member[offset],str); } return((void *)member);}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_userland_notify_list *member,*nextmember; int pid; chandev_lock(); /* remove operations still outstanding for this device */ for_each_allow_delete(member,nextmember,chandev_userland_notify_head) if(strcmp(member->devname,devname)==0) chandev_free_listmember((list **)&chandev_userland_notify_head,(list *)member); if((member=chandev_allocstr(devname,offsetof(chandev_userland_notify_list,devname)))) { 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); if(pid<0) { printk("error making kernel thread for chandev_exec_start_script\n"); return(pid); } else return(0); } else { chandev_unlock(); printk("chandev_add_to_startmscklist memory allocation failed devname=%s\n",devname); return(-ENOMEM); }}int chandev_oper_func(int irq,devreg_t *dreg){ chandev_last_machine_check=jiffies; if(atomic_dec_and_test(&chandev_msck_thread_lock)) { schedule_task(&chandev_msck_task_tq); } atomic_set(&chandev_new_msck,TRUE); return(0);}static void chandev_not_oper_handler(int irq,int status ){ chandev_not_oper_struct *new_not_oper; chandev_last_machine_check=jiffies; if((new_not_oper=kmalloc(sizeof(chandev_not_oper_struct),GFP_ATOMIC))) { new_not_oper->irq=irq; new_not_oper->status=status; spin_lock(&chandev_not_oper_spinlock); enqueue_tail(&chandev_not_oper_head,(queue *)new_not_oper); spin_unlock(&chandev_not_oper_spinlock); if(atomic_dec_and_test(&chandev_msck_thread_lock)) { schedule_task(&chandev_msck_task_tq); } } else printk("chandev_not_oper_handler failed to allocate memory & " "lost a not operational interrupt %d %x", irq,status);}chandev_irqinfo *chandev_get_irqinfo_by_irq(int irq){ chandev_irqinfo *curr_irqinfo; for_each(curr_irqinfo,chandev_irqinfo_head) if(irq==curr_irqinfo->sch.irq) return(curr_irqinfo); return(NULL);}chandev *chandev_get_by_irq(int irq){ chandev *curr_chandev; for_each(curr_chandev,(chandev *)chandev_head.head) if(curr_chandev->sch.irq==irq) { return(curr_chandev); } return(NULL);}chandev_activelist *chandev_get_activelist_by_irq(int irq){ chandev_activelist *curr_device; for_each(curr_device,chandev_activelist_head) { 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);}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))) { 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, const char *devname, void *dev_id){ chandev_irqinfo *new_irqinfo; chandev_activelist *curr_device; s390_dev_info_t devinfo; int retval; chandev_lock(); if((curr_device=chandev_get_activelist_by_irq(irq))) { printk("chandev_request_irq failed devname=%s irq=%d " "it already belongs to %s shutdown this device first.\n", devname,irq,curr_device->devname); chandev_unlock(); return(-EPERM); } /* remove any orphan irqinfo left lying around. */ chandev_remove_irqinfo_by_irq(irq); chandev_unlock(); if((new_irqinfo=chandev_allocstr(devname,offsetof(chandev_irqinfo,devname)))) { if((retval=get_dev_info_by_irq(irq,&devinfo))|| (retval=s390_request_irq_special(irq,handler, chandev_not_oper_handler, irqflags,devname,dev_id))) kfree(new_irqinfo); else { 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; chandev_add_to_list((list **)&chandev_irqinfo_head,new_irqinfo); } } else { printk("chandev_request_irq memory allocation failed devname=%s irq=%d\n",devname,irq); retval=-ENOMEM; } 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); 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){ if(type==-1) strcpy(buff," * "); else sprintf(buff," 0x%04x ",(int)type); buff+=strlen(buff); if(model==-1) strcpy(buff," * "); else sprintf(buff," 0x%02x ",(int)model);}void chandev_sprint_devinfo(char *buff,s32 cu_type,s16 cu_model,s32 dev_type,s16 dev_model){ chandev_sprint_type_model(buff,cu_type,cu_model); chandev_sprint_type_model(&buff[strlen(buff)],dev_type,dev_model);}void chandev_remove_parms(chandev_type chan_type,int exact_match,int lo_devno){ chandev_parms *curr_parms,*next_parms; chandev_lock(); 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))&& (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,u16 lo_devno,u16 hi_devno,char *parmstr){ chandev_parms *parms; if(lo_devno>hi_devno) { 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_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, u8 default_checksum_received_ip_pkts,u8 default_use_hw_stats){ chandev_model_info *newmodel; int err; char buff[40]; if((newmodel=chandev_alloc(sizeof(chandev_model_info)))) { devreg_t *drinfo=&newmodel->drinfo; newmodel->chan_type=chan_type; newmodel->cu_type=cu_type; newmodel->cu_model=cu_model; newmodel->dev_type=dev_type; 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, newmodel->dev_type,newmodel->dev_model); printk(KERN_INFO"can't call s390_device_register for this device chan_type/chan_model/dev_type/dev_model %s\n",buff); kfree(newmodel); return; } drinfo->flag=DEVREG_TYPE_DEVCHARS; 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; drinfo->ci.hc.dmode=dev_model; drinfo->oper_func=chandev_oper_func; if((err=s390_device_register(&newmodel->drinfo))) { chandev_sprint_devinfo(buff,newmodel->cu_type,newmodel->cu_model, newmodel->dev_type,newmodel->dev_model); printk("s390_device_register failed in chandev_add_model"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -