diff options
author | davidxu <davidxu@FreeBSD.org> | 2005-11-30 05:12:03 +0000 |
---|---|---|
committer | davidxu <davidxu@FreeBSD.org> | 2005-11-30 05:12:03 +0000 |
commit | 5d50adf57dd3eb3f8996480fa4dd26e4756e268e (patch) | |
tree | dd58fac4b1e67b3b84318bcdf4611733ffd150f0 /sys/kern | |
parent | 06d803185574a0ba37434b1e98b322e54cc32a4e (diff) | |
download | FreeBSD-src-5d50adf57dd3eb3f8996480fa4dd26e4756e268e.zip FreeBSD-src-5d50adf57dd3eb3f8996480fa4dd26e4756e268e.tar.gz |
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.
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/kern_descrip.c | 11 | ||||
-rw-r--r-- | sys/kern/kern_thread.c | 1 | ||||
-rw-r--r-- | sys/kern/uipc_mqueue.c | 325 |
3 files changed, 230 insertions, 107 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index b6704ba..28922d3 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -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); diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c index a4ff105..798f0d4 100644 --- a/sys/kern/kern_thread.c +++ b/sys/kern/kern_thread.c @@ -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; diff --git a/sys/kern/uipc_mqueue.c b/sys/kern/uipc_mqueue.c index 81495ea..95903b5 100644 --- a/sys/kern/uipc_mqueue.c +++ b/sys/kern/uipc_mqueue.c @@ -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; @@ -485,24 +505,9 @@ mqfs_create_file(struct mqfs_node *parent, const char *name, int namelen) } /* - * 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(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) { - uma_zfree(mquser_zone, p); + 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; |