VFS: properly cancel select queries on unpause

Change-Id: I16e71db3f5c1bcc7ba6045bc9f02b13d71dc31eb
This commit is contained in:
David van Moolenbroek 2013-08-25 00:26:38 +02:00 committed by Lionel Sambuc
parent 87aefd7eb2
commit 2e9f4d0198
5 changed files with 50 additions and 27 deletions

View file

@ -24,9 +24,6 @@
struct dmap dmap[NR_DEVICES]; struct dmap dmap[NR_DEVICES];
#define DT_EMPTY { no_dev, no_dev_io, NONE, "", 0, STYLE_NDEV, NULL, NONE, \
0, NULL, 0}
/*===========================================================================* /*===========================================================================*
* lock_dmap * * lock_dmap *
*===========================================================================*/ *===========================================================================*/
@ -281,10 +278,16 @@ void init_dmap()
{ {
/* Initialize the table with empty device <-> driver mappings. */ /* Initialize the table with empty device <-> driver mappings. */
int i; int i;
struct dmap dmap_default = DT_EMPTY;
for (i = 0; i < NR_DEVICES; i++) memset(dmap, 0, sizeof(dmap));
dmap[i] = dmap_default;
for (i = 0; i < NR_DEVICES; i++) {
dmap[i].dmap_opcl = no_dev;
dmap[i].dmap_io = no_dev_io;
dmap[i].dmap_driver = NONE;
dmap[i].dmap_style = STYLE_NDEV;
dmap[i].dmap_servicing = NONE;
}
} }
/*===========================================================================* /*===========================================================================*

View file

@ -20,6 +20,7 @@ extern struct dmap {
char dmap_label[LABEL_MAX]; char dmap_label[LABEL_MAX];
int dmap_flags; int dmap_flags;
int dmap_style; int dmap_style;
int dmap_sel_busy;
struct filp *dmap_sel_filp; struct filp *dmap_sel_filp;
endpoint_t dmap_servicing; endpoint_t dmap_servicing;
mutex_t dmap_lock; mutex_t dmap_lock;

View file

@ -557,7 +557,7 @@ void unpause(endpoint_t proc_e)
break; break;
case FP_BLOCKED_ON_SELECT:/* process blocking on select() */ case FP_BLOCKED_ON_SELECT:/* process blocking on select() */
select_forget(proc_e); select_forget();
break; break;
case FP_BLOCKED_ON_POPEN: /* process trying to open a fifo */ case FP_BLOCKED_ON_POPEN: /* process trying to open a fifo */

View file

@ -360,7 +360,7 @@ int do_gcov_flush(void);
int do_select(message *m_out); int do_select(message *m_out);
void init_select(void); void init_select(void);
void select_callback(struct filp *, int ops); void select_callback(struct filp *, int ops);
void select_forget(endpoint_t proc_e); void select_forget(void);
void select_reply1(endpoint_t driver_e, int minor, int status); void select_reply1(endpoint_t driver_e, int minor, int status);
void select_reply2(endpoint_t driver_e, int minor, int status); void select_reply2(endpoint_t driver_e, int minor, int status);
void select_timeout_check(timer_t *); void select_timeout_check(timer_t *);

View file

@ -377,7 +377,7 @@ static int select_request_major(struct filp *f, int *ops, int block)
return(SUSPEND); return(SUSPEND);
dp = &dmap[major]; dp = &dmap[major];
if (dp->dmap_sel_filp) if (dp->dmap_sel_busy)
return(SUSPEND); return(SUSPEND);
f->filp_select_flags &= ~FSF_UPDATE; f->filp_select_flags &= ~FSF_UPDATE;
@ -385,6 +385,7 @@ static int select_request_major(struct filp *f, int *ops, int block)
if (r != OK) if (r != OK)
return(r); return(r);
dp->dmap_sel_busy = TRUE;
dp->dmap_sel_filp = f; dp->dmap_sel_filp = f;
f->filp_select_flags |= FSF_BUSY; f->filp_select_flags |= FSF_BUSY;
@ -573,11 +574,11 @@ static void select_cancel_all(struct selectentry *se)
static void select_cancel_filp(struct filp *f) static void select_cancel_filp(struct filp *f)
{ {
/* Reduce number of select users of this filp */ /* Reduce number of select users of this filp */
dev_t major;
assert(f); assert(f);
assert(f->filp_selectors >= 0); assert(f->filp_selectors > 0);
if (f->filp_selectors == 0) return; assert(f->filp_count > 0);
if (f->filp_count == 0) return;
select_lock_filp(f, f->filp_select_ops); select_lock_filp(f, f->filp_select_ops);
@ -587,6 +588,17 @@ static void select_cancel_filp(struct filp *f)
f->filp_select_ops = 0; f->filp_select_ops = 0;
f->filp_select_flags = 0; f->filp_select_flags = 0;
f->filp_pipe_select_ops = 0; f->filp_pipe_select_ops = 0;
/* If this filp is the subject of an ongoing select query to a
* character device, mark the query as stale, so that this filp will
* not be checked when the result arrives.
*/
if (is_supported_major(f)) {
major = major(f->filp_vno->v_sdev);
if (dmap[major].dmap_sel_busy &&
dmap[major].dmap_sel_filp == f)
dmap[major].dmap_sel_filp = NULL; /* leave _busy set */
}
} }
unlock_filp(f); unlock_filp(f);
@ -638,24 +650,24 @@ void init_select(void)
/*===========================================================================* /*===========================================================================*
* select_forget * * select_forget *
*===========================================================================*/ *===========================================================================*/
void select_forget(endpoint_t proc_e) void select_forget(void)
{ {
/* Something has happened (e.g. signal delivered that interrupts select()). /* The calling thread's associated process is expected to be unpaused, due to
* Totally forget about the select(). */ * a signal that is supposed to interrupt the current system call.
* Totally forget about the select().
*/
int slot; int slot;
struct selectentry *se; struct selectentry *se;
for (slot = 0; slot < MAXSELECTS; slot++) { for (slot = 0; slot < MAXSELECTS; slot++) {
se = &selecttab[slot]; se = &selecttab[slot];
if (se->requestor != NULL && se->req_endpt == proc_e) if (se->requestor == fp)
break; break;
} }
if (slot >= MAXSELECTS) return; /* Entry not found */ if (slot >= MAXSELECTS) return; /* Entry not found */
se->error = EINTR;
if (is_deferred(se)) return; /* Still awaiting initial reply */
/* Do NOT test on is_deferred here. We can safely cancel ongoing queries. */
select_cancel_all(se); select_cancel_all(se);
} }
@ -743,20 +755,25 @@ int status;
dev = makedev(major, minor); dev = makedev(major, minor);
/* Get filp belonging to character special file */ /* Get filp belonging to character special file */
if ((f = dp->dmap_sel_filp) == NULL) { if (!dp->dmap_sel_busy) {
printf("VFS (%s:%d): major %d was not expecting a DEV_SELECT reply\n", printf("VFS (%s:%d): major %d was not expecting a DEV_SELECT reply\n",
__FILE__, __LINE__, major); __FILE__, __LINE__, major);
return; return;
} }
/* Is the filp still in use and busy waiting for a reply? The owner might /* The select filp may have been set to NULL if the requestor has been
* have vanished before the driver was able to reply. */ * unpaused in the meantime. In that case, we ignore the result, but we do
if (f->filp_count >= 1 && (f->filp_select_flags & FSF_BUSY)) { * look for other filps to restart later.
*/
if ((f = dp->dmap_sel_filp) != NULL) {
/* Find vnode and check we got a reply from the device we expected */ /* Find vnode and check we got a reply from the device we expected */
vp = f->filp_vno; vp = f->filp_vno;
assert(vp != NULL); assert(vp != NULL);
assert(S_ISCHR(vp->v_mode)); assert(S_ISCHR(vp->v_mode));
if (vp->v_sdev != dev) { if (vp->v_sdev != dev) {
/* This should never happen. The driver may be misbehaving.
* For now we assume that the reply we want will arrive later..
*/
printf("VFS (%s:%d): expected reply from dev %d not %d\n", printf("VFS (%s:%d): expected reply from dev %d not %d\n",
__FILE__, __LINE__, vp->v_sdev, dev); __FILE__, __LINE__, vp->v_sdev, dev);
return; return;
@ -764,12 +781,14 @@ int status;
} }
/* No longer waiting for a reply from this device */ /* No longer waiting for a reply from this device */
dp->dmap_sel_busy = FALSE;
dp->dmap_sel_filp = NULL; dp->dmap_sel_filp = NULL;
/* Process select result only if requestor is still around. That is, the /* Process the select result only if the filp is valid. */
* corresponding filp is still in use. if (f != NULL) {
*/ assert(f->filp_count >= 1);
if (f->filp_count >= 1) { assert(f->filp_select_flags & FSF_BUSY);
select_lock_filp(f, f->filp_select_ops); select_lock_filp(f, f->filp_select_ops);
f->filp_select_flags &= ~FSF_BUSY; f->filp_select_flags &= ~FSF_BUSY;