patch-2.4.4 linux/drivers/s390/misc/chandev.c
Next file: linux/drivers/s390/net/Makefile
Previous file: linux/drivers/s390/misc/Makefile
Back to the patch index
Back to the overall index
- Lines: 565
- Date:
Wed Apr 11 19:02:28 2001
- Orig file:
v2.4.3/linux/drivers/s390/misc/chandev.c
- Orig date:
Tue Feb 13 14:13:44 2001
diff -u --recursive --new-file v2.4.3/linux/drivers/s390/misc/chandev.c linux/drivers/s390/misc/chandev.c
@@ -6,7 +6,10 @@
*
* Generic channel device initialisation support.
*/
+#define TRUE 1
+#define FALSE 0
#define __KERNEL_SYSCALLS__
+#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/ctype.h>
@@ -20,6 +23,7 @@
#include <linux/vmalloc.h>
#include <asm/s390dyn.h>
#include <asm/queue.h>
+#include <linux/kmod.h>
typedef struct chandev_model_info chandev_model_info;
struct chandev_model_info
@@ -229,6 +233,7 @@
static long chandev_lock_owner;
static int chandev_lock_cnt;
static spinlock_t chandev_spinlock;
+void *chandev_firstlock_addr,*chandev_lastlock_addr;
typedef struct chandev_not_oper_struct chandev_not_oper_struct;
@@ -256,24 +261,6 @@
for((variable)=(head);(variable)!=NULL;(variable)=(variable)->next)
-#define CHANDEV_USE_KERNEL_THREADS (LINUX_VERSION_CODE<KERNEL_VERSION(2,4,0))
-
-#if CHANDEV_USE_KERNEL_THREADS
-static void chandev_start_msck_thread(void *unused);
-#if LINUX_VERSION_CODE<KERNEL_VERSION(2,3,0)
-#define chandev_daemonize(name,mask,use_init_fs) s390_daemonize(name,mask)
-#else
-#define chandev_daemonize(args...) s390_daemonize(args)
-#endif
-typedef int chandev_task_retval;
-#define chandev_task_return(val) return(val)
-#else
-#define chandev_daemonize(noargs...)
-#define chandev_task_retval void
-#define chandev_task_return(val) return
-#endif
-
-
static void chandev_lock(void)
{
chandev_interrupt_check();
@@ -283,18 +270,33 @@
spin_lock(&chandev_spinlock);
chandev_lock_cnt=1;
chandev_lock_owner=(long)current;
+ chandev_firstlock_addr=__builtin_return_address(0);
}
else
+ {
chandev_lock_cnt++;
+ chandev_lastlock_addr=__builtin_return_address(0);
+ }
if(chandev_lock_cnt<0||chandev_lock_cnt>100)
- panic("odd lock_cnt in lcs %d lcs_chan_lock",chandev_lock_cnt);
+ {
+ printk("odd lock_cnt %d lcs_chan_lock",chandev_lock_cnt);
+ chandev_lock_cnt=1;
+ }
}
+static int chandev_full_unlock(void)
+{
+ int ret_lock_cnt=chandev_lock_cnt;
+ chandev_lock_cnt=0;
+ chandev_lock_owner=CHANDEV_INVALID_LOCK_OWNER;
+ spin_unlock(&chandev_spinlock);
+ return(ret_lock_cnt);
+}
static void chandev_unlock(void)
{
if(chandev_lock_owner!=(long)current)
- panic("chandev_unlock: current=%lx"
+ printk("chandev_unlock: current=%lx"
" chandev_lock_owner=%lx chandev_lock_cnt=%d\n",
(long)current,
chandev_lock_owner,
@@ -305,19 +307,20 @@
spin_unlock(&chandev_spinlock);
}
if(chandev_lock_cnt<0)
- panic("odd lock_cnt in lcs %d lcs_chan_unlock",chandev_lock_cnt);
-}
+ {
+ printk("odd lock_cnt=%d in chan_unlock",chandev_lock_cnt);
+ chandev_full_unlock();
+ }
-int chandev_full_unlock(void)
-{
- int ret_lock_cnt=chandev_lock_cnt;
- chandev_lock_cnt=0;
- chandev_lock_owner=CHANDEV_INVALID_LOCK_OWNER;
- spin_unlock(&chandev_spinlock);
- return(ret_lock_cnt);
}
+void chandev_relock(int saved_lock_cnt)
+{
+
+ chandev_lock();
+ chandev_lock_cnt=saved_lock_cnt;
+}
void *chandev_alloc(size_t size)
@@ -405,145 +408,8 @@
}
-struct files_struct *chandev_new_files_struct(void)
-{
- struct files_struct *newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL);
- if (!newf)
- return(NULL);
- memset(newf,0,sizeof(struct files_struct));
- atomic_set(&newf->count, 1);
- newf->file_lock = RW_LOCK_UNLOCKED;
- newf->next_fd = 0;
- newf->max_fds = NR_OPEN_DEFAULT;
- newf->max_fdset = __FD_SETSIZE;
- newf->close_on_exec = &newf->close_on_exec_init;
- newf->open_fds = &newf->open_fds_init;
- newf->fd = &newf->fd_array[0];
- return(newf);
-}
-/*
- * Mostly robbed from kmod.c
- */
-static inline void
-use_init_fs_context(void)
-{
- struct fs_struct *our_fs, *init_fs;
- struct dentry *root, *pwd;
- struct vfsmount *rootmnt, *pwdmnt;
-
- /*
- * Make modprobe's fs context be a copy of init's.
- *
- * We cannot use the user's fs context, because it
- * may have a different root than init.
- * Since init was created with CLONE_FS, we can grab
- * its fs context from "init_task".
- *
- * The fs context has to be a copy. If it is shared
- * with init, then any chdir() call in modprobe will
- * also affect init and the other threads sharing
- * init_task's fs context.
- *
- * We created the exec_modprobe thread without CLONE_FS,
- * so we can update the fields in our fs context freely.
- */
-
- init_fs = init_task.fs;
- read_lock(&init_fs->lock);
- rootmnt = mntget(init_fs->rootmnt);
- root = dget(init_fs->root);
- pwdmnt = mntget(init_fs->pwdmnt);
- pwd = dget(init_fs->pwd);
- read_unlock(&init_fs->lock);
-
- /* FIXME - unsafe ->fs access */
- our_fs = current->fs;
- our_fs->umask = init_fs->umask;
- set_fs_root(our_fs, rootmnt, root);
- set_fs_pwd(our_fs, pwdmnt, pwd);
- write_lock(&our_fs->lock);
- if (our_fs->altroot) {
- struct vfsmount *mnt = our_fs->altrootmnt;
- struct dentry *dentry = our_fs->altroot;
- our_fs->altrootmnt = NULL;
- our_fs->altroot = NULL;
- write_unlock(&our_fs->lock);
- dput(dentry);
- mntput(mnt);
- } else
- write_unlock(&our_fs->lock);
- dput(root);
- mntput(rootmnt);
- dput(pwd);
- mntput(pwdmnt);
-}
-
-
-static int exec_usermodehelper(char *program_path, char *argv[], char *envp[])
-{
- int err;
- wait_queue_head_t wait;
-
-
- current->session = 1;
- current->pgrp = 1;
-
- /* We copy this off init & can't go until this is set up */
- init_waitqueue_head(&wait);
- while(init_task.fs->root==NULL)
- {
- sleep_on_timeout(&wait,HZ);
- }
- use_init_fs_context();
-
- /* Prevent parent user process from sending signals to child.
- Otherwise, if the modprobe program does not exist, it might
- be possible to get a user defined signal handler to execute
- as the super user right after the execve fails if you time
- the signal just right.
- */
- spin_lock_irq(¤t->sigmask_lock);
- flush_signals(current);
- flush_signal_handlers(current);
- spin_unlock_irq(¤t->sigmask_lock);
- /* current->files sometimes was null this means we need */
- /* to build our own */
- exit_files(current);
- if((current->files=chandev_new_files_struct())==NULL)
- {
- printk("chandev_new_files_struct allocation failed\n");
- return(0);
- }
-
- /* Drop the "current user" thing */
- {
- struct user_struct *user = current->user;
- current->user = INIT_USER;
- atomic_inc(&INIT_USER->__count);
- atomic_inc(&INIT_USER->processes);
- atomic_dec(&user->processes);
- free_uid(user);
- }
-
- /* Take all effective privileges.. */
- current->uid = current->euid = current->fsuid = 0;
- cap_set_full(current->cap_effective);
- /* Allow execve & open args to be in kernel space. */
- set_fs(KERNEL_DS);
- /* We need stdin out & err for scripts */
- if (open("/dev/console", O_RDWR, 0)< 0)
- printk("chandev exec_usermode_helper unable to open an initial console.\n");
- (void) dup(0);
- (void) dup(0);
-
-
- /* Go, go, go... */
- err=execve(program_path, argv, envp);
- return err;
-}
-
-static int exec_start_script(void *unused)
+static int chandev_exec_start_script(void *unused)
{
char **argv,*tempname;
@@ -555,7 +421,8 @@
static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
init_waitqueue_head(&wait);
- s390_daemonize("chandev_script",0,FALSE);
+ strcpy(current->comm,"chandev_script");
+
for(loopcnt=0;loopcnt<10&&(jiffies-chandev_last_startmsck_list_update)<HZ;loopcnt++)
{
sleep_on_timeout(&wait,HZ);
@@ -612,6 +479,12 @@
chandev_free_all_list((list **)&startlist_head);
chandev_free_all_list((list **)&mscklist_head);
chandev_unlock();
+
+ /* We need to wait till there is a root filesystem */
+ while(init_task.fs->root==NULL)
+ {
+ sleep_on_timeout(&wait,HZ);
+ }
/* We are basically execve'ing here there normally is no */
/* return */
retval=exec_usermodehelper(exec_script, argv, envp);
@@ -623,7 +496,7 @@
Fail2:
/* We don't really need to report /bin/chandev not existing */
if(retval!=-ENOENT)
- printk("exec_start_script failed retval=%d\n",retval);
+ printk("chandev_exec_start_script failed retval=%d\n",retval);
return(0);
}
@@ -643,7 +516,6 @@
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)
{
- int retval;
chandev_startmsck_list *member;
int pid;
@@ -664,14 +536,11 @@
add_to_list((list **)listhead,(list *)member);
chandev_last_startmsck_list_update=jiffies;
chandev_unlock();
- /* We do CLONE_FILES so we can exit_files to get rid of it */
- /* cheaply & allocate a new one we need current->files & */
- /* some tasks have current->files==NULL */
- pid = kernel_thread(exec_start_script,NULL,CLONE_FILES|SIGCHLD);
+ pid = kernel_thread(chandev_exec_start_script,NULL,SIGCHLD);
if(pid<0)
{
- printk("error making kernel thread for exec_start_script\n");
- retval=pid;
+ printk("error making kernel thread for chandev_exec_start_script\n");
+ return(pid);
}
else
return(0);
@@ -679,11 +548,10 @@
}
else
{
+ chandev_unlock();
printk("chandev_add_to_startmscklist memory allocation failed devname=%s\n",devname);
- retval=-ENOMEM;
+ return(-ENOMEM);
}
- chandev_unlock();
- return(retval);
}
@@ -695,11 +563,7 @@
chandev_last_machine_check=jiffies;
if(atomic_dec_and_test(&chandev_msck_thread_lock))
{
-#if CHANDEV_USE_KERNEL_THREADS
- queue_task(&chandev_msck_task_tq,&tq_scheduler);
-#else
schedule_task(&chandev_msck_task_tq);
-#endif
}
atomic_set(&chandev_new_msck,TRUE);
return(0);
@@ -719,11 +583,7 @@
spin_unlock(&chandev_not_oper_spinlock);
if(atomic_dec_and_test(&chandev_msck_thread_lock))
{
-#if CHANDEV_USE_KERNEL_THREADS
- queue_task(&chandev_msck_task_tq,&tq_scheduler);
-#else
schedule_task(&chandev_msck_task_tq);
-#endif
}
}
else
@@ -767,6 +627,18 @@
}
+void chandev_remove_irqinfo_by_irq(unsigned int irq)
+{
+ chandev_irqinfo *remove_irqinfo;
+
+ 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,
+ (list *)remove_irqinfo);
+ chandev_unlock();
+
+}
int chandev_request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),
@@ -790,9 +662,7 @@
return(-EPERM);
}
/* remove any orphan irqinfo left lying around. */
- if((new_irqinfo=chandev_get_irqinfo_by_irq(irq)))
- chandev_remove_from_list((list **)chandev_irqinfo_head,
- (list *)new_irqinfo);
+ chandev_remove_irqinfo_by_irq(irq);
chandev_unlock();
if((new_irqinfo=chandev_allocstr(devname,offsetof(chandev_irqinfo,devname))))
{
@@ -819,6 +689,12 @@
return(retval);
}
+void chandev_free_irq(unsigned int irq, void *dev_id)
+{
+ /* remove any orphan irqinfo left lying around. */
+ chandev_remove_irqinfo_by_irq(irq);
+ free_irq(irq,dev_id);
+}
void chandev_sprint_type_model(char *buff,s32 type,s16 model)
@@ -1148,6 +1024,7 @@
void chandev_shutdown(chandev_activelist *curr_device)
{
+ int saved_lock_cnt;
chandev_lock();
if(curr_device->category==network_device)
@@ -1155,12 +1032,16 @@
/* unregister_netdev calls the dev->close so we shouldn't do this */
/* this otherwise we crash */
if(curr_device->unreg_dev)
- curr_device->unreg_dev(curr_device->dev_ptr);
+ {
+ saved_lock_cnt=chandev_full_unlock();
+ curr_device->unreg_dev(curr_device->dev_ptr);
+ chandev_relock(saved_lock_cnt);
+ }
}
+ 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_irqinfo_head,(list *)curr_device->read_irqinfo);
- chandev_free_listmember((list **)&chandev_irqinfo_head,(list *)curr_device->write_irqinfo);
chandev_free_listmember((list **)&chandev_activelist_head,
(list *)curr_device);
chandev_unlock();
@@ -1492,8 +1373,7 @@
/* as probefunctions can call schedule & */
/* reenter to do a kernel thread & we may deadlock */
rc=probefunc(&probeinfo);
- chandev_lock();
- chandev_lock_cnt=saved_lock_cnt;
+ chandev_relock(saved_lock_cnt);
if(rc==0)
{
newdevice=probeinfo.newdevice;
@@ -1619,9 +1499,13 @@
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)
{
@@ -1743,13 +1627,12 @@
}
-static chandev_task_retval chandev_msck_task(void *unused)
+static void chandev_msck_task(void *unused)
{
int loopcnt,not_oper_probe_required=FALSE;
wait_queue_head_t wait;
chandev_not_oper_struct *new_not_oper;
- chandev_daemonize("chandev_msck_kernel_thread",0,TRUE);
/* This loop exists because machine checks tend to come in groups & we have
to wait for the other devnos to appear also */
init_waitqueue_head(&wait);
@@ -1780,21 +1663,10 @@
}
if(not_oper_probe_required)
chandev_probe();
- chandev_task_return(0);
}
-#if CHANDEV_USE_KERNEL_THREADS
-static void chandev_start_msck_thread(void *unused)
-{
- /* tq_scheduler sometimes leaves interrupts disabled from do bottom half */
- __sti();
- kernel_thread((int (*)(void *))chandev_msck_task,
- (void*)NULL,0);
-}
-#endif
-
static char *argstrs[]=
@@ -1953,6 +1825,7 @@
chandev_type chan_type;
char *str,*currstr,*interpretstr=NULL;
int cnt,strcnt;
+ int retval=0;
#define CHANDEV_MAX_EXTRA_INTS 8
chandev_int ints[CHANDEV_MAX_EXTRA_INTS+1];
memset(ints,0,sizeof(ints));
@@ -2182,17 +2055,29 @@
else
goto BadArgs;
}
- return(1);
+ retval=1;
BadArgs:
- printk("chandev_setup bad argument %s",instr);
- if(errstr)
+ if(!retval)
{
- printk("%s %d interpreted as %s",errstr,lineno,interpretstr);
- if(strcnt>1)
- printk(" before semicolon no %d",cnt);
+ printk("chandev_setup bad argument %s",instr);
+ if(errstr)
+ {
+ printk("%s %d interpreted as %s",errstr,lineno,interpretstr);
+ if(strcnt>1)
+ printk(" before semicolon no %d",cnt);
+ }
+ printk(".\n Type man chandev for more info.\n\n");
}
- printk(".\n Type man chandev for more info.\n\n");
- return(0);
+ 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="
static int chandev_setup_bootargs(char *str,int paramno)
@@ -2608,11 +2493,7 @@
chandev_create_proc();
#endif
chandev_msck_task_tq.routine=
-#if CHANDEV_USE_KERNEL_THREADS
- chandev_start_msck_thread;
-#else
chandev_msck_task;
-#endif
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
INIT_LIST_HEAD(&chandev_msck_task_tq.list);
chandev_msck_task_tq.sync=0;
@@ -2684,4 +2565,13 @@
}
chandev_unlock();
}
+
+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_initnetdevice);
+
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)