Last step to make mq_notify conform to POSIX standard, If the process

has successfully attached a notification request to the message queue
via a queue descriptor, file closing should remove the attachment.
This commit is contained in:
David Xu 2005-11-30 05:12:03 +00:00
parent 908801933a
commit b2f92ef96b
5 changed files with 237 additions and 107 deletions

View file

@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/mqueue.h>
#include <sys/mutex.h>
#include <sys/namei.h>
#include <sys/proc.h>
@ -134,6 +135,7 @@ struct filelist filehead; /* head of list of open files */
int openfiles; /* actual number of open files */
struct sx filelist_lock; /* sx to protect filelist */
struct mtx sigio_lock; /* mtx to protect pointers to sigio */
void (*mq_fdclose)(struct thread *td, int fd, struct file *fp);
/* A mutex to protect the association between a proc and filedesc. */
static struct mtx fdesc_mtx;
@ -713,6 +715,8 @@ do_dup(struct thread *td, enum dup_type type, int old, int new, register_t *retv
*/
if (delfp != NULL) {
knote_fdclose(td, new);
if (delfp->f_type == DTYPE_MQUEUE)
mq_fdclose(td, new, delfp);
FILEDESC_UNLOCK(fdp);
(void) closef(delfp, td);
if (holdleaders) {
@ -1002,6 +1006,8 @@ close(td, uap)
* for the new fd.
*/
knote_fdclose(td, fd);
if (fp->f_type == DTYPE_MQUEUE)
mq_fdclose(td, fd, fp);
FILEDESC_UNLOCK(fdp);
error = closef(fp, td);
@ -1766,7 +1772,8 @@ fdcloseexec(struct thread *td)
*/
for (i = 0; i <= fdp->fd_lastfile; i++) {
if (fdp->fd_ofiles[i] != NULL &&
(fdp->fd_ofileflags[i] & UF_EXCLOSE)) {
(fdp->fd_ofiles[i]->f_type == DTYPE_MQUEUE ||
(fdp->fd_ofileflags[i] & UF_EXCLOSE))) {
struct file *fp;
knote_fdclose(td, i);
@ -1778,6 +1785,8 @@ fdcloseexec(struct thread *td)
fdp->fd_ofiles[i] = NULL;
fdp->fd_ofileflags[i] = 0;
fdunused(fdp, i);
if (fp->f_type == DTYPE_MQUEUE)
mq_fdclose(td, i, fp);
FILEDESC_UNLOCK(fdp);
(void) closef(fp, td);
FILEDESC_LOCK(fdp);

View file

@ -293,6 +293,7 @@ proc_linkup(struct proc *p, struct ksegrp *kg, struct thread *td)
}
else
p->p_ksi = NULL;
LIST_INIT(&p->p_mqnotifier);
p->p_numksegrps = 0;
p->p_numthreads = 0;

View file

@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
#include <sys/buf.h>
#include <sys/dirent.h>
#include <sys/event.h>
#include <sys/eventhandler.h>
#include <sys/fcntl.h>
#include <sys/filedesc.h>
#include <sys/file.h>
@ -143,17 +144,19 @@ struct mqfs_node {
#define VTON(vp) (((struct mqfs_vdata *)((vp)->v_data))->mv_node)
#define VTOMQ(vp) ((struct mqueue *)(VTON(vp)->mn_data))
#define VFSTOMQFS(m) ((struct mqfs_info *)((m)->mnt_data))
#define FPTOMQ(fp) (((struct mqueue_user *) \
(fp)->f_data)->mu_node->mn_data)
#define FPTOMQ(fp) ((struct mqueue *)(((struct mqfs_node *) \
(fp)->f_data)->mn_data))
TAILQ_HEAD(msgq, mqueue_msg);
struct mqueue;
struct mqueue_user
{
struct sigevent mu_sigev;
struct ksiginfo mu_ksi;
struct mqfs_node *mu_node;
struct proc *mu_proc;
struct mqueue_notifier {
LIST_ENTRY(mqueue_notifier) nt_link;
struct sigevent nt_sigev;
ksiginfo_t nt_ksi;
struct proc *nt_proc;
int nt_fd;
};
struct mqueue {
@ -168,7 +171,7 @@ struct mqueue {
int mq_senders;
struct selinfo mq_rsel;
struct selinfo mq_wsel;
struct mqueue_user *mq_notifier;
struct mqueue_notifier *mq_notifier;
};
#define MQ_RSEL 0x01
@ -202,12 +205,14 @@ SYSCTL_INT(_kern_mqueue, OID_AUTO, curmq, CTLFLAG_RW,
static int unloadable = 0;
static MALLOC_DEFINE(M_MQUEUEDATA, "mqdata", "mqueue data");
static eventhandler_tag exit_tag;
/* Only one instance per-system */
static struct mqfs_info mqfs_data;
static uma_zone_t mqnode_zone;
static uma_zone_t mqueue_zone;
static uma_zone_t mvdata_zone;
static uma_zone_t mquser_zone;
static uma_zone_t mqnoti_zone;
static struct vop_vector mqfs_vnodeops;
static struct fileops mqueueops;
@ -244,8 +249,12 @@ static int _mqueue_send(struct mqueue *mq, struct mqueue_msg *msg,
static int _mqueue_recv(struct mqueue *mq, struct mqueue_msg **msg,
int timo);
static void mqueue_send_notification(struct mqueue *mq);
static void mqueue_fdclose(struct thread *td, int fd, struct file *fp);
static void mq_proc_exit(void *arg, struct proc *p);
/* kqueue filters */
/*
* kqueue filters
*/
static void filt_mqdetach(struct knote *kn);
static int filt_mqread(struct knote *kn, long hint);
static int filt_mqwrite(struct knote *kn, long hint);
@ -278,17 +287,12 @@ mqfs_fileno_uninit(struct mqfs_info *mi)
up = mi->mi_unrhdr;
mi->mi_unrhdr = NULL;
delete_unrhdr(up);
uma_zdestroy(mqnode_zone);
uma_zdestroy(mqueue_zone);
uma_zdestroy(mvdata_zone);
uma_zdestroy(mquser_zone);
}
/*
* Allocate a file number
*/
void
static void
mqfs_fileno_alloc(struct mqfs_info *mi, struct mqfs_node *mn)
{
/* make sure our parent has a file number */
@ -329,7 +333,7 @@ mqfs_fileno_alloc(struct mqfs_info *mi, struct mqfs_node *mn)
/*
* Release a file number
*/
void
static void
mqfs_fileno_free(struct mqfs_info *mi, struct mqfs_node *mn)
{
switch (mn->mn_type) {
@ -441,7 +445,7 @@ mqfs_fixup_dir(struct mqfs_node *parent)
/*
* Create a directory
*/
struct mqfs_node *
static struct mqfs_node *
mqfs_create_dir(struct mqfs_node *parent, const char *name, int namelen)
{
struct mqfs_node *dir;
@ -462,12 +466,28 @@ mqfs_create_dir(struct mqfs_node *parent, const char *name, int namelen)
return (dir);
}
/*
* Create a symlink
*/
static struct mqfs_node *
mqfs_create_link(struct mqfs_node *parent, const char *name, int namelen)
{
struct mqfs_node *node;
node = mqfs_create_file(parent, name, namelen);
if (node == NULL)
return (NULL);
node->mn_type = mqfstype_symlink;
return (node);
}
#endif
/*
* Create a file
*/
struct mqfs_node *
static struct mqfs_node *
mqfs_create_file(struct mqfs_node *parent, const char *name, int namelen)
{
struct mqfs_node *node;
@ -484,25 +504,10 @@ mqfs_create_file(struct mqfs_node *parent, const char *name, int namelen)
return (node);
}
/*
* Create a symlink
*/
struct mqfs_node *
mqfs_create_link(struct mqfs_node *parent, const char *name, int namelen)
{
struct mqfs_node *node;
node = mqfs_create_file(parent, name, namelen);
if (node == NULL)
return (NULL);
node->mn_type = mqfstype_symlink;
return (node);
}
/*
* Destroy a node or a tree of nodes
*/
int
static int
mqfs_destroy(struct mqfs_node *node)
{
struct mqfs_node *parent;
@ -614,7 +619,7 @@ mqfs_init(struct vfsconf *vfc)
mvdata_zone = uma_zcreate("mvdata",
sizeof(struct mqfs_vdata), NULL, NULL, NULL,
NULL, UMA_ALIGN_PTR, 0);
mquser_zone = uma_zcreate("mquser", sizeof(struct mqueue_user),
mqnoti_zone = uma_zcreate("mqnotifier", sizeof(struct mqueue_notifier),
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
mi = &mqfs_data;
sx_init(&mi->mi_lock, "mqfs lock");
@ -631,6 +636,9 @@ mqfs_init(struct vfsconf *vfc)
mqfs_fileno_init(mi);
mqfs_fileno_alloc(mi, root);
mqfs_fixup_dir(root);
exit_tag = EVENTHANDLER_REGISTER(process_exit, mq_proc_exit, NULL,
EVENTHANDLER_PRI_ANY);
mq_fdclose = mqueue_fdclose;
return (0);
}
@ -644,11 +652,16 @@ mqfs_uninit(struct vfsconf *vfc)
if (!unloadable)
return (EOPNOTSUPP);
EVENTHANDLER_DEREGISTER(process_exit, exit_tag);
mi = &mqfs_data;
mqfs_destroy(mi->mi_root);
mi->mi_root = NULL;
mqfs_fileno_uninit(mi);
sx_destroy(&mi->mi_lock);
uma_zdestroy(mqnode_zone);
uma_zdestroy(mqueue_zone);
uma_zdestroy(mvdata_zone);
uma_zdestroy(mqnoti_zone);
return (0);
}
@ -667,7 +680,7 @@ do_recycle(void *context, int pending __unused)
/*
* Allocate a vnode
*/
int
static int
mqfs_allocv(struct mount *mp, struct vnode **vpp, struct mqfs_node *pn)
{
struct mqfs_vdata *vd;
@ -1527,7 +1540,7 @@ mqueue_savemsg(struct mqueue_msg *msg, char *msg_ptr, int *msg_prio)
error = copyout(((char *)msg) + sizeof(*msg), msg_ptr,
msg->msg_size);
if (error == 0)
if (error == 0 && msg_prio != NULL)
error = copyout(&msg->msg_prio, msg_prio, sizeof(int));
return (error);
}
@ -1676,14 +1689,16 @@ _mqueue_send(struct mqueue *mq, struct mqueue_msg *msg, int timo)
static void
mqueue_send_notification(struct mqueue *mq)
{
struct mqueue_user *mu;
struct mqueue_notifier *nt;
struct proc *p;
mtx_assert(&mq->mq_mutex, MA_OWNED);
mu = mq->mq_notifier;
PROC_LOCK(mu->mu_proc);
if (!KSI_ONQ(&mu->mu_ksi))
psignal_event(mu->mu_proc, &mu->mu_sigev, &mu->mu_ksi);
PROC_UNLOCK(mu->mu_proc);
nt = mq->mq_notifier;
p = nt->nt_proc;
PROC_LOCK(p);
if (!KSI_ONQ(&nt->nt_ksi))
psignal_event(p, &nt->nt_sigev, &nt->nt_ksi);
PROC_UNLOCK(p);
mq->mq_notifier = NULL;
}
@ -1805,16 +1820,58 @@ _mqueue_recv(struct mqueue *mq, struct mqueue_msg **msg, int timo)
return (error);
}
static __inline struct mqueue_user *
mquser_alloc(void)
static __inline struct mqueue_notifier *
notifier_alloc(void)
{
return (uma_zalloc(mquser_zone, M_WAITOK | M_ZERO));
return (uma_zalloc(mqnoti_zone, M_WAITOK | M_ZERO));
}
static __inline void
mquser_free(struct mqueue_user *p)
notifier_free(struct mqueue_notifier *p)
{
uma_zfree(mquser_zone, p);
uma_zfree(mqnoti_zone, p);
}
static struct mqueue_notifier *
notifier_search(struct proc *p, int fd)
{
struct mqueue_notifier *nt;
LIST_FOREACH(nt, &p->p_mqnotifier, nt_link) {
if (nt->nt_fd == fd)
break;
}
return (nt);
}
static void
notifier_insert(struct proc *p, struct mqueue_notifier *nt)
{
LIST_INSERT_HEAD(&p->p_mqnotifier, nt, nt_link);
}
static void
notifier_delete(struct proc *p, struct mqueue_notifier *nt)
{
LIST_REMOVE(nt, nt_link);
notifier_free(nt);
}
static void
notifier_remove(struct proc *p, struct mqueue *mq, int fd)
{
struct mqueue_notifier *nt;
mtx_assert(&mq->mq_mutex, MA_OWNED);
PROC_LOCK(p);
nt = notifier_search(p, fd);
if (nt != NULL) {
if (mq->mq_notifier == nt)
mq->mq_notifier = NULL;
sigqueue_take(&nt->nt_ksi);
notifier_delete(p, nt);
}
PROC_UNLOCK(p);
}
/*
@ -1823,12 +1880,11 @@ mquser_free(struct mqueue_user *p)
int
mq_open(struct thread *td, struct mq_open_args *uap)
{
char path[MQFS_NAMELEN+1];
char path[MQFS_NAMELEN + 1];
struct mq_attr attr, *pattr;
struct mqfs_node *pn;
struct filedesc *fdp;
struct file *fp;
struct mqueue_user *mu;
struct mqueue *mq;
int fd, error, len, flags, cmode;
@ -1853,7 +1909,7 @@ mq_open(struct thread *td, struct mq_open_args *uap)
error = copyinstr(uap->path, path, MQFS_NAMELEN + 1, NULL);
if (error)
return (error);
return (error);
/*
* The first character of name must be a slash (/) character
@ -1875,13 +1931,15 @@ mq_open(struct thread *td, struct mq_open_args *uap)
error = ENOENT;
} else {
mq = mqueue_alloc(pattr);
if (mq != NULL)
if (mq == NULL) {
error = ENFILE;
} else {
pn = mqfs_create_file(mqfs_data.mi_root,
path + 1, len - 1);
if (pn == NULL) {
if (mq != NULL)
if (pn == NULL) {
error = ENOSPC;
mqueue_free(mq);
error = ENOSPC;
}
}
}
@ -1919,17 +1977,11 @@ mq_open(struct thread *td, struct mq_open_args *uap)
mqnode_addref(pn);
sx_xunlock(&mqfs_data.mi_lock);
mu = mquser_alloc();
mu->mu_node = pn;
ksiginfo_init(&mu->mu_ksi);
mu->mu_ksi.ksi_flags |= KSI_INS | KSI_EXT;
mu->mu_ksi.ksi_code = SI_MESGQ;
mu->mu_proc = td->td_proc;
FILE_LOCK(fp);
fp->f_flag = (flags & (FREAD | FWRITE | O_NONBLOCK));
fp->f_type = DTYPE_MQUEUE;
fp->f_ops = &mqueueops;
fp->f_data = mu;
fp->f_data = pn;
FILE_UNLOCK(fp);
FILEDESC_LOCK_FAST(fdp);
@ -1978,7 +2030,6 @@ static int
_getmq(struct thread *td, int fd, _fgetf func,
struct file **fpp, struct mqfs_node **ppn, struct mqueue **pmq)
{
struct mqueue_user *mu;
struct mqfs_node *pn;
int error;
@ -1989,8 +2040,7 @@ _getmq(struct thread *td, int fd, _fgetf func,
fdrop(*fpp, td);
return (EBADF);
}
mu = (*fpp)->f_data;
pn = mu->mu_node;
pn = (*fpp)->f_data;
if (ppn)
*ppn = pn;
if (pmq)
@ -2104,11 +2154,15 @@ int
mq_notify(struct thread *td, struct mq_notify_args *uap)
{
struct sigevent ev;
struct mqueue_user *mu;
struct filedesc *fdp;
struct proc *p;
struct mqueue *mq;
struct file *fp;
struct mqueue_notifier *nt, *newnt = NULL;
int error;
p = td->td_proc;
fdp = td->td_proc->p_fd;
if (uap->sigev) {
error = copyin(uap->sigev, &ev, sizeof(ev));
if (error)
@ -2124,37 +2178,119 @@ mq_notify(struct thread *td, struct mq_notify_args *uap)
error = getmq(td, uap->mqd, &fp, NULL, &mq);
if (error)
return (error);
mu = fp->f_data;
again:
FILEDESC_LOCK_FAST(fdp);
if (fget_locked(fdp, uap->mqd) != fp) {
FILEDESC_UNLOCK_FAST(fdp);
error = EBADF;
goto out;
}
mtx_lock(&mq->mq_mutex);
FILEDESC_UNLOCK_FAST(fdp);
if (uap->sigev != NULL) {
if (mq->mq_notifier != NULL) {
error = EBUSY;
} else {
PROC_LOCK(td->td_proc);
sigqueue_take(&mu->mu_ksi);
PROC_UNLOCK(td->td_proc);
mq->mq_notifier = mu;
mu->mu_sigev = ev;
PROC_LOCK(p);
nt = notifier_search(p, uap->mqd);
if (nt == NULL) {
if (newnt == NULL) {
PROC_UNLOCK(p);
mtx_unlock(&mq->mq_mutex);
newnt = notifier_alloc();
goto again;
}
}
if (nt != NULL) {
sigqueue_take(&nt->nt_ksi);
if (newnt != NULL) {
notifier_free(newnt);
newnt = NULL;
}
} else {
nt = newnt;
newnt = NULL;
ksiginfo_init(&nt->nt_ksi);
nt->nt_ksi.ksi_flags |= KSI_INS | KSI_EXT;
nt->nt_ksi.ksi_code = SI_MESGQ;
nt->nt_proc = p;
nt->nt_fd = uap->mqd;
notifier_insert(p, nt);
}
nt->nt_sigev = ev;
mq->mq_notifier = nt;
PROC_UNLOCK(p);
/*
* if there is no receivers and message queue is not
* empty, we should send notification as soon as
* possible.
* if there is no receivers and message queue
* is not empty, we should send notification
* as soon as possible.
*/
if (mq->mq_receivers == 0 &&
!TAILQ_EMPTY(&mq->mq_msgq))
mqueue_send_notification(mq);
}
} else {
if (mq->mq_notifier == mu)
mq->mq_notifier = NULL;
else
error = EPERM;
notifier_remove(p, mq, uap->mqd);
}
mtx_unlock(&mq->mq_mutex);
out:
fdrop(fp, td);
if (newnt != NULL)
notifier_free(newnt);
return (error);
}
static void
mqueue_fdclose(struct thread *td, int fd, struct file *fp)
{
struct filedesc *fdp;
struct mqueue *mq;
fdp = td->td_proc->p_fd;
FILEDESC_LOCK_ASSERT(fdp, MA_OWNED);
if (fp->f_ops == &mqueueops) {
mq = FPTOMQ(fp);
mtx_lock(&mq->mq_mutex);
notifier_remove(td->td_proc, mq, fd);
/* have to wakeup thread in same process */
if (mq->mq_flags & MQ_RSEL) {
mq->mq_flags &= ~MQ_RSEL;
selwakeuppri(&mq->mq_rsel, PSOCK);
}
if (mq->mq_flags & MQ_WSEL) {
mq->mq_flags &= ~MQ_WSEL;
selwakeuppri(&mq->mq_wsel, PSOCK);
}
mtx_unlock(&mq->mq_mutex);
}
}
static void
mq_proc_exit(void *arg __unused, struct proc *p)
{
struct filedesc *fdp;
struct file *fp;
struct mqueue *mq;
int i;
fdp = p->p_fd;
FILEDESC_LOCK_FAST(fdp);
for (i = 0; i < fdp->fd_nfiles; ++i) {
fp = fget_locked(fdp, i);
if (fp != NULL && fp->f_ops == &mqueueops) {
mq = FPTOMQ(fp);
mtx_lock(&mq->mq_mutex);
notifier_remove(p, FPTOMQ(fp), i);
mtx_unlock(&mq->mq_mutex);
}
}
FILEDESC_UNLOCK_FAST(fdp);
KASSERT(LIST_EMPTY(&p->p_mqnotifier), ("mq notifiers left"));
}
static int
mqf_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
int flags, struct thread *td)
@ -2207,38 +2343,16 @@ mqf_poll(struct file *fp, int events, struct ucred *active_cred,
static int
mqf_close(struct file *fp, struct thread *td)
{
struct mqueue_user *mu;
struct mqfs_node *pn;
struct mqueue *mq;
FILE_LOCK(fp);
fp->f_ops = &badfileops;
FILE_UNLOCK(fp);
mu = fp->f_data;
pn = fp->f_data;
fp->f_data = NULL;
pn = mu->mu_node;
mq = pn->mn_data;
mtx_lock(&mq->mq_mutex);
if (mq->mq_notifier == mu) {
PROC_LOCK(td->td_proc);
sigqueue_take(&mu->mu_ksi);
PROC_UNLOCK(td->td_proc);
mq->mq_notifier = NULL;
}
/* have to wakeup thread in same process */
if (mq->mq_flags & MQ_RSEL) {
mq->mq_flags &= ~MQ_RSEL;
selwakeuppri(&mq->mq_rsel, PSOCK);
}
if (mq->mq_flags & MQ_WSEL) {
mq->mq_flags &= ~MQ_WSEL;
selwakeuppri(&mq->mq_wsel, PSOCK);
}
mtx_unlock(&mq->mq_mutex);
sx_xlock(&mqfs_data.mi_lock);
mqnode_release(pn);
sx_xunlock(&mqfs_data.mi_lock);
mquser_free(mu);
return (0);
}
@ -2246,8 +2360,7 @@ static int
mqf_stat(struct file *fp, struct stat *st, struct ucred *active_cred,
struct thread *td)
{
struct mqueue_user *mu = fp->f_data;
struct mqfs_node *pn = mu->mu_node;
struct mqfs_node *pn = fp->f_data;
bzero(st, sizeof *st);
st->st_atimespec = pn->mn_atime;

View file

@ -37,4 +37,9 @@ struct mq_attr {
long __reserved[4]; /* Ignored for input, zeroed for output */
};
#ifdef _KERNEL
struct thread;
struct file;
extern void (*mq_fdclose)(struct thread *td, int fd, struct file *fp);
#endif
#endif

View file

@ -158,6 +158,7 @@ struct sleepqueue;
struct td_sched;
struct trapframe;
struct turnstile;
struct mqueue_notifier;
/*
* Here we define the three structures used for process information.
@ -609,6 +610,7 @@ struct proc {
struct label *p_label; /* (*) Proc (not subject) MAC label. */
struct p_sched *p_sched; /* (*) Scheduler-specific data. */
STAILQ_HEAD(, ktr_request) p_ktr; /* (o) KTR event queue. */
LIST_HEAD(, mqueue_notifier) p_mqnotifier; /* (c) mqueue notifiers.*/
};
#define p_session p_pgrp->pg_session