Module: xenomai-gch Branch: for-forge Commit: 0c2d552f45d3b1a5688129f515c489d04b9881e8 URL: http://git.xenomai.org/?p=xenomai-gch.git;a=commit;h=0c2d552f45d3b1a5688129f515c489d04b9881e8
Author: Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org> Date: Mon Dec 23 21:03:04 2013 +0100 cobalt/fd: add common fd implementation --- include/cobalt/kernel/fd.h | 186 ++++++++++++++ include/cobalt/kernel/ppd.h | 3 +- include/cobalt/uapi/syscall.h | 6 + kernel/cobalt/Makefile | 1 + kernel/cobalt/fd.c | 533 +++++++++++++++++++++++++++++++++++++++++ kernel/cobalt/init.c | 3 + kernel/cobalt/posix/Makefile | 1 + kernel/cobalt/posix/fdio.c | 74 ++++++ kernel/cobalt/posix/fdio.h | 35 +++ kernel/cobalt/posix/select.c | 9 + kernel/cobalt/posix/syscall.c | 7 + kernel/cobalt/shadow.c | 3 + lib/cobalt/rtdm.c | 57 ++++- 13 files changed, 907 insertions(+), 11 deletions(-) diff --git a/include/cobalt/kernel/fd.h b/include/cobalt/kernel/fd.h new file mode 100644 index 0000000..c2282d3 --- /dev/null +++ b/include/cobalt/kernel/fd.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2005-2007 Jan Kiszka <jan.kis...@web.de> + * Copyright (C) 2005 Joerg Langenberg <joerg.langenb...@gmx.net> + * Copyright (C) 2008,2013,2014 Gilles Chanteperdrix <g...@xenomai.org>. + * + * This program 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; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _COBALT_KERNEL_FD_H +#define _COBALT_KERNEL_FD_H + +#include <linux/types.h> +#include <linux/socket.h> +#include <cobalt/kernel/tree.h> + +struct xnfd; +struct xnselector; +struct xnsys_ppd; + +/** + * IOCTL handler + * + * @param[in] fd File descriptor structure associated with opened device instance + * @param[in] request Request number as passed by the user + * @param[in,out] arg Request argument as passed by the user + * + * @return A positive value or 0 on success. On failure return either + * -ENOSYS, to request that the function be called again from the opposite + * realtime/non-realtime context, or another negative error code. + * + * @see @c ioctl() in IEEE Std 1003.1, + * http://www.opengroup.org/onlinepubs/009695399 + */ +typedef int xnfd_ioctl_t(struct xnfd *fd, unsigned int request, void __user *arg); + +/** + * Read handler + * + * @param[in] fd File descriptor structure associated with opened device instance + * @param[out] buf Input buffer as passed by the user + * @param[in] size Number of bytes the user requests to read + * + * @return On success, the number of bytes read. On failure return either + * -ENOSYS, to request that this handler be called again from the opposite + * realtime/non-realtime context, or another negative error code. + * + * @see @c read() in IEEE Std 1003.1, + * http://www.opengroup.org/onlinepubs/009695399 + */ +typedef ssize_t xnfd_read_t(struct xnfd *fd, void __user *buf, size_t size); + +/** + * Write handler + * + * @param[in] fd File descriptor structure associated with opened device instance + * @param[in] buf Output buffer as passed by the user + * @param[in] size Number of bytes the user requests to write + * + * @return On success, the number of bytes written. On failure return + * either -ENOSYS, to request that this handler be called again from the + * opposite realtime/non-realtime context, or another negative error code. + * + * @see @c write() in IEEE Std 1003.1, + * http://www.opengroup.org/onlinepubs/009695399 + */ +typedef ssize_t xnfd_write_t(struct xnfd *fd, const void __user *buf, size_t size); + +/** + * Receive message handler + * + * @param[in] fd File descriptor structure associated with opened device instance + * @param[in,out] msg Message descriptor as passed by the user, automatically + * mirrored to safe kernel memory in case of user mode call + * @param[in] flags Message flags as passed by the user + * + * @return On success, the number of bytes received. On failure return + * either -ENOSYS, to request that this handler be called again from the + * opposite realtime/non-realtime context, or another negative error code. + * + * @see @c recvmsg() in IEEE Std 1003.1, + * http://www.opengroup.org/onlinepubs/009695399 + */ +typedef ssize_t xnfd_recvmsg_t(struct xnfd *fd, struct msghdr *msg, int flags); + +/** + * Transmit message handler + * + * @param[in] fd File descriptor structure associated with opened device instance + * @param[in] user_info Opaque pointer to information about user mode caller, + * NULL if kernel mode call + * @param[in] msg Message descriptor as passed by the user, automatically + * mirrored to safe kernel memory in case of user mode call + * @param[in] flags Message flags as passed by the user + * + * @return On success, the number of bytes transmitted. On failure return + * either -ENOSYS, to request that this handler be called again from the + * opposite realtime/non-realtime context, or another negative error code. + * + * @see @c sendmsg() in IEEE Std 1003.1, + * http://www.opengroup.org/onlinepubs/009695399 + */ +typedef ssize_t xnfd_sendmsg_t(struct xnfd *fd, const struct msghdr *msg, int flags); + +struct xnfd_ops { + xnfd_ioctl_t *ioctl_rt; + xnfd_ioctl_t *ioctl_nrt; + xnfd_read_t *read_rt; + xnfd_read_t *read_nrt; + xnfd_write_t *write_rt; + xnfd_write_t *write_nrt; + xnfd_recvmsg_t *recvmsg_rt; + xnfd_recvmsg_t *recvmsg_nrt; + xnfd_sendmsg_t *sendmsg_rt; + xnfd_sendmsg_t *sendmsg_nrt; + int (*select_bind)(struct xnfd *fd, struct xnselector *selector, + unsigned type, unsigned index); + void (*close)(struct xnfd *fd); +}; + +struct xnfd { + unsigned magic; + struct xnfd_ops *ops; + struct xnsys_ppd *cont; + unsigned refs; + struct list_head cleanup; +}; + +struct xnfd_index { + struct xnid id; + struct xnfd *fd; +}; + +#define XNFD_MAGIC_ANY 0 + +static inline struct xnsys_ppd *xnfd_owner(struct xnfd *fd) +{ + return fd->cont; +} + +int xnfd_enter(struct xnsys_ppd *p, struct xnfd *xnfd, int ufd, + unsigned magic, struct xnfd_ops *ops); + +struct xnfd *xnfd_get(struct xnsys_ppd *p, int ufd, unsigned magic); + +int xnfd_lock(struct xnfd *fd); + +void xnfd_put(struct xnfd *fd); + +void xnfd_unlock(struct xnfd *fd); + +int xnfd_ioctl(struct xnsys_ppd *p, int fd, unsigned request, ...); + +ssize_t xnfd_read(struct xnsys_ppd *p, int fd, void __user *buf, size_t size); + +ssize_t +xnfd_write(struct xnsys_ppd *p, int fd, const void __user *buf, size_t size); + +int xnfd_close(struct xnsys_ppd *p, int fd, unsigned magic); + +ssize_t +xnfd_recvmsg(struct xnsys_ppd *p, int fd, struct msghdr *msg, int flags); + +ssize_t +xnfd_sendmsg(struct xnsys_ppd *p, int fd, const struct msghdr *msg, int flags); + +int xnfd_valid_p(int ufd); + +int xnfd_select_bind(int ufd, struct xnselector *selector, unsigned type); + +void xnfd_cleanup(struct xnsys_ppd *p); + +void xnfd_init(void); + +#endif /* _COBALT_KERNEL_FD_H */ diff --git a/include/cobalt/kernel/ppd.h b/include/cobalt/kernel/ppd.h index 4ddd045..c869421 100644 --- a/include/cobalt/kernel/ppd.h +++ b/include/cobalt/kernel/ppd.h @@ -25,10 +25,10 @@ #include <cobalt/kernel/shadow.h> #include <cobalt/kernel/lock.h> #include <cobalt/kernel/heap.h> +#include <cobalt/kernel/fd.h> #define NR_PERSONALITIES 4 -/* Called with nklock locked irqs off. */ void *xnshadow_private_get(unsigned int muxid); struct xnsys_ppd { @@ -36,6 +36,7 @@ struct xnsys_ppd { unsigned long mayday_addr; atomic_t refcnt; char *exe_path; + struct rb_root fds; }; struct xnshadow_process { diff --git a/include/cobalt/uapi/syscall.h b/include/cobalt/uapi/syscall.h index d536c89..c67c0cf 100644 --- a/include/cobalt/uapi/syscall.h +++ b/include/cobalt/uapi/syscall.h @@ -113,5 +113,11 @@ #define sc_cobalt_event_destroy 92 #define sc_cobalt_sched_setconfig_np 93 #define sc_cobalt_sched_getconfig_np 94 +#define sc_cobalt_ioctl 95 +#define sc_cobalt_read 96 +#define sc_cobalt_write 97 +#define sc_cobalt_recvmsg 98 +#define sc_cobalt_sendmsg 99 +#define sc_cobalt_close 100 #endif /* !_COBALT_UAPI_SYSCALL_H */ diff --git a/kernel/cobalt/Makefile b/kernel/cobalt/Makefile index af4c2e0..2dfda25 100644 --- a/kernel/cobalt/Makefile +++ b/kernel/cobalt/Makefile @@ -5,6 +5,7 @@ xenomai-y := apc.o \ assert.o \ bufd.o \ clock.o \ + fd.o \ heap.o \ init.o \ intr.o \ diff --git a/kernel/cobalt/fd.c b/kernel/cobalt/fd.c new file mode 100644 index 0000000..cf59ab7 --- /dev/null +++ b/kernel/cobalt/fd.c @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2005 Jan Kiszka <jan.kis...@web.de> + * Copyright (C) 2005 Joerg Langenberg <joerg.langenb...@gmx.net> + * Copyright (C) 2013,2014 Gilles Chanteperdrix <g...@xenomai.org>. + * + * This program 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; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/list.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/kthread.h> +#include <cobalt/kernel/registry.h> +#include <cobalt/kernel/lock.h> +#include <cobalt/kernel/fd.h> +#include <cobalt/kernel/ppd.h> + +static DEFINE_XNLOCK(__xnfd_lock); +static LIST_HEAD(xnfd_cleanup_queue); +static struct semaphore xnfd_cleanup_sem; + +extern int +__rt_dev_ioctl_fallback(struct xnfd *fd, unsigned request, void __user *arg); +extern void __rt_dev_unref(struct xnfd *fd, unsigned idx); + +static int enosys(void) +{ + return -ENOSYS; +} + +static int ebadf(void) +{ + return -EBADF; +} + +static void nop_close(struct xnfd *fd) +{ +} + +static inline struct xnfd_index *xnfd_index_fetch(struct xnsys_ppd *p, int ufd) +{ + struct xnid *id = xnid_fetch(&p->fds, ufd); + if (id == NULL) + return NULL; + + return container_of(id, struct xnfd_index, id); +} + +static struct xnfd *xnfd_fetch(struct xnsys_ppd *p, int ufd) +{ + struct xnfd_index *idx = xnfd_index_fetch(p, ufd); + if (idx == NULL) + return NULL; + + return idx->fd; +} + +int xnfd_enter(struct xnsys_ppd *p, struct xnfd *fd, int ufd, + unsigned magic, struct xnfd_ops *ops) +{ + struct xnfd_index *idx; + spl_t s; + int err; + + if (magic == XNFD_MAGIC_ANY) { + err = -EINVAL; + goto err; + } + + idx = kmalloc(sizeof(*idx), GFP_KERNEL); + if (idx == NULL) { + err = -ENOMEM; + goto err; + } + + if (ops->ioctl_rt == NULL && ops->ioctl_nrt == NULL) + ops->ioctl_rt = ops->ioctl_nrt = (xnfd_ioctl_t *)ebadf; + else { + if (ops->ioctl_rt == NULL) + ops->ioctl_rt = (xnfd_ioctl_t *)enosys; + if (ops->ioctl_nrt == NULL) + ops->ioctl_nrt = (xnfd_ioctl_t *)enosys; + } + + if (ops->read_rt == NULL && ops->read_nrt == NULL) + ops->read_rt = ops->read_nrt = (xnfd_read_t *)ebadf; + else { + if (ops->read_rt == NULL) + ops->read_rt = (xnfd_read_t *)enosys; + if (ops->read_nrt == NULL) + ops->read_nrt = (xnfd_read_t *)enosys; + } + + if (ops->write_rt == NULL && ops->write_nrt == NULL) + ops->write_rt = ops->write_nrt = (xnfd_write_t *)ebadf; + else { + if (ops->write_rt == NULL) + ops->write_rt = (xnfd_write_t *)enosys; + if (ops->write_nrt == NULL) + ops->write_nrt = (xnfd_write_t *)enosys; + } + + if (ops->recvmsg_rt == NULL && ops->recvmsg_nrt == NULL) + ops->recvmsg_rt = ops->recvmsg_nrt = (xnfd_recvmsg_t *)ebadf; + else { + if (ops->recvmsg_rt == NULL) + ops->recvmsg_rt = (xnfd_recvmsg_t *)enosys; + if (ops->recvmsg_nrt == NULL) + ops->recvmsg_nrt = (xnfd_recvmsg_t *)enosys; + } + + if (ops->sendmsg_rt == NULL && ops->sendmsg_nrt == NULL) + ops->sendmsg_rt = ops->sendmsg_nrt = (xnfd_sendmsg_t *)ebadf; + else { + if (ops->sendmsg_rt == NULL) + ops->sendmsg_rt = (xnfd_sendmsg_t *)enosys; + if (ops->sendmsg_nrt == NULL) + ops->sendmsg_nrt = (xnfd_sendmsg_t *)enosys; + } + + if (ops->select_bind == NULL) + ops->select_bind = (typeof(ops->select_bind))ebadf; + + if (ops->close == NULL) + ops->close = nop_close; + + fd->magic = magic; + fd->ops = ops; + fd->cont = p; + fd->refs = 1; + + idx->fd = fd; + + xnlock_get_irqsave(&__xnfd_lock, s); + err = xnid_enter(&p->fds, &idx->id, ufd); + if (err < 0) { + xnlock_put_irqrestore(&__xnfd_lock, s); + err = -EBUSY; + goto err_free_index; + } + xnlock_put_irqrestore(&__xnfd_lock, s); + + return 0; + + err_free_index: + kfree(idx); + err: + if (ops->close) + ops->close(fd); + return err; +} + +struct xnfd *xnfd_get(struct xnsys_ppd *p, int ufd, unsigned magic) +{ + struct xnfd *res; + spl_t s; + + xnlock_get_irqsave(&__xnfd_lock, s); + res = xnfd_fetch(p, ufd); + if (res == NULL || (magic != XNFD_MAGIC_ANY && res->magic != magic)) { + res = ERR_PTR(-EBADF); + goto err_unlock; + } + + ++res->refs; + err_unlock: + xnlock_put_irqrestore(&__xnfd_lock, s); + + return res; +} +EXPORT_SYMBOL_GPL(xnfd_get); + +struct lostage_trigger_close { + struct ipipe_work_header work; /* Must be first */ +}; + +static void xnfd_do_close(struct xnfd *fd) +{ + fd->ops->close(fd); + + if (!XENO_ASSERT(NUCLEUS, !spltest())) + splnone(); +} + +static int xnfd_cleanup_thread(void *data) +{ + struct xnfd *fd; + int err; + spl_t s; + + for (;;) { + set_cpus_allowed_ptr(current, cpu_online_mask); + + do { + err = down_killable(&xnfd_cleanup_sem); + } while (err && !kthread_should_stop()); + + if (kthread_should_stop()) + break; + + xnlock_get_irqsave(&__xnfd_lock, s); + fd = list_first_entry(&xnfd_cleanup_queue, + struct xnfd, cleanup); + list_del(&fd->cleanup); + xnlock_put_irqrestore(&__xnfd_lock, s); + + xnfd_do_close(fd); + } + + return 0; +} + +static void lostage_trigger_close(struct ipipe_work_header *work) +{ + up(&xnfd_cleanup_sem); +} + +static void xnfd_put_inner(struct xnfd *fd, spl_t s) +{ + int destroy; + + destroy = --fd->refs == 0; + xnlock_put_irqrestore(&__xnfd_lock, s); + + if (!destroy) + return; + + if (ipipe_root_p) + xnfd_do_close(fd); + else { + struct lostage_trigger_close closework = { + .work = { + .size = sizeof(closework), + .handler = lostage_trigger_close, + }, + }; + + xnlock_get_irqsave(&__xnfd_lock, s); + list_add_tail(&fd->cleanup, &xnfd_cleanup_queue); + xnlock_put_irqrestore(&__xnfd_lock, s); + + ipipe_post_work_root(&closework, work); + } +} + +void xnfd_put(struct xnfd *fd) +{ + spl_t s; + + xnlock_get_irqsave(&__xnfd_lock, s); + xnfd_put_inner(fd, s); +} +EXPORT_SYMBOL_GPL(xnfd_put); + +int xnfd_lock(struct xnfd *fd) +{ + spl_t s; + + xnlock_get_irqsave(&__xnfd_lock, s); + if (fd->refs == 0) { + xnlock_put_irqrestore(&__xnfd_lock, s); + return -EIDRM; + } + ++fd->refs; + xnlock_put_irqrestore(&__xnfd_lock, s); + + return 0; +} +EXPORT_SYMBOL_GPL(xnfd_lock); + +void xnfd_unlock(struct xnfd *fd) +{ + spl_t s; + + xnlock_get_irqsave(&__xnfd_lock, s); +/* just warn if context was a dangling pointer */ + XENO_ASSERT(NUCLEUS, fd->refs > 0); + xnfd_put_inner(fd, s); +} +EXPORT_SYMBOL_GPL(xnfd_unlock); + +int xnfd_ioctl(struct xnsys_ppd *p, int ufd, unsigned request, ...) +{ + void __user *arg; + struct xnfd *fd; + va_list args; + int err; + + va_start(args, request); + arg = va_arg(args, void __user *); + va_end(args); + + trace_mark(xn_rtdm, ioctl, "sys_ppd %p fd %d request %d arg %p", + p, ufd, request, arg); + + fd = xnfd_get(p, ufd, XNFD_MAGIC_ANY); + if (IS_ERR(fd)) + return PTR_ERR(fd); + + if (ipipe_root_p) + err = fd->ops->ioctl_nrt(fd, request, arg); + else + err = fd->ops->ioctl_rt(fd, request, arg); + + if (!XENO_ASSERT(NUCLEUS, !spltest())) + splnone(); + + if (err < 0) { + int ret = __rt_dev_ioctl_fallback(fd, request, arg); + if (ret != -ENOSYS) + err = ret; + } + + xnfd_put(fd); + + return err; +} +EXPORT_SYMBOL_GPL(xnfd_ioctl); + +ssize_t xnfd_read(struct xnsys_ppd *p, int ufd, void __user *buf, size_t size) +{ + struct xnfd *fd; + ssize_t err; + + trace_mark(xn_rtdm, read, "sys_ppd %p fd %d buf %p nbyte %zu", + p, ufd, buf, size); + + fd = xnfd_get(p, ufd, XNFD_MAGIC_ANY); + if (IS_ERR(fd)) + return PTR_ERR(fd); + + if (ipipe_root_p) + err = fd->ops->read_nrt(fd, buf, size); + else + err = fd->ops->read_rt(fd, buf, size); + + if (!XENO_ASSERT(NUCLEUS, !spltest())) + splnone(); + + xnfd_put(fd); + + return err; +} +EXPORT_SYMBOL_GPL(xnfd_read); + +ssize_t xnfd_write(struct xnsys_ppd *p, int ufd, + const void __user *buf, size_t size) +{ + struct xnfd *fd; + ssize_t err; + + trace_mark(xn_rtdm, write, "sys_ppd %p fd %d buf %p nbyte %zu", + p, ufd, buf, size); + + fd = xnfd_get(p, ufd, XNFD_MAGIC_ANY); + if (IS_ERR(fd)) + return PTR_ERR(fd); + + if (ipipe_root_p) + err = fd->ops->write_nrt(fd, buf, size); + else + err = fd->ops->write_rt(fd, buf, size); + + if (!XENO_ASSERT(NUCLEUS, !spltest())) + splnone(); + + xnfd_put(fd); + + return err; +} +EXPORT_SYMBOL_GPL(xnfd_write); + +ssize_t +xnfd_recvmsg(struct xnsys_ppd *p, int ufd, struct msghdr *msg, int flags) +{ + struct xnfd *fd; + ssize_t err; + + trace_mark(xn_rtdm, recvmsg, "sys_ppd %p fd %d msg_name %p " + "msg_namelen %u msg_iov %p msg_iovlen %zu " + "msg_control %p msg_controllen %zu msg_flags %d", + p, ufd, msg->msg_name, msg->msg_namelen, + msg->msg_iov, msg->msg_iovlen, msg->msg_control, + msg->msg_controllen, msg->msg_flags); + + fd = xnfd_get(p, ufd, XNFD_MAGIC_ANY); + if (IS_ERR(fd)) + return PTR_ERR(fd); + + if (ipipe_root_p) + err = fd->ops->recvmsg_nrt(fd, msg, flags); + else + err = fd->ops->recvmsg_rt(fd, msg, flags); + + if (!XENO_ASSERT(NUCLEUS, !spltest())) + splnone(); + + xnfd_put(fd); + + return err; +} +EXPORT_SYMBOL_GPL(xnfd_recvmsg); + +ssize_t +xnfd_sendmsg(struct xnsys_ppd *p, int ufd, const struct msghdr *msg, int flags) +{ + struct xnfd *fd; + ssize_t err; + + trace_mark(xn_rtdm, sendmsg, "sys_ppd %p fd %d msg_name %p " + "msg_namelen %u msg_iov %p msg_iovlen %zu " + "msg_control %p msg_controllen %zu msg_flags %d", + p, ufd, msg->msg_name, msg->msg_namelen, + msg->msg_iov, msg->msg_iovlen, msg->msg_control, + msg->msg_controllen, msg->msg_flags); + + fd = xnfd_get(p, ufd, XNFD_MAGIC_ANY); + if (IS_ERR(fd)) + return PTR_ERR(fd); + + if (ipipe_root_p) + err = fd->ops->sendmsg_nrt(fd, msg, flags); + else + err = fd->ops->sendmsg_rt(fd, msg, flags); + + if (!XENO_ASSERT(NUCLEUS, !spltest())) + splnone(); + + xnfd_put(fd); + + return err; +} +EXPORT_SYMBOL_GPL(xnfd_sendmsg); + +static void +xnfd_close_inner(struct xnsys_ppd *p, struct xnfd_index *idx, spl_t s) +{ + xnid_remove(&p->fds, &idx->id); + xnfd_put_inner(idx->fd, s); + + kfree(idx); +} + +int xnfd_close(struct xnsys_ppd *p, int ufd, unsigned magic) +{ + struct xnfd_index *idx; + struct xnfd *fd; + spl_t s; + + trace_mark(xn_rtdm, close, "sys_ppd %p fd %d", p, ufd); + + xnlock_get_irqsave(&__xnfd_lock, s); + idx = xnfd_index_fetch(p, ufd); + if (idx == NULL) + goto ebadf; + + fd = idx->fd; + if (magic != XNFD_MAGIC_ANY && fd->magic != magic) { + ebadf: + xnlock_put_irqrestore(&__xnfd_lock, s); + return -EBADF; + } + __rt_dev_unref(fd, xnid_id(&idx->id)); + xnfd_close_inner(p, idx, s); + + return 0; +} +EXPORT_SYMBOL_GPL(xnfd_close); + +int xnfd_valid_p(int ufd) +{ + struct xnfd *fd; + spl_t s; + + xnlock_get_irqsave(&__xnfd_lock, s); + fd = xnfd_fetch(xnsys_ppd_get(0), ufd); + xnlock_put_irqrestore(&__xnfd_lock, s); + + return fd != NULL; +} + +int xnfd_select_bind(int ufd, struct xnselector *selector, unsigned type) +{ + struct xnsys_ppd *p; + struct xnfd *fd; + int rc; + + p = xnsys_ppd_get(0); + fd = xnfd_get(p, ufd, XNFD_MAGIC_ANY); + if (IS_ERR(fd)) + return PTR_ERR(fd); + + rc = fd->ops->select_bind(fd, selector, type, ufd); + + xnfd_put(fd); + + return rc; +} + +static void xnfd_destroy(void *cookie, struct xnid *id) +{ + struct xnsys_ppd *p = cookie; + struct xnfd_index *idx; + spl_t s; + + idx = container_of(id, struct xnfd_index, id); + xnlock_get_irqsave(&__xnfd_lock, s); + xnfd_close_inner(p, idx, XNFD_MAGIC_ANY); +} + +void xnfd_cleanup(struct xnsys_ppd *p) +{ + xntree_cleanup(&p->fds, p, xnfd_destroy); +} + +void xnfd_init(void) +{ + sema_init(&xnfd_cleanup_sem, 0); + kthread_run(xnfd_cleanup_thread, NULL, "xnfd"); +} diff --git a/kernel/cobalt/init.c b/kernel/cobalt/init.c index bda790e..2f00aee 100644 --- a/kernel/cobalt/init.c +++ b/kernel/cobalt/init.c @@ -33,6 +33,7 @@ #include <cobalt/kernel/pipe.h> #include <cobalt/kernel/select.h> #include <cobalt/kernel/vdso.h> +#include <cobalt/kernel/fd.h> #include "rtdm/internal.h" #include "posix/internal.h" #include "procfs.h" @@ -399,6 +400,8 @@ static int __init xenomai_init(void) if (ret) goto cleanup_rtdm; + xnfd_init(); + printk(XENO_INFO "Cobalt v%s enabled%s\n", XENO_VERSION_STRING, boot_notice); diff --git a/kernel/cobalt/posix/Makefile b/kernel/cobalt/posix/Makefile index aad94a6..5414110 100644 --- a/kernel/cobalt/posix/Makefile +++ b/kernel/cobalt/posix/Makefile @@ -5,6 +5,7 @@ posix-y := \ cond.o \ cond_attr.o \ event.o \ + fdio.o \ init.o \ monitor.o \ mqueue.o \ diff --git a/kernel/cobalt/posix/fdio.c b/kernel/cobalt/posix/fdio.c new file mode 100644 index 0000000..1f72b7a --- /dev/null +++ b/kernel/cobalt/posix/fdio.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2005 Jan Kiszka <jan.kis...@web.de>. + * Copyright (C) 2005 Joerg Langenberg <joerg.langenb...@gmx.net>. + * Copyright (C) 2013,2014 Gilles Chanteperdrix <g...@xenomai.org>. + * + * This program 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; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/ipipe.h> +#include <cobalt/kernel/fd.h> +#include <cobalt/kernel/ppd.h> +#include "fdio.h" + +int cobalt_ioctl(int fd, unsigned int request, void __user *arg) +{ + return xnfd_ioctl(xnsys_ppd_get(0), fd, request, arg); +} + +ssize_t cobalt_read(int fd, void __user *buf, size_t size) +{ + return xnfd_read(xnsys_ppd_get(0), fd, buf, size); +} + +ssize_t cobalt_write(int fd, const void __user *buf, size_t size) +{ + return xnfd_write(xnsys_ppd_get(0), fd, buf, size); +} + +ssize_t cobalt_recvmsg(int fd, struct msghdr __user *umsg, int flags) +{ + struct msghdr m; + int ret; + + if (__xn_copy_from_user(&m, umsg, sizeof(m))) + return -EFAULT; + + ret = xnfd_recvmsg(xnsys_ppd_get(0), fd, &m, flags); + if (ret < 0) + return ret; + + if (__xn_copy_to_user(umsg, &m, sizeof(*umsg))) + return -EFAULT; + + return ret; +} + +ssize_t cobalt_sendmsg(int fd, struct msghdr __user *umsg, int flags) +{ + struct msghdr m; + + if (__xn_copy_from_user(&m, umsg, sizeof(m))) + return -EFAULT; + + return xnfd_sendmsg(xnsys_ppd_get(0), fd, &m, flags); +} + +int cobalt_close(int fd) +{ + return xnfd_close(xnsys_ppd_get(0), fd, XNFD_MAGIC_ANY); +} diff --git a/kernel/cobalt/posix/fdio.h b/kernel/cobalt/posix/fdio.h new file mode 100644 index 0000000..2947221 --- /dev/null +++ b/kernel/cobalt/posix/fdio.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005 Jan Kiszka <jan.kis...@web.de>. + * Copyright (C) 2005 Joerg Langenberg <joerg.langenb...@gmx.net>. + * Copyright (C) 2013,2014 Gilles Chanteperdrix <g...@xenomai.org>. + * + * This program 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; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef FDIO_H +#define FDIO_H + +int cobalt_ioctl(int fd, unsigned int request, void __user *arg); + +ssize_t cobalt_read(int fd, void __user *buf, size_t size); + +ssize_t cobalt_write(int fd, const void __user *buf, size_t size); + +ssize_t cobalt_recvmsg(int fd, struct msghdr __user *umsg, int flags); + +ssize_t cobalt_sendmsg(int fd, struct msghdr __user *umsg, int flags); + +int cobalt_close(int fd); + +#endif /* FDIO_H */ diff --git a/kernel/cobalt/posix/select.c b/kernel/cobalt/posix/select.c index 6741331..d6d9e43 100644 --- a/kernel/cobalt/posix/select.c +++ b/kernel/cobalt/posix/select.c @@ -22,6 +22,7 @@ #include <linux/types.h> #include <linux/err.h> #include <rtdm/driver.h> +#include <cobalt/kernel/fd.h> #include "internal.h" #include "clock.h" #include "mqueue.h" @@ -35,6 +36,9 @@ static int fd_valid_p(int fd) struct rtdm_dev_context *ctx; struct cobalt_process *cc; + if (xnfd_valid_p(fd)) + return 1; + if (fd >= rtdm_fd_start) { ctx = rtdm_context_get(fd - rtdm_fd_start); if (ctx == NULL) @@ -69,6 +73,11 @@ static int select_bind_one(struct xnselector *selector, unsigned type, int fd) const int rtdm_fd_start = __FD_SETSIZE - RTDM_FD_MAX; struct cobalt_process *cc; cobalt_assoc_t *assoc; + int rc; + + rc = xnfd_select_bind(fd, selector, type); + if (rc != -ENOENT) + return rc; if (fd >= rtdm_fd_start) return rtdm_select_bind(fd - rtdm_fd_start, diff --git a/kernel/cobalt/posix/syscall.c b/kernel/cobalt/posix/syscall.c index 4f4759b..ee5b46e 100644 --- a/kernel/cobalt/posix/syscall.c +++ b/kernel/cobalt/posix/syscall.c @@ -39,6 +39,7 @@ #include "clock.h" #include "event.h" #include "select.h" +#include "fdio.h" int cobalt_muxid; @@ -169,6 +170,12 @@ static struct xnsyscall cobalt_syscalls[] = { SKINCALL_DEF(sc_cobalt_event_sync, cobalt_event_sync, any), SKINCALL_DEF(sc_cobalt_sched_setconfig_np, cobalt_sched_setconfig_np, any), SKINCALL_DEF(sc_cobalt_sched_getconfig_np, cobalt_sched_getconfig_np, any), + SKINCALL_DEF(sc_cobalt_ioctl, cobalt_ioctl, probing), + SKINCALL_DEF(sc_cobalt_read, cobalt_read, probing), + SKINCALL_DEF(sc_cobalt_write, cobalt_write, probing), + SKINCALL_DEF(sc_cobalt_recvmsg, cobalt_recvmsg, probing), + SKINCALL_DEF(sc_cobalt_sendmsg, cobalt_sendmsg, probing), + SKINCALL_DEF(sc_cobalt_close, cobalt_close, lostage), }; struct xnpersonality cobalt_personality = { diff --git a/kernel/cobalt/shadow.c b/kernel/cobalt/shadow.c index cea76bc..0b0ff34 100644 --- a/kernel/cobalt/shadow.c +++ b/kernel/cobalt/shadow.c @@ -58,6 +58,7 @@ #include <cobalt/kernel/ppd.h> #include <cobalt/kernel/vdso.h> #include <cobalt/kernel/thread.h> +#include <cobalt/kernel/fd.h> #include <asm/xenomai/features.h> #include <asm/xenomai/syscall.h> #include <asm-generic/xenomai/mayday.h> @@ -1631,6 +1632,7 @@ static void user_process_detach(void *arg) if (p->exe_path) kfree(p->exe_path); + xnfd_cleanup(p); process_hash_remove(process); xnheap_destroy_mapped(&p->sem_heap, post_ppd_release, NULL); atomic_dec(&personalities[user_muxid]->refcnt); @@ -1678,6 +1680,7 @@ static void *user_process_attach(void) exe_path = NULL; /* Not lethal, but weird. */ } p->exe_path = exe_path; + xntree_init(&p->fds); atomic_set(&p->refcnt, 1); atomic_inc(&personalities[user_muxid]->refcnt); diff --git a/lib/cobalt/rtdm.c b/lib/cobalt/rtdm.c index e789d76..d253bb1 100644 --- a/lib/cobalt/rtdm.c +++ b/lib/cobalt/rtdm.c @@ -26,9 +26,11 @@ #include <sys/socket.h> #include <rtdm/rtdm.h> #include <cobalt/uapi/rtdm/syscall.h> +#include <cobalt/uapi/syscall.h> #include <asm/xenomai/syscall.h> extern int __rtdm_muxid; +extern int __cobalt_muxid; extern int __rtdm_fd_start; static inline int set_errno(int ret) @@ -109,6 +111,13 @@ COBALT_IMPL(int, close, (int fd)) { int ret; + ret = XENOMAI_SKINCALL1(__cobalt_muxid, sc_cobalt_close, fd); + if (ret != -EBADF) { + if (ret == 0) + __STD(close(fd)); + return set_errno(ret); + } + if (fd >= __rtdm_fd_start) { int oldtype; @@ -131,11 +140,17 @@ COBALT_IMPL(int, ioctl, (int fd, unsigned long int request, ...)) { va_list ap; void *arg; + int err; va_start(ap, request); arg = va_arg(ap, void *); va_end(ap); + err = XENOMAI_SKINCALL3(__cobalt_muxid, + sc_cobalt_ioctl, fd, request, arg); + if (err != -EBADF) + return set_errno(err); + if (fd >= __rtdm_fd_start) return set_errno(XENOMAI_SKINCALL3(__rtdm_muxid, sc_rtdm_ioctl, @@ -147,11 +162,19 @@ COBALT_IMPL(int, ioctl, (int fd, unsigned long int request, ...)) COBALT_IMPL(ssize_t, read, (int fd, void *buf, size_t nbyte)) { - if (fd >= __rtdm_fd_start) { - int ret, oldtype; + int ret, oldtype; + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); + ret = XENOMAI_SKINCALL3(__cobalt_muxid, sc_cobalt_read, + fd, buf, nbyte); + + if (ret != -EBADF) { + pthread_setcanceltype(oldtype, NULL); + return set_errno(ret); + } + if (fd >= __rtdm_fd_start) { ret = set_errno(XENOMAI_SKINCALL3(__rtdm_muxid, sc_rtdm_read, fd - __rtdm_fd_start, @@ -160,17 +183,28 @@ COBALT_IMPL(ssize_t, read, (int fd, void *buf, size_t nbyte)) pthread_setcanceltype(oldtype, NULL); return ret; - } else - return __STD(read(fd, buf, nbyte)); + } + + pthread_setcanceltype(oldtype, NULL); + + return __STD(read(fd, buf, nbyte)); } COBALT_IMPL(ssize_t, write, (int fd, const void *buf, size_t nbyte)) { - if (fd >= __rtdm_fd_start) { - int ret, oldtype; + int ret, oldtype; - pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); + ret = XENOMAI_SKINCALL3(__cobalt_muxid, sc_cobalt_write, + fd, buf, nbyte); + + if (ret != -EBADF) { + pthread_setcanceltype(oldtype, NULL); + return set_errno(ret); + } + + if (fd >= __rtdm_fd_start) { ret = set_errno(XENOMAI_SKINCALL3(__rtdm_muxid, sc_rtdm_write, fd - __rtdm_fd_start, @@ -179,8 +213,11 @@ COBALT_IMPL(ssize_t, write, (int fd, const void *buf, size_t nbyte)) pthread_setcanceltype(oldtype, NULL); return ret; - } else - return __STD(write(fd, buf, nbyte)); + } + + pthread_setcanceltype(oldtype, NULL); + + return __STD(write(fd, buf, nbyte)); } COBALT_IMPL(ssize_t, recvmsg, (int fd, struct msghdr * msg, int flags)) _______________________________________________ Xenomai-git mailing list Xenomai-git@xenomai.org http://www.xenomai.org/mailman/listinfo/xenomai-git