/* * Copyright (c) 1995 - 2003 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include RCSID("$arla: xfs_dev-common.c,v 1.61 2003/07/15 16:25:42 lha Exp $"); struct xfs_channel xfs_channel[NNNPFS]; void xfs_initq(struct xfs_link *q) { q->next = q; q->prev = q; } /* Is this queue empty? */ int xfs_emptyq(const struct xfs_link *q) { return q->next == q; } /* Is this link on any queue? Link *must* be inited! */ int xfs_onq(const struct xfs_link *link) { return link->next != NULL || link->prev != NULL; } /* Append q with p */ void xfs_appendq(struct xfs_link *q, struct xfs_link *p) { p->next = q; p->prev = q->prev; p->prev->next = p; q->prev = p; } /* remove `p' from its queue */ void xfs_outq(struct xfs_link *p) { p->next->prev = p->prev; p->prev->next = p->next; p->next = p->prev = NULL; } /* * Only allow one open. */ int xfs_devopen_common(dev_t dev) { struct xfs_channel *chan; if (minor(dev) < 0 || minor(dev) >= NNNPFS) return ENXIO; chan = &xfs_channel[minor(dev)]; /* Only allow one reader/writer */ if (chan->status & CHANNEL_OPENED) { NNPFSDEB(XDEBDEV, ("xfs_devopen: already open\n")); return EBUSY; } else { chan->status |= CHANNEL_OPENED; } chan->message_buffer = xfs_alloc(MAX_XMSG_SIZE, M_NNPFS_MSG); /* initialize the queues if they have not been initialized before */ xfs_initq(&chan->sleepq); xfs_initq(&chan->messageq); return 0; } #if defined(HAVE_TWO_ARGUMENT_VFS_BUSY) #define xfs_vfs_busy(mp, flags, lock, proc) vfs_busy((mp), (flags)) #define xfs_vfs_unbusy(mp, proc) vfs_unbusy((mp)) #elif defined(HAVE_THREE_ARGUMENT_VFS_BUSY) #define xfs_vfs_busy(mp, flags, lock, proc) vfs_busy((mp), (flags), (lock)) #define xfs_vfs_unbusy(mp, proc) vfs_unbusy((mp)) #elif defined(HAVE_FOUR_ARGUMENT_VFS_BUSY) #define xfs_vfs_busy(mp, flags, lock, proc) vfs_busy((mp), (flags), (lock), (proc)) #define xfs_vfs_unbusy(mp, proc) vfs_unbusy((mp), (proc)) #elif defined(__osf__) #define xfs_vfs_busy(mp, flags, lock, proc) (0) #define xfs_vfs_unbusy(mp, proc) (0) #else #define xfs_vfs_busy(mp, flags, lock, proc) vfs_busy((mp)) #define xfs_vfs_unbusy(mp, proc) vfs_unbusy((mp)) #endif /* * Wakeup all sleepers and cleanup. */ int xfs_devclose_common(dev_t dev, d_thread_t *proc) { struct xfs_channel *chan = &xfs_channel[minor(dev)]; struct xfs_link *first; /* Sanity check, paranoia? */ if (!(chan->status & CHANNEL_OPENED)) panic("xfs_devclose never opened?"); chan->status &= ~CHANNEL_OPENED; /* No one is going to read those messages so empty queue! */ while (!xfs_emptyq(&chan->messageq)) { NNPFSDEB(XDEBDEV, ("before outq(messageq)\n")); first = chan->messageq.next; xfs_outq(first); if (first->error_or_size != 0) xfs_free(first, first->error_or_size, M_NNPFS_LINK); NNPFSDEB(XDEBDEV, ("after outq(messageq)\n")); } /* Wakeup those waiting for replies that will never arrive. */ while (!xfs_emptyq(&chan->sleepq)) { NNPFSDEB(XDEBDEV, ("before outq(sleepq)\n")); first = chan->sleepq.next; xfs_outq(first); first->error_or_size = ENODEV; wakeup((caddr_t) first); NNPFSDEB(XDEBDEV, ("after outq(sleepq)\n")); } if (chan->status & CHANNEL_WAITING) wakeup((caddr_t) chan); if (chan->message_buffer) { xfs_free(chan->message_buffer, MAX_XMSG_SIZE, M_NNPFS_MSG); chan->message_buffer = NULL; } /* * Free all xfs nodes. */ if (xfs[minor(dev)].mp != NULL) { if (xfs_vfs_busy(xfs[minor(dev)].mp, VB_READ|VB_WAIT, NULL, proc)) { NNPFSDEB(XDEBNODE, ("xfs_dev_close: vfs_busy() --> BUSY\n")); return EBUSY; } free_all_xfs_nodes(&xfs[minor(dev)], FORCECLOSE, 0); xfs_vfs_unbusy(xfs[minor(dev)].mp, proc); } return 0; } #ifdef NNPFS_DEBUG /* * debugging glue for CURSIG */ static long xfs_cursig (d_thread_t *p) { #if defined(__osf__) thread_t th = current_thread(); struct np_uthread *npu = thread_to_np_uthread(th); return CURSIG(p,npu); #elif defined(HAVE_FREEBSD_THREAD) #ifndef CURSIG return 0; /* XXX we would like to use sig_ffs, but that isn't * exported */ #else return CURSIG(p->td_proc); #endif #else #if defined(__NetBSD__) && __NetBSD_Version__ >= 106130000 return 0; /* XXX CURSIG operates on a struct lwp */ #else return CURSIG(p); #endif #endif } #endif /* * Move messages from kernel to user space. */ int xfs_devread(dev_t dev, struct uio * uiop, int ioflag) { struct xfs_channel *chan = &xfs_channel[minor(dev)]; struct xfs_link *first; int error = 0; #ifdef NNPFS_DEBUG char devname[64]; #endif NNPFSDEB(XDEBDEV, ("xfs_devread dev = %s\n", xfs_devtoname_r(dev, devname, sizeof(devname)))); NNPFSDEB(XDEBDEV, ("xfs_devread: m = %lx, m->prev = %lx, m->next = %lx\n", (unsigned long)&chan->messageq, (unsigned long)chan->messageq.prev, (unsigned long)chan->messageq.next)); #ifdef HAVE_FREEBSD_THREAD chan->proc = xfs_uio_to_thread(uiop); #else chan->proc = xfs_uio_to_proc(uiop); #endif again: if (!xfs_emptyq (&chan->messageq)) { while (!xfs_emptyq (&chan->messageq)) { /* Remove message */ first = chan->messageq.next; NNPFSDEB(XDEBDEV, ("xfs_devread: first = %lx, " "first->prev = %lx, first->next = %lx\n", (unsigned long)first, (unsigned long)first->prev, (unsigned long)first->next)); NNPFSDEB(XDEBDEV, ("xfs_devread: message->size = %u\n", first->message->size)); if (first->message->size > uiop->uio_resid) break; error = uiomove((caddr_t) first->message, first->message->size, uiop); if (error) break; xfs_outq(first); if (first->error_or_size != 0) xfs_free(first, first->error_or_size, M_NNPFS_LINK); } } else { chan->status |= CHANNEL_WAITING; if (tsleep((caddr_t) chan, (PZERO + 1) | PCATCH, "xfsread", 0)) { #ifdef HAVE_FREEBSD_THREAD NNPFSDEB(XDEBMSG, ("caught signal xfs_devread: %ld\n", xfs_cursig(xfs_uio_to_thread(uiop)))); #else NNPFSDEB(XDEBMSG, ("caught signal xfs_devread: %ld\n", xfs_cursig(xfs_uio_to_proc(uiop)))); #endif error = EINTR; } else if ((chan->status & CHANNEL_WAITING) == 0) { goto again; } else error = EIO; } NNPFSDEB(XDEBDEV, ("xfs_devread done error = %d\n", error)); return error; } /* * Move messages from user space to kernel space, * wakeup sleepers, insert new data in VFS. */ int xfs_devwrite(dev_t dev, struct uio *uiop, int ioflag) { struct xfs_channel *chan = &xfs_channel[minor(dev)]; char *p; int error; u_int cnt; struct xfs_message_header *msg_buf; #ifdef NNPFS_DEBUG char devname[64]; #endif NNPFSDEB(XDEBDEV, ("xfs_devwrite dev = %s\n", xfs_devtoname_r (dev, devname, sizeof(devname)))); #ifdef HAVE_FREEBSD_THREAD chan->proc = xfs_uio_to_thread(uiop); #else chan->proc = xfs_uio_to_proc(uiop); #endif cnt = uiop->uio_resid; error = uiomove((caddr_t) chan->message_buffer, MAX_XMSG_SIZE, uiop); if (error != 0) return error; cnt -= uiop->uio_resid; /* * This thread handles the received message. */ for (p = (char *)chan->message_buffer; cnt > 0; p += msg_buf->size, cnt -= msg_buf->size) { #ifdef HAVE_FREEBSD_THREAD d_thread_t *pp = xfs_uio_to_thread(uiop); #else d_thread_t *pp = xfs_uio_to_proc(uiop); #endif msg_buf = (struct xfs_message_header *)p; error = xfs_message_receive (minor(dev), msg_buf, msg_buf->size, pp); } NNPFSDEB(XDEBDEV, ("xfs_devwrite error = %d\n", error)); return error; } /* * Send a message to user space. */ int xfs_message_send(int fd, struct xfs_message_header * message, u_int size) { struct xfs_channel *chan = &xfs_channel[fd]; struct { struct xfs_link this_message; struct xfs_message_header msg; } *t; NNPFSDEB(XDEBMSG, ("xfs_message_send opcode = %d\n", message->opcode)); if (!(chan->status & CHANNEL_OPENED)) /* No receiver? */ return ENODEV; /* Prepare message and copy it later */ message->size = size; message->sequence_num = chan->nsequence++; t = xfs_alloc(sizeof(t->this_message) + size, M_NNPFS); t->this_message.error_or_size = sizeof(t->this_message) + size; bcopy(message, &t->msg, size); t->this_message.message = &t->msg; xfs_appendq(&chan->messageq, &t->this_message); if (chan->status & CHANNEL_WAITING) { chan->status &= ~CHANNEL_WAITING; wakeup((caddr_t) chan); } xfs_select_wakeup(chan); return 0; } #if defined(SWEXIT) #define NNPFS_P_EXIT SWEXIT #elif defined(P_WEXIT) #define NNPFS_P_EXIT P_WEXIT #else #error what is your exit named ? #endif #if defined(HAVE_STRUCT_PROC_P_SIGMASK) || defined(HAVE_STRUCT_PROC_P_SIGCTX) || defined(HAVE_STRUCT_PROC_P_SIGWAITMASK) || defined(__osf__) || defined(HAVE_FREEBSD_THREAD) static void xfs_block_sigset (sigset_t *sigset) { #if defined(__sigaddset) #define xfs_sig_block(ss,signo) __sigaddset((ss), (signo)) #elif defined(SIGADDSET) #define xfs_sig_block(ss,signo) SIGADDSET(*(ss), (signo)) #else #define xfs_sig_block(ss,signo) *(ss) |= sigmask(signo) #endif xfs_sig_block(sigset, SIGIO); xfs_sig_block(sigset, SIGALRM); xfs_sig_block(sigset, SIGVTALRM); xfs_sig_block(sigset, SIGCHLD); #ifdef SIGINFO xfs_sig_block(sigset, SIGINFO); #endif #undef xfs_sig_block } #endif /* * Send a message to user space and wait for reply. */ int xfs_message_rpc(int fd, struct xfs_message_header * message, u_int size, d_thread_t *proc) { int ret; struct xfs_channel *chan = &xfs_channel[fd]; struct xfs_link *this_message; struct xfs_link *this_process; struct xfs_message_header *msg; #if defined(HAVE_STRUCT_PROC_P_SIGMASK) || defined(HAVE_STRUCT_PROC_P_SIGCTX) || defined(__osf__) || defined(HAVE_FREEBSD_THREAD) sigset_t oldsigmask; #endif int catch; NNPFSDEB(XDEBMSG, ("xfs_message_rpc opcode = %d\n", message->opcode)); if (proc == NULL) { #ifdef HAVE_FREEBSD_THREAD proc = xfs_curthread(); #else proc = xfs_curproc(); #endif } if (!(chan->status & CHANNEL_OPENED)) /* No receiver? */ return ENODEV; #ifdef HAVE_FREEBSD_THREAD if (chan->proc != NULL && chan->proc->td_proc != NULL && proc->td_proc->p_pid == chan->proc->td_proc->p_pid) { printf("xfs_message_rpc: deadlock avoided " "pid = %u == %u\n", proc->td_proc->p_pid, chan->proc->td_proc->p_pid); #else if (chan->proc != NULL && proc->p_pid == chan->proc->p_pid) { printf("xfs_message_rpc: deadlock avoided " "pid = %u == %u\n", proc->p_pid, chan->proc->p_pid); #endif #if 0 psignal (proc, SIGABRT); #endif return EDEADLK; } if (size < sizeof(struct xfs_message_wakeup)) { printf("NNPFS PANIC Error: Message to small to receive wakeup, opcode = %d\n", message->opcode); return ENOMEM; } this_message = xfs_alloc(sizeof(struct xfs_link), M_NNPFS_LINK); this_process = xfs_alloc(sizeof(struct xfs_link), M_NNPFS_LINK); msg = xfs_alloc(size, M_NNPFS_MSG); bcopy(message, msg, size); msg->size = size; msg->sequence_num = chan->nsequence++; this_message->error_or_size = 0; this_message->message = msg; this_process->message = msg; xfs_appendq(&chan->messageq, this_message); xfs_appendq(&chan->sleepq, this_process); xfs_select_wakeup(chan); this_process->error_or_size = 0; if (chan->status & CHANNEL_WAITING) { chan->status &= ~CHANNEL_WAITING; wakeup((caddr_t) chan); } /* * Remove signals from the sigmask so no IO will wake us up from * tsleep(). We don't want to wake up from since program (emacs, * bash & co can't handle them. */ #ifdef HAVE_FREEBSD_THREAD /* FreeBSD 5.1 */ oldsigmask = proc->td_sigmask; xfs_block_sigset (&proc->td_sigmask); #elif HAVE_STRUCT_PROC_P_SIGMASK /* NetBSD 1.5, Darwin 1.3, FreeBSD 4.3, 5.0, OpenBSD 2.8 */ oldsigmask = proc->p_sigmask; xfs_block_sigset (&proc->p_sigmask); #elif defined(HAVE_STRUCT_PROC_P_SIGCTX) /* NetBSD 1.6 */ oldsigmask = proc->p_sigctx.ps_sigmask; xfs_block_sigset (&proc->p_sigctx.ps_sigmask); #elif defined(HAVE_STRUCT_PROC_P_SIGWAITMASK) /* OSF 4.0 */ oldsigmask = proc->p_sigwaitmask; xfs_block_sigset (&proc->p_sigwaitmask); #elif defined(__osf__) /* OSF 5.0 */ oldsigmask = u.u_sigmask; xfs_block_sigset (&u.u_sigmask); #endif /* * if we are exiting we should not try to catch signals, since * there might not be enough context left in the process to handle * signal delivery, and besides, most BSD-variants ignore all * signals while closing anyway. */ catch = 0; #ifdef HAVE_FREEBSD_THREAD if (!(proc->td_proc->p_flag & NNPFS_P_EXIT)) #else if (!(proc->p_flag & NNPFS_P_EXIT)) #endif catch |= PCATCH; /* * We have to check if we have a receiver here too because the * daemon could have terminated before we sleep. This seems to * happen sometimes when rebooting. */ if (!(chan->status & CHANNEL_OPENED)) { NNPFSDEB(XDEBMSG, ("xfs_message_rpc: channel went away\n")); this_process->error_or_size = EINTR; } else if ((ret = tsleep((caddr_t) this_process, (PZERO + 1) | catch, "xfs", 0)) != 0) { NNPFSDEB(XDEBMSG, ("caught signal (%d): %ld\n", ret, xfs_cursig(proc))); this_process->error_or_size = EINTR; } #ifdef HAVE_FREEBSD_THREAD proc->td_sigmask = oldsigmask; #elif HAVE_STRUCT_PROC_P_SIGMASK proc->p_sigmask = oldsigmask; #elif defined(HAVE_STRUCT_PROC_P_SIGCTX) proc->p_sigctx.ps_sigmask = oldsigmask; #elif defined(HAVE_STRUCT_PROC_P_SIGWAITMASK) proc->p_sigwaitmask = oldsigmask; #elif defined(__osf__) u.u_sigmask = oldsigmask; #endif /* * Caught signal, got reply message or device was closed. * Need to clean up both messageq and sleepq. */ if (xfs_onq(this_message)) { xfs_outq(this_message); } if (xfs_onq(this_process)) { xfs_outq(this_process); } ret = this_process->error_or_size; NNPFSDEB(XDEBMSG, ("xfs_message_rpc this_process->error_or_size = %d\n", this_process->error_or_size)); NNPFSDEB(XDEBMSG, ("xfs_message_rpc opcode ((xfs_message_wakeup*)(this_process->message))->error = %d\n", ((struct xfs_message_wakeup *) (this_process->message))->error)); bcopy(msg, message, size); xfs_free(this_message, sizeof(*this_message), M_NNPFS_LINK); xfs_free(this_process, sizeof(*this_process), M_NNPFS_LINK); xfs_free(msg, size, M_NNPFS_MSG); return ret; } /* * For each message type there is a message handler * that implements its action, xfs_message_receive * invokes the correct function. */ int xfs_message_receive(int fd, struct xfs_message_header *message, u_int size, d_thread_t *p) { NNPFSDEB(XDEBMSG, ("xfs_message_receive opcode = %d\n", message->opcode)); /* Dispatch and coerce message type */ switch (message->opcode) { case NNPFS_MSG_WAKEUP: return xfs_message_wakeup(fd, (struct xfs_message_wakeup *) message, message->size, p); case NNPFS_MSG_WAKEUP_DATA: return xfs_message_wakeup_data(fd, (struct xfs_message_wakeup_data *) message, message->size, p); case NNPFS_MSG_INSTALLROOT: return xfs_message_installroot(fd, (struct xfs_message_installroot *) message, message->size, p); case NNPFS_MSG_INSTALLNODE: return xfs_message_installnode(fd, (struct xfs_message_installnode *) message, message->size, p); case NNPFS_MSG_INSTALLATTR: return xfs_message_installattr(fd, (struct xfs_message_installattr *) message, message->size, p); case NNPFS_MSG_INSTALLDATA: return xfs_message_installdata(fd, (struct xfs_message_installdata *) message, message->size, p); case NNPFS_MSG_INVALIDNODE: return xfs_message_invalidnode(fd, (struct xfs_message_invalidnode *) message, message->size, p); case NNPFS_MSG_UPDATEFID: return xfs_message_updatefid(fd, (struct xfs_message_updatefid *)message, message->size, p); case NNPFS_MSG_GC_NODES: return xfs_message_gc_nodes(fd, (struct xfs_message_gc_nodes *)message, message->size, p); case NNPFS_MSG_VERSION: return xfs_message_version(fd, (struct xfs_message_version *)message, message->size, p); default: printf("NNPFS PANIC Warning xfs_dev: Unknown message opcode == %d\n", message->opcode); return EINVAL; } } int xfs_message_wakeup(int fd, struct xfs_message_wakeup *message, u_int size, d_thread_t *p) { struct xfs_channel *chan = &xfs_channel[fd]; struct xfs_link *sleepq = &chan->sleepq; struct xfs_link *t = chan->sleepq.next; /* Really first in q */ NNPFSDEB(XDEBMSG, ("xfs_message_wakeup error: %d\n", message->error)); for (; t != sleepq; t = t->next) if (t->message->sequence_num == message->sleepers_sequence_num) { if (t->message->size < size) { printf("NNPFS PANIC Error: Could not wakeup requestor with opcode = %d properly, to small receive buffer.\n", t->message->opcode); t->error_or_size = ENOMEM; } else bcopy(message, t->message, size); wakeup((caddr_t) t); break; } return 0; } int xfs_message_wakeup_data(int fd, struct xfs_message_wakeup_data * message, u_int size, d_thread_t *p) { struct xfs_channel *chan = &xfs_channel[fd]; struct xfs_link *sleepq = &chan->sleepq; struct xfs_link *t = chan->sleepq.next; /* Really first in q */ NNPFSDEB(XDEBMSG, ("xfs_message_wakeup_data error: %d\n", message->error)); for (; t != sleepq; t = t->next) if (t->message->sequence_num == message->sleepers_sequence_num) { if (t->message->size < size) { printf("NNPFS PANIC Error: Could not wakeup requestor with opcode = %d properly, to small receive buffer.\n", t->message->opcode); t->error_or_size = ENOMEM; } else bcopy(message, t->message, size); wakeup((caddr_t) t); break; } return 0; } /* * */ int xfs_uprintf_device(void) { #if 0 int i; for (i = 0; i < NNNPFS; i++) { uprintf("xfs_channel[%d] = {\n", i); uprintf("messageq.next = %lx ", xfs_channel[i].messageq.next); uprintf("messageq.prev = %lx ", xfs_channel[i].messageq.prev); uprintf("sleepq.next = %lx ", xfs_channel[i].sleepq.next); uprintf("sleepq.prev = %lx ", xfs_channel[i].sleepq.prev); uprintf("nsequence = %d status = %d\n", xfs_channel[i].nsequence, xfs_channel[i].status); uprintf("}\n"); } #endif return 0; }