kern: awake must not interrupt flushes in dev9p

While sending Tflush after a wakeup, we might call sleep.

Since the awake was not yet consumed, the new sleep was interrupted
causing a new Tflush, in an infinite loop that consumes all tags.
This commit is contained in:
Giacomo Tesio 2017-12-11 00:08:42 +01:00
parent 799f99a40f
commit a3918b108d
2 changed files with 50 additions and 23 deletions

View File

@ -54,6 +54,10 @@ static AlarmPool awkpool;
static AwakeAlarm *registry;
static Lock rl;
enum
{
AwakeDisabled = 64, /* must be greater than all syscalls */
};
typedef enum ElapsedAlarmFate
{
@ -146,6 +150,8 @@ awake_can_interrupt(Syscalls scall)
{
if(scall == 0)
panic("awake_can_interrupts on page fault");
if(scall == (Syscalls)AwakeDisabled)
return 0;
if(scall >= nelem(awakeable_syscalls) - 1)
panic("awake_can_interrupts: unknown syscall %d", scall);
return awakeable_syscalls[scall];
@ -153,7 +159,7 @@ awake_can_interrupt(Syscalls scall)
#else
# define awake_detect_loop(h)
# define awake_can_interrupt(scall) (awakeable_syscalls[scall])
# define awake_can_interrupt(scall) (scall != (Syscalls)AwakeDisabled && awakeable_syscalls[scall])
//# undef assert
//# define assert(a)
#endif
@ -230,6 +236,8 @@ void
awake_fell_asleep(Proc *p)
{
Syscalls cs = p->cursyscall;
if(p->wakeups[p->notified].blockingsc == (Syscalls)AwakeDisabled)
return;
if(cs != 0 && cs != SysAwake){
/* awake_register might sleep() on alarm_new and we
* don't want this sleep to be interrupted.
@ -243,7 +251,7 @@ Syscalls
awake_disable(void)
{
Syscalls blockingsc = up->wakeups[up->notified].blockingsc;
up->wakeups[up->notified].blockingsc = 0;
up->wakeups[up->notified].blockingsc = AwakeDisabled;
return blockingsc;
}
void

View File

@ -1,3 +1,20 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2015-2017 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
@ -807,6 +824,7 @@ mountio(Mnt *mnt, Mntrpc *r)
int n;
Syscalls awksysc;
awksysc = 0;
while(waserror()) {
if(mnt->rip == up)
mntgate(mnt);
@ -847,6 +865,23 @@ mountio(Mnt *mnt, Mntrpc *r)
}
b->wp += n;
poperror();
if(r->request.type == Tflush){
/* Tflush must not be interrupted by awake.
*
* The following code might call sleep() but it must
* since the syscall has already been interrupted
* (or we would not have to send a Tflush) awake must
* not interrupt such sleep.
*
* Thus we disable awake otherwise we will consume all
* tags trying to flush the flush.
*
* TODO: verify we do not have to do the same
* with notes
*/
awksysc = awake_disable();
}
mnt->c->dev->bwrite(mnt->c, b, 0);
/* Gate readers onto the mount point one at a time */
@ -855,29 +890,10 @@ mountio(Mnt *mnt, Mntrpc *r)
if(mnt->rip == nil)
break;
unlock(&mnt->l);
if(r->request.type == Tflush){
/* Tflush must not be interrupted by awake
* or we will consume all tags trying: since
* awake cannot know that it's not worth to
* flush an interrupted flush it will interrupt
* them all and each interrupt will cause
* a new flush to be sent.
*
* TODO: verify we do not have to do the same
* with notes
*/
awksysc = awake_disable();
if(waserror()){
awake_enable(awksysc);
nexterror();
}
}
sleep(r->z, rpcattn, r);
if(r->request.type == Tflush){
awake_enable(awksysc);
poperror();
}
if(r->done){
if(r->request.type == Tflush)
awake_enable(awksysc);
poperror();
mntflushfree(mnt, r);
return;
@ -890,6 +906,9 @@ mountio(Mnt *mnt, Mntrpc *r)
error(Emountrpc);
mountmux(mnt, r);
}
if(r->request.type == Tflush)
awake_enable(awksysc);
mntgate(mnt);
poperror();
mntflushfree(mnt, r);