gatesl@mist.cs.orst.edu (Lee Gates) (02/19/91)
As a class project, I am working on a modification to the BSD 4.3 source code to allow one to kill uninteruptable processes. It would seem that we are at a bit of a standpoint. Initially, I thought that I could have the kernel raise the priority of the suspect process in the psignal() call, which after setting it to run, would allow the process to release the resources it was sleeping with, and exit gracefully, as I would post the kill signal before letting it run again. The others in my group have questioned this, and now I have begun to wonder if it will work. Will the above method cause a race condition resulting from the fact that the process probably assumes that the next time it runs it will have the resource it was sleeping on? And if so, I would appreciate some other suggestions as to how to solve this problem. thanx -- lee gatesl@prism.cs.orst.edu "having fun watching Oregonians rust" ------------randomly-chosen-drink/quote/simpsons'-quote--------------- "If you choose not to decide you still have made a choice." - Geddy Lee
neil@pio.gid.co.uk (Neil Todd) (02/20/91)
In the referenced article gatesl@mist.cs.orst.edu (Lee Gates) writes: | | As a class project, I am working on a modification to the | BSD 4.3 source code to allow one to kill uninteruptable processes. (rest deleted) A while back (~4-5 yrs) Chris Torek (I think) produced a nice little patch to the 4.3 kernel to kill groups of run away (and rapidly spawning) processes - this was the 'zonk' system call. You could probably gain an insight into your problem by looking at this. The catch is that I don't have access to the machine that I installed the patch on. Zonk was very useful, especially on Student/teaching machines - one could guarantee that some bright spark would experiement with self spawining processes, zonk would kill all jobs owned by a particular UID stone dead. Neil
rock@cbnews.att.com (Y. Rock Lee) (02/21/91)
In article <19065@rpp386.cactus.org> jfh@rpp386.cactus.org (John F Haugh II) writes: >Just for kicks, imagine some real slow device has been set up to >do a DMA transfer to some physical address that is held by the >process which is unkillable. Imagine that you kill that process >and it exits. Imagine the I/O completes and someone elses >memory gets trashed. All that and more ... Please excuse my ignorance on the block devices (most of the time I work on character/streams device). Forcibly awaking a process doing read/write (DMA transter) will either give the process a buffer with garbage data or throw away a buffer containing valid data. How can this trash someone else's memory? Y. Rock Lee, att!cblph!rock rock@cblph.ATT.COM
rock@cbnews.att.com (Y. Rock Lee) (02/21/91)
In article <1991Feb20.232118.11035@odin.diku.dk> thorinn@diku.dk (Lars Henrik Mathiesen) writes: >The first class you probably shouldn't mess with. If you're lucky, >removing the sleeping process will only result in the loss of some >buffer. In worse cases, you get permanently un-openable devices or >crashes. The real cure for these is to rewrite _each_case_ to sleep at >interruptible priority and clean up properly (more than a class >project, I think). [this is a guess, not an argument] The "permanently un-openable devices" can only happen in the case of open. Because open wasn't "complete" so the close call in the exit cannot do a correct clean up. Please correct me if I miss something. Y. Rock Lee, att!cblph!rock rock@cblph.ATT.COM
rock@cbnews.att.com (Y. Rock Lee) (02/21/91)
In article <1991Feb19.001941.29928@lynx.CS.ORST.EDU> gatesl@mist.cs.orst.edu (Lee Gates) writes: > Will the above method cause a race condition resulting from >the fact that the process probably assumes that the next time it runs >it will have the resource it was sleeping on? And if so, I would >appreciate some other suggestions as to how to solve this problem. Yes, the process will think it has the resource it was sleeping on. But, it will be killed and release the resource during its exit before it has a chance to "think". This part looks OK to me. My only concern is that the driver of the particular device which the process is waiting for may react crazily when it is misinformed (a good driver should guard against this). Y. Rock Lee, att!cblph!rock rock@cblph.ATT.COM
torek@elf.ee.lbl.gov (Chris Torek) (02/21/91)
In article <4066@stl.stc.co.uk> "Neil Todd" <neil@pio.gid.co.uk> writes: >A while back (~4-5 yrs) Chris Torek (I think) produced a nice little >patch to the 4.3 kernel to kill groups of run away (and rapidly >spawning) processes - this was the 'zonk' system call. ``Not I,'' said the pig. (Since I just ate half a dozen chocolate chip cookies, I think I qualify. :-) ) Seriously: I never produced this particular bletcherous hack. (I am responsible for a number of other, different bletcherous hacks, but not this one.) If (A) you have SIGSTOP and (B) signals work correctly, the super-user can stop everything, pick out the bad processes, kill them, and then resume everything. (This is a bit tricky to get right, admittedly.) -- In-Real-Life: Chris Torek, Lawrence Berkeley Lab EE div (+1 415 486 5427) Berkeley, CA Domain: torek@ee.lbl.gov
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (02/22/91)
In article <10112@dog.ee.lbl.gov> torek@elf.ee.lbl.gov (Chris Torek) writes: > If (A) you have SIGSTOP and (B) signals work correctly, > the super-user can stop everything, pick out the bad processes, kill > them, and then resume everything. (This is a bit tricky to get right, > admittedly.) What's tricky about it? #include <sys/time.h> #include <sys/resource.h> #include <signal.h> #include <stdio.h> #include <errno.h> extern int errno; main(argc,argv,envp) /* invoke as, e.g., zonk /bin/csh csh -f; untested */ int argc; char *argv[]; char *envp[]; { if (getuid()) { fprintf(stderr,"zonk: fatal: uid not 0\n"); exit(1); } if (geteuid()) { fprintf(stderr,"zonk: fatal: euid not 0\n"); exit(2); } if (setpriority(PRIO_PROCESS,0,-20)) fprintf(stderr,"zonk: weird: can't set my priority to -20\n"); if (kill(-1,SIGSTOP) == -1) perror("zonk: warning: first kill failed"); if (kill(-1,SIGSTOP) == -1) perror("zonk: warning: second kill failed"); if (kill(-1,SIGSTOP) == -1) perror("zonk: warning: good-luck kill failed"); for (;;) { (void) execve(argv[1],argv + 2,envp); perror("zonk: critical: exec failed, will try again"); sleep(60); } } ---Dan
jfh@rpp386.cactus.org (John F Haugh II) (02/22/91)
In article <1991Feb21.145705.27763@cbnews.att.com> rock@cbnews.att.com (Y. Rock Lee) writes: >Forcibly awaking a process doing read/write (DMA transter) will either >give the process a buffer with garbage data or throw away a buffer >containing valid data. How can this trash someone else's memory? DMA addresses typically refer to physical memory. The process requesting the DMA transfer normally is locked in memory before the transfer is requested so that the physical address the controller was told to send the data to will remain valid. If the process dies and the physical memory is reallocated (or page out or swap out or ... occurs), that physical address will be allocated to some other process which isn't expecting to have your DMA transfer sent its way. -- John F. Haugh II UUCP: ...!cs.utexas.edu!rpp386!jfh Ma Bell: (512) 832-8832 Domain: jfh@rpp386.cactus.org "I've never written a device driver, but I have written a device driver manual" -- Robert Hartman, IDE Corp.
pat@orac.pgh.pa.us (Pat Barron) (02/22/91)
In article <1991Feb21.152845.29019@cbnews.att.com> rock@cbnews.att.com (Y. Rock Lee) writes: >In article <1991Feb19.001941.29928@lynx.CS.ORST.EDU> gatesl@mist.cs.orst.edu (Lee Gates) writes: >> Will the above method cause a race condition resulting from >>the fact that the process probably assumes that the next time it runs >>it will have the resource it was sleeping on? And if so, I would >>appreciate some other suggestions as to how to solve this problem. > >Yes, the process will think it has the resource it was sleeping on. Uhh, nope. When a particular even occurs, *all* processes waiting on that event are awakened. By the time you run again, someone else may have snarfed up the resource you were waiting for. This has been the case forever (well, at least since V7) - when you come out of a sleep(), you *must* check that the reason you were sleeping is no longer true.... --Pat.
sarima@tdatirv.UUCP (Stanley Friesen) (02/23/91)
In article <1991Feb21.145705.27763@cbnews.att.com> rock@cbnews.att.com (Y. Rock Lee) writes: >Forcibly awaking a process doing read/write (DMA transter) will either >give the process a buffer with garbage data or throw away a buffer >containing valid data. How can this trash someone else's memory? Scenario - forcibly wake-up a process waiting on a slow device, process mucks about with unfilled buffer, and *releases* it. process probably dies due to signal the woke it up. another process acquires the buffer from the free pool (Perhaps intending to use it for paging) slow device finally finishes, putting results into buffer. new process returns the buffer or (worse) uses it as a new page. VOILA - corrupted stuff in the new process. -- --------------- uunet!tdatirv!sarima (Stanley Friesen)
jfh@greenber.austin.ibm.com (John F Haugh II) (02/23/91)
In article <1991Feb21.152845.29019@cbnews.att.com> rock@cbnews.att.com (Y. Rock Lee) writes: >Yes, the process will think it has the resource it was sleeping on. >But, it will be killed and release the resource during its exit >before it has a chance to "think". This part looks OK to me. >My only concern is that the driver of the particular device which >the process is waiting for may react crazily when it is misinformed >(a good driver should guard against this). This just isn't true. A typical sleep loop looks something like while (some_status & some_busy_flag) sleep (&some_status, PRI_O_MINE); some_status |= some_busy_flag; If your only concern is getting this process to ignore the setting of "some_busy_flag", you might be doing the right thing - but remember - "some_status" still has the "some_busy_flag" set. Killing the process will not get that bit clear and if that bit being set is what is hanging the process, the next process to enter that loop is also going to hang. What is needed is an exception routine that understands =exactly= what to do to reset the resource to some well-defined state for any possible state the resource may be in. -- John F. Haugh II | I've Been Moved | MaBellNet: (512) 838-4340 SneakerNet: 809/1D064 | AGAIN ! | VNET: LCCB386 at AUSVMQ BangNet: ..!cs.utexas.edu!ibmchs!auschs!snowball.austin.ibm.com!jfh (e-i-e-i-o)
rock@cbnews.att.com (Y. Rock Lee) (02/23/91)
In article <5558@awdprime.UUCP> jfh@greenber.austin.ibm.com (John F Haugh II) writes: >In article <1991Feb21.152845.29019@cbnews.att.com> rock@cbnews.att.com (Y. Rock Lee) writes: >>Yes, the process will think it has the resource it was sleeping on. >>But, it will be killed and release the resource during its exit >>before it has a chance to "think". This part looks OK to me. > >A typical sleep loop looks something like > > while (some_status & some_busy_flag) > sleep (&some_status, PRI_O_MINE); > > some_status |= some_busy_flag; This was what I had in mind (which was wrong after I double checked it): A signal puts this sleeping process back into the run queue (its priority has been set to higher than PZERO). sleep doesn't return; it does a longjmp back to syscall. Before the system call returns, it checks if there is a signal. There is. So, it handles the signal and exits (no signal handling routine set). The catch is that the process went to sleep before we change its priority. In this case sleep goes different route and does a simple return. Therefore, we will continue execute the driver code, which may be dangerous! >What is needed is an exception routine that understands =exactly= >what to do to reset the resource to some well-defined state for any >possible state the resource may be in. That's the reason why system priority is chosen to begin with. So, don't mess with it IF you can convince your professor not to do this project, :-) But, I guess, it is OK to do "experiment" in school. On the other hand, this utility can be very useful. If a process is hanging but cannot be killed (sleeping in uninterruptable priority), you have two ways to get rid of it: use this utility or reboot the system. That is, this utility can be useful, but is DANGEROUS in general! Y. Rock Lee, att!cblph!rock rock@cblph.ATT.COM
gatesl@prism.cs.orst.edu (Lee Gates) (02/23/91)
In article <1991Feb23.025958.9914@cbnews.att.com> rock@cbnews.att.com (Y. Rock Lee) writes: > >This was what I had in mind (which was wrong after I double checked it): > > A signal puts this sleeping process back into the run queue (its > priority has been set to higher than PZERO). sleep doesn't return; > it does a longjmp back to syscall. Before the system call returns, > it checks if there is a signal. There is. So, it handles the signal > and exits (no signal handling routine set). > >The catch is that the process went to sleep before we change its priority. >In this case sleep goes different route and does a simple return. >Therefore, we will continue execute the driver code, which may be dangerous! > >>What is needed is an exception routine that understands =exactly= >>what to do to reset the resource to some well-defined state for any >>possible state the resource may be in. > >That's the reason why system priority is chosen to begin with. >So, don't mess with it IF you can convince your professor not to do >this project, :-) But, I guess, it is OK to do "experiment" in school. > >On the other hand, this utility can be very useful. If a process is hanging >but cannot be killed (sleeping in uninterruptable priority), you have two >ways to get rid of it: use this utility or reboot the system. That is, >this utility can be useful, but is DANGEROUS in general! > Since I posted, I have discussed it with him, and we have narrowed the field considerably. The only check the modification will do is to see if a serial device driver is locked. If so, it will release the serial device, and kill the process. Otherwise, it will have a double check to see if it really should kill the proc, then actually kill it. I feel I understand everything much better now, all I have to do is figure out where in the code to look... Thanx to all for your help! lee
cks@hawkwind.utcs.toronto.edu (Chris Siebenmann) (02/24/91)
pat@orac.pgh.pa.us (Pat Barron) writes: | This has been the case forever (well, at least since V7) - when you come | out of a sleep(), you *must* check that the reason you were sleeping is | no longer true.... Unless your code is careful to make sure that it is the only one which can consume the particular resource which it is sleeping on; consider, for example, a system call tracing facility, where you have a sepperate process that drains the trace buffers into a disk file. Also, one common strategy for dealing with the 'other people may have taken this resource already' is while (!resource) sleep(resource, priority); If you whack the process and get it to come out of the sleep, and the resource isn't available, the process will just go back to sleep, which isn't particularly helpful for the original poster's purpose. -- V9: the kernel where you can do fgrep <something> */*.[ch] and not get "Arguments too long". cks@hawkwind.utcs.toronto.edu ...!{utgpu,utzoo,watmath}!utgpu!cks
tif@doorstop.austin.ibm.com (Paul Chamberlain) (02/25/91)
In article <5558@awdprime.UUCP> jfh@greenber.austin.ibm.com (John F Haugh II) writes: >A typical sleep loop looks something like > > while (some_status & some_busy_flag) > sleep (&some_status, PRI_O_MINE); > > some_status |= some_busy_flag; John, perhaps it obvious, but I've seen several places that neglect to do this right, and I don't want it to become anymore widespread. Unless you like drivers that hang sometimes, this is the way it should be: DISABLE_INTERRUPTS; while (some_status & some_busy_flag) sleep (&some_status, PRI_O_MINE); ENABLE_INTERRUPTS; some_status |= some_busy_flag; Paul Chamberlain | I do NOT speak for IBM. IBM VNET: PAULCC AT AUSTIN 512/838-9662 | ...!cs.utexas.edu!ibmchs!auschs!doorstop.austin.ibm.com!tif
dbc@cimage.com (David Caswell) (02/26/91)
.n article <1991Feb21.145705.27763@cbnews.att.com> rock@cbnews.att.com (Y. Rock Lee) writes: .In article <19065@rpp386.cactus.org> jfh@rpp386.cactus.org (John F Haugh II) writes: .>Just for kicks, imagine some real slow device has been set up to .>do a DMA transfer to some physical address that is held by the .>process which is unkillable. Imagine that you kill that process .>and it exits. Imagine the I/O completes and someone elses .>memory gets trashed. All that and more ... . .Please excuse my ignorance on the block devices (most of the time .I work on character/streams device). It's the character devices that are doing the DMA transfer. Normal I/O even if it is character-at-a-time is block I/O.
rock@cbnews.att.com (Y. Rock Lee) (02/27/91)
In article <1991Feb25.184853.10487@cimage.com> dbc@dgsi.UUCP (David Caswell) writes: >It's the character devices that are doing the DMA transfer. Normal I/O >even if it is character-at-a-time is block I/O. I "browsed" through a disk driver over the weekend. The followings are what I've learned. Please comment if you see anything wrong. Normal block I/O uses the system buffer pool. Inside the disk strategy routine, the physical address of the granted system buffer is given to the disk controller for DMA data trnasfer. Since the kernel is not pageable, there is no need to lock this buffer in memory (does BSD have a pageable kernel?). The character disk driver, on the other hand, uses the address pointed to by the u_base directly. Since this user page may be paged out, the disk read/write routine goes through a physical I/O function to lock this user page in memory and uses its physical address for the DMA tranfer followed. "Raw disk I/O" is the common term used for this driver interface. Y. Rock Lee, att!cblph!rock rock@cblph.ATT.COM
mouse@thunder.mcrcim.mcgill.edu (der Mouse) (02/27/91)
In article <5583@awdprime.UUCP>, tif@doorstop.austin.ibm.com (Paul Chamberlain) writes: > In article <5558@awdprime.UUCP> jfh@greenber.austin.ibm.com (John F Haugh II) writes: >> A typical sleep loop looks something like >> while (some_status & some_busy_flag) >> sleep (&some_status, PRI_O_MINE); >> some_status |= some_busy_flag; > John, perhaps it obvious, but I've seen several places that neglect > to do this right, and I don't want it to become anymore widespread. > Unless you like drivers that hang sometimes, this is the way it > should be: > DISABLE_INTERRUPTS; > while (some_status & some_busy_flag) > sleep (&some_status, PRI_O_MINE); > ENABLE_INTERRUPTS; > some_status |= some_busy_flag; I would tend to move the enable after the bit set. I think at present it doesn't make any difference, but only because kernel code can't be preempted, except in a limited way by interrupts, and the interrupt handler never sets or clears the busy bit. If either of these changes, you'll be glad you have the enable after the setting of the busy bit! der Mouse old: mcgill-vision!mouse new: mouse@larry.mcrcim.mcgill.edu
jfh@rpp386.cactus.org (John F Haugh II) (02/28/91)
In article <1991Feb27.105436.1554@thunder.mcrcim.mcgill.edu> mouse@thunder.mcrcim.mcgill.edu (der Mouse) writes: >> DISABLE_INTERRUPTS; >> while (some_status & some_busy_flag) >> sleep (&some_status, PRI_O_MINE); >> ENABLE_INTERRUPTS; >> some_status |= some_busy_flag; > >I would tend to move the enable after the bit set. I think at present >it doesn't make any difference, but only because kernel code can't be >preempted, except in a limited way by interrupts, and the interrupt >handler never sets or clears the busy bit. If either of these changes, >you'll be glad you have the enable after the setting of the busy bit! If any of the other bits are modified at interrupt level you have to make =any= modifications to =any= bits in the status word with interrupts disabled for the highest level the word is modified at. Consider the execution of a process off interrupt level which is then interrupted by the device being serviced. Thread "A" is the non-interrupt level execution, and thread "B" is the instruction stream executed at interrupt time. It isn't a big window, but with Murphy at the controls ... Thread A Thread B wakes up, checks bits and sees that resource is free loads the word at 'some_status' so it can set the busy bit and write it back POING! interrupt occurs and device driver loads word at 'some_status' so it can set some bit. the bit gets set and the word gets written back execution resumes after the interrupt with the original value of 'some_status' still in the register it was loaded in to - without the bit set from the interrupt service routine the 'some_busy_bit' is set and the word written back to 'some_status'. the action taken during the interrupt service has been overwritten. -- John F. Haugh II UUCP: ...!cs.utexas.edu!rpp386!jfh Ma Bell: (512) 832-8832 Domain: jfh@rpp386.cactus.org "I've never written a device driver, but I have written a device driver manual" -- Robert Hartman, IDE Corp.