/*
 * Copyright (c) 1995 - 2000 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the Kungliga Tekniska
 *      Högskolan and its contributors.
 * 
 * 4. 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.
 */

#define __NO_VERSION__

#include <xfs/xfs_locl.h>
#include <linux/poll.h>

RCSID("$Id: xfs_dev.c,v 1.68.4.6 2000/10/17 12:06:23 lha Exp $");

#include <xfs/xfs_message.h>
#include <xfs/xfs_msg_locl.h>
#include <xfs/xfs_dev.h>
#include <xfs/xfs_fs.h>
#include <xfs/xfs_deb.h>
#include <xfs/nxfs.h>

#ifndef HAVE_WAIT_QUEUE_HEAD_T
typedef struct wait_queue *wait_queue_head_t;
#endif

#ifndef HAVE_INIT_WAITQUEUE_HEAD
static void
init_waitqueue_head(wait_queue_head_t *h)
{
    *h = NULL;
}
#endif

/*
 * Queues of xfs_links hold outbound messages and processes sleeping
 * for replies. The last field is used to return error to sleepers and
 * to keep record of memory to be deallocated when messages have been
 * delivered or dropped.
 */
struct xfs_link {
    struct xfs_link *prev, *next;
    struct xfs_message_header *message;
    u_int error_or_size;	/* error on sleepq and size on messageq */
    u_int woken;
    wait_queue_head_t wait_queue;
};  

struct xfs_channel {
    struct xfs_link messageq;	/* Messages not yet read */
    struct xfs_link sleepq;	/* Waiting for reply message */
    u_int nsequence;
    wait_queue_head_t wait_queue;
    struct xfs_message_header *message_buffer;
    int status;
#define CHANNEL_OPENED	0x1
    struct semaphore channel_sem; /* protects messageq, sleepq, nsequence, inactive_list */
    struct list_head inactive_list;
};

static struct xfs_channel xfs_channel[NXFS];

static void
xfs_initq(struct xfs_link *q)
{
    q->next = q;
    q->prev = q;
    init_waitqueue_head(&(q->wait_queue));
    q->woken = 0;
}

/* Is this queue empty? */
#define xfs_emptyq(q) ((q)->next == (q))

/* Is this link on any queue? Link *must* be inited! */
#define xfs_onq(link) ((link)->next != 0 || (link)->prev != 0)

/* Append q with p */
static 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;
}

static void
xfs_outq(struct xfs_link *p)     
{
    p->next->prev = p->prev;
    p->prev->next = p->next;
    p->next = p->prev = 0;
}

/*
 * Enqueue the `message' of `size' to the `chan' for later processing.
 */

static int
enqueue_message (struct xfs_channel *chan,
		 struct xfs_message_header *message,
		 u_int size)
{
    struct {
	struct xfs_link this_message;
	struct xfs_message_header msg;
    } *t;
    /* Prepare message and copy it later */

    message->size = size;

    t = xfs_alloc(sizeof(t->this_message) + size, XFS_MEM_SENDRPC);
    if (t == NULL)
	return -ENOMEM;
    t->this_message.error_or_size = sizeof(t->this_message) + size;

    memmove(&t->msg, message, size);
    t->this_message.message = &t->msg;

    down(&chan->channel_sem);
    
    t->msg.sequence_num = chan->nsequence++;
    XFSDEB(XDEBMSG, ("xfs_message_send seq = %d\n", t->msg.sequence_num));

    xfs_appendq(&chan->messageq, &t->this_message);
    init_waitqueue_head(&(t->this_message.wait_queue));
    up(&chan->channel_sem);

    return 0;
}

/*
 * add `xn' to the list of inactive nodes.
 */

void
xfs_queue_inactive (struct xfs_node *xn)
{
    struct xfs *xfsp = XFS_FROM_XNODE(xn);
    struct xfs_channel *chan = &xfs_channel[xfsp->fd];

    XFSDEB(XDEBDEV, ("xfs_queue_inactive\n"));

    down(&chan->channel_sem);
    list_add (&xn->inactive_list, &chan->inactive_list);
    up(&chan->channel_sem);
    wake_up(&chan->wait_queue);
}

/*
 * send of inactive messages for all queued nodes on `fd'
 */

static void
xfs_process_inactive_queue (struct xfs_channel *chan)
{
    int i = 0;
    int ret;

    down(&chan->channel_sem);
    for (;;) {
	struct list_head *lh = chan->inactive_list.next;
	struct xfs_node *xn;
	struct xfs_message_inactivenode msg;

	if (lh == &chan->inactive_list)
	    break;

	++i;

	xn = list_entry (lh, struct xfs_node, inactive_list);

	msg.header.opcode = XFS_MSG_INACTIVENODE;
	msg.handle        = xn->handle;
	msg.flag          = XFS_NOREFS | XFS_DELETE;
	
	list_del (&xn->inactive_list);

	up(&chan->channel_sem);
	ret = enqueue_message (chan, &msg.header, sizeof(msg));
	down(&chan->channel_sem);
 	if (ret == 0) {
	    xfs_free(xn, XFS_MEM_XNODE);
	} else {
	    list_add (&xn->inactive_list, &chan->inactive_list);
	    XFSDEB(XDEBDEV, ("xfs_process_inactive_queue: "
			     "failed enqueue-ing msg %d\n", ret));
	}
    }
    up(&chan->channel_sem);
    if (i)
	XFSDEB(XDEBDEV, ("xfs_process_inactive_queue: done (%d)\n", i));
}

/*
 * throw away all inactive nodes on `fd'
 *
 * Need to be called with the chan->channel_sem held
 */

static void
xfs_empty_inactive_queue (struct xfs_channel *chan)
{
    int i = 0;

    for (;;) {
	struct list_head *lh = chan->inactive_list.next;
	struct xfs_node *xn;

	if (lh == &chan->inactive_list)
	    break;

	++i;

	xn = list_entry (lh, struct xfs_node, inactive_list);
	list_del (&xn->inactive_list);
	xfs_free(xn, XFS_MEM_XNODE);
    }
    if (i)
	XFSDEB(XDEBDEV, ("xfs_empty_inactive_queue: done (%d)\n", i));
}

/*
 * Only allow one open.
 */
static int
xfs_devopen(struct inode *inode, struct file *file)
{
    struct xfs_channel *chan;
    struct xfs *xfsp = &xfs[MINOR(inode->i_rdev)];
    int ret = 0;
    
    lock_kernel();

    XFSDEB(XDEBDEV, ("xfs_devopen dev = %d, flags = %d\n",
		     inode->i_rdev, file->f_flags));
    
    if (MINOR(inode->i_rdev) >=NXFS) {
	ret = -ENXIO;
	goto out;
    }

    MOD_INC_USE_COUNT;
    
    chan = &xfs_channel[MINOR(inode->i_rdev)];
    
    /* Only allow one reader/writer */
    if (chan->status & CHANNEL_OPENED) {
	MOD_DEC_USE_COUNT;
	ret = -EBUSY;
	goto out;
    }

    chan->message_buffer = xfs_alloc(MAX_XMSG_SIZE, XFS_MEM_MSGBUF);
    if (chan->message_buffer == NULL) {
	MOD_DEC_USE_COUNT;
	ret = -ENOMEM;
	goto out;
    }
    
    chan->status |= CHANNEL_OPENED;
    xfsp->status |= XFS_DEVOPEN;

    INIT_LIST_HEAD(&chan->inactive_list);

 out:
    unlock_kernel();
    return ret;
}

static int
xfs_devclose(struct inode * inode, struct file * file)
{
    struct xfs_channel *chan = &xfs_channel[MINOR(inode->i_rdev)];
    struct xfs_link *first;

    lock_kernel ();

    XFSDEB(XDEBDEV, ("xfs_devclose dev = %d, flags = %d\n",
		     inode->i_rdev, file->f_flags));
    
    /* Sanity check, paranoia? */
    if (!(chan->status & CHANNEL_OPENED))
	panic("xfs_devclose never opened?");
    
    chan->status &= ~CHANNEL_OPENED;
    xfs[MINOR(inode->i_rdev)].status &= ~XFS_DEVOPEN;
    
    down(&chan->channel_sem);

    /* No one is going to read those messages so empty queue! */
    while (!xfs_emptyq(&chan->messageq)) {
	XFSDEB(XDEBDEV, ("before outq(messageq)\n"));
	first = chan->messageq.next;
	xfs_outq(first);
	if (first->error_or_size != 0)
	    xfs_free(first, XFS_MEM_SENDRPC);
	XFSDEB(XDEBDEV, ("after outq(messageq)\n"));
    }

    /* Wakeup those waiting for replies that will never arrive. */
    while (!xfs_emptyq(&chan->sleepq)) {
	XFSDEB(XDEBDEV, ("before outq(sleepq)\n"));
	first = chan->sleepq.next;
	xfs_outq(first);
	up(&chan->channel_sem);
	first->error_or_size = -ENODEV;
	wake_up(&first->wait_queue);
	first->woken = 1;
	XFSDEB(XDEBDEV, ("after outq(sleepq)\n"));
	down(&chan->channel_sem);
    }
    xfs_empty_inactive_queue (chan);

    up(&chan->channel_sem);
    
    if (chan->message_buffer) {
	xfs_free(chan->message_buffer, XFS_MEM_MSGBUF);
	chan->message_buffer = 0;
    }

    /* Free all xfs_nodes. */
    free_all_xfs_nodes(&xfs[MINOR(inode->i_rdev)]);
    
    MOD_DEC_USE_COUNT;
    unlock_kernel ();
    return 0;
}

/*
 * Move messages from kernel to user space.
 */

static ssize_t
xfs_devread(struct file *file, char *buf, size_t count, loff_t *ppos)
{
    kdev_t dev = file->f_dentry->d_inode->i_rdev;
    struct xfs_channel *chan = &xfs_channel[MINOR(dev)];
    struct xfs_link *first;
    int ret = 0;
    
    lock_kernel ();

    XFSDEB(XDEBDEV, ("xfs_devread: m = %p, m->prev = %p, m->next = %p\n",
		     &chan->messageq, chan->messageq.prev,
		     chan->messageq.next));
    
    xfs_process_inactive_queue (chan);

    down(&chan->channel_sem);
    while (!xfs_emptyq (&chan->messageq)) {
	first = chan->messageq.next;
	XFSDEB(XDEBDEV, ("xfs_devread: first = %p, "
			 "first->prev = %p, first->next = %p\n",
			 first, first->prev, first->next));
	
	if (first->message->size > count)
	    break;

	XFSDEB(XDEBDEV, ("xfs_devread: message->size = %u\n",
			 first->message->size));
	
	copy_to_user (buf, first->message, first->message->size);
	buf += first->message->size;
	count -= first->message->size;
	ret += first->message->size;
	
	xfs_outq(first);
	
	if (first->error_or_size != 0)
	    xfs_free(first, XFS_MEM_SENDRPC);
    }
    up(&chan->channel_sem);
    
    *ppos += ret;
    
    unlock_kernel ();
    return ret;
}

/*
 * Move messages from user space to kernel space,
 * wakeup sleepers, insert new data in VFS.
 */

static ssize_t
xfs_devwrite(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
    kdev_t dev = file->f_dentry->d_inode->i_rdev;
    struct xfs_channel *chan = &xfs_channel[MINOR(dev)];
    char *p;
    struct xfs_message_header *msg_buf;
    int ret = 0;
    
    lock_kernel ();

    XFSDEB(XDEBDEV, ("xfs_devwrite\n"));
    
    if (count > MAX_XMSG_SIZE)
	count = MAX_XMSG_SIZE;
    copy_from_user(chan->message_buffer, buf, count);
    
    /*
     * This thread handles the received message.
     */
    
    for (p = (char *)chan->message_buffer;
	 count > 0;
	 p += msg_buf->size, count -= msg_buf->size) {
	int error;
	msg_buf = (struct xfs_message_header *)p;
	
	error = xfs_message_receive (MINOR(dev),
				     msg_buf,
				     msg_buf->size);
	if (error) {
	    ret = error;
	    break;
	}
	ret += msg_buf->size;
	
	if (msg_buf->size == 0) {
	    XFSDEB(XDEBDEV, ("xfs_devwrite: error: size == 0\n"));
	    return -EINVAL;
	}
    }
    XFSDEB(XDEBDEV, ("xfs_devwrite ret = %d\n", ret));
    
    if (ret >= 0)
	*ppos += ret;
    
    unlock_kernel ();
    return ret;
}

/*
 * Not used.
 */
static int
xfs_devioctl(struct inode *inode, struct file *file,
	     unsigned int cmd, unsigned long arg)
{
    XFSDEB(XDEBDEV, ("xfs_devioctl dev = %d, flags = %d\n",
		     inode->i_rdev, file->f_flags));
    return -EINVAL;
}

static unsigned int
xfs_devpoll(struct file *file, poll_table *wait)
{
    kdev_t dev = file->f_dentry->d_inode->i_rdev;
    struct xfs_channel *chan = &xfs_channel[MINOR(dev)];
    int ret = 0;
    
    lock_kernel ();

    poll_wait(file, &chan->wait_queue, wait);
    
    xfs_process_inactive_queue (chan);

    down(&chan->channel_sem);
    if (!xfs_emptyq(&chan->messageq))
	ret = POLLIN;
    up(&chan->channel_sem);
    unlock_kernel ();
    return ret;
}

/*
 * 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];
    int ret;
    
    XFSDEB(XDEBMSG, ("xfs_message_send opcode = %d\n", message->opcode));
    
    if (!(chan->status & CHANNEL_OPENED))	/* No receiver? */
	return -ENODEV;
    
    xfs_process_inactive_queue (chan);

    ret = enqueue_message (chan, message, size);

    wake_up(&chan->wait_queue);
    return ret;
}

#ifdef HAVE_STRUCT_TASK_STRUCT_PENDING

static inline int
sigisactive(struct task_struct *t, int signal)
{
    return (sigismember(&t->pending.signal, signal) &&
	    !sigismember(&t->blocked, signal));
}

static inline int
sigissignaled(struct task_struct *t, int signal)
{
    return (sigismember(&current->pending.signal, signal));
}

#else

static inline int
sigisactive(struct task_struct *t, int signal)
{
    return (sigismember(&t->signal, signal) &&
	    !sigismember(&t->blocked, signal));
}

static inline int
sigissignaled(struct task_struct *t, int signal)
{
    return (sigismember(&current->signal, signal));
}

#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)
{
    int ret;
    struct xfs_channel *chan = &xfs_channel[fd];
    struct xfs_link *this_message;
    struct xfs_link *this_process;
    struct xfs_message_header *msg;
    
    XFSDEB(XDEBMSG, ("xfs_message_rpc opcode = %d\n", message->opcode));
    
    if (!(chan->status & CHANNEL_OPENED))	/* No receiver? */
	return -ENODEV;
    
    if (size < sizeof(struct xfs_message_wakeup)) {
	printk(KERN_EMERG "XFS Panic: "
	       "Message too small to receive wakeup, opcode = %d\n",
	       message->opcode);
	return -ENOMEM;
    }
    
    xfs_process_inactive_queue(chan);

    this_message = xfs_alloc(sizeof(struct xfs_link), XFS_MEM_SENDRPC);
    if (this_message == NULL)
	return -ENOMEM;
    this_process = xfs_alloc(sizeof(struct xfs_link), XFS_MEM_SENDRPC);
    if (this_process == NULL) {
	xfs_free (this_message, XFS_MEM_SENDRPC);
	return -ENOMEM;
    }
    
    msg = xfs_alloc(size, XFS_MEM_SENDRPC);
    if (msg == NULL) {
	xfs_free (this_message, XFS_MEM_SENDRPC);
	xfs_free (this_process, XFS_MEM_SENDRPC);
	return -ENOMEM;
    }
    memcpy(msg, message, size);
    
    msg->size = size;
    this_message->error_or_size = 0;

    init_waitqueue_head(&(this_message->wait_queue));
    init_waitqueue_head(&(this_process->wait_queue));

    this_message->message = msg;
    this_process->message = msg;

    down(&chan->channel_sem);
    
    msg->sequence_num = chan->nsequence++;
    XFSDEB(XDEBMSG, ("xfs_message_rpc seq = %d\n", msg->sequence_num));
    
    xfs_appendq(&chan->messageq, this_message);
    xfs_appendq(&chan->sleepq, this_process);
    
    this_process->error_or_size = 0;

    up(&chan->channel_sem);
    
    wake_up(&chan->wait_queue);
    
    this_process->woken = 0;
    while (!sigisactive(current, SIGINT) &&
	   !sigisactive(current, SIGTERM) &&
	   !sigisactive(current, SIGSTOP) &&
	   !sigisactive(current, SIGKILL) &&
	   !this_process->woken) {
	interruptible_sleep_on(&this_process->wait_queue);
    }
    
    if (!this_process->woken) {
	int i;

	XFSDEB(XDEBMSG, ("caught signal:"));
	for (i = 1; i <= _NSIG; ++i)
	    if (sigissignaled(current, i))
		XFSDEB(XDEBMSG, ("%d ", i));
	XFSDEB(XDEBMSG, ("\n"));
	this_process->error_or_size = -EINTR;
    }
    
    XFSDEB(XDEBMSG, ("xfs_message_rpc wakeup: seq = %d\n",
		     msg->sequence_num));
    /*
     * Caught signal, got reply message or device was closed.
     * Need to clean up both messageq and sleepq.
     */
    down(&chan->channel_sem);
    if (xfs_onq(this_message)) {
	xfs_outq(this_message);
    }
    if (xfs_onq(this_process)) {
	xfs_outq(this_process);
    }
    up(&chan->channel_sem);
    ret = this_process->error_or_size;
    XFSDEB(XDEBMSG, ("xfs_message_rpc opcode this_process"
		     "->error_or_size = %d\n", this_process->error_or_size));
    XFSDEB(XDEBMSG, ("xfs_message_rpc opcode "
		     "((xfs_message_wakeup*)"
		     "(this_process->message))->error = %d\n",
		     ((struct xfs_message_wakeup*)
		      (this_process->message))->error ));
    
    memcpy(message, msg, size);
    
    xfs_free (this_message, XFS_MEM_SENDRPC);
    xfs_free (this_process, XFS_MEM_SENDRPC);
    xfs_free (msg, XFS_MEM_SENDRPC);

    if (ret < -1000 || ret > 0) {
	printk(KERN_EMERG
	       "XFS Panic: xfs_message_rpc was about to return error %d\n",
	       ret);
	return -EINVAL;
    }

    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)
{
    XFSDEB(XDEBMSG, ("xfs_message_receive opcode = %d\n", message->opcode));
    
    /* Dispatch and coerce message type */
    switch (message->opcode) {
    case XFS_MSG_WAKEUP:
	return xfs_message_wakeup(fd,
				  (struct xfs_message_wakeup *)
				  message,
				  message->size);
    case XFS_MSG_WAKEUP_DATA:
	return xfs_message_wakeup_data(fd,
				       (struct xfs_message_wakeup_data *)
				       message,
				       message->size);
    case XFS_MSG_INSTALLROOT:
	return xfs_message_installroot(fd,
				       (struct xfs_message_installroot *)
				       message,
				       message->size);
    case XFS_MSG_INSTALLNODE:
	return xfs_message_installnode(fd,
				       (struct xfs_message_installnode *)
				       message, 
				       message->size);
    case XFS_MSG_INSTALLATTR:
	return xfs_message_installattr(fd,
				       (struct xfs_message_installattr *)
				       message, 
				       message->size);
    case XFS_MSG_INSTALLDATA:
	return xfs_message_installdata(fd,
				       (struct xfs_message_installdata *)
				       message, 
				       message->size);
    case XFS_MSG_INVALIDNODE:
	return xfs_message_invalidnode(fd,
				       (struct xfs_message_invalidnode *)
				       message, 
				       message->size);
    case XFS_MSG_UPDATEFID:
	return xfs_message_updatefid(fd,
				     (struct xfs_message_updatefid *)
				     message,
				     message->size);
    case XFS_MSG_GC_NODES:
	return xfs_message_gc_nodes(fd,
				    (struct xfs_message_gc_nodes *)
				    message,
				    message->size);
    case XFS_MSG_VERSION:
	return xfs_message_version(fd,
				   (struct xfs_message_version *)
				   message,
				   message->size);
    default:
	printk(KERN_EMERG 
	       "XFS Panic: 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)
{
    struct xfs_channel *chan = &xfs_channel[fd];
    struct xfs_link *sleepq = &chan->sleepq;
    struct xfs_link *t = chan->sleepq.next; /* Really first in q */
    
    XFSDEB(XDEBMSG, ("xfs_message_wakeup error: %d seq = %d\n",
		     message->error, message->sleepers_sequence_num));
    
    down(&chan->channel_sem);
    for (; t != sleepq; t = t->next)
	if (t->message->sequence_num == message->sleepers_sequence_num) {
	    if (t->message->size < size) {
		printk(KERN_EMERG 
		       "XFS Panic: Could not wakeup requestor with "
		       "opcode = %d properly, too small receive buffer.\n",
		       t->message->opcode);
		t->error_or_size = -ENOMEM;
	    } else {
		memmove(t->message, message, size);
		if (message->error < 0 || message->error > 1000)
		    t->error_or_size = -EPROTO;
		else
		    t->error_or_size = -message->error;
	    }
	    wake_up(&t->wait_queue);
	    t->woken = 1;
	    break;
	}
    up(&chan->channel_sem);
    
    return 0;
}

int
xfs_message_wakeup_data(int fd, struct xfs_message_wakeup_data *message,
			u_int size)
{
    struct xfs_channel *chan = &xfs_channel[fd];
    struct xfs_link *sleepq = &chan->sleepq;
    struct xfs_link *t = chan->sleepq.next; /* Really first in q */
    
    XFSDEB(XDEBMSG, ("xfs_message_wakeup_data error: %d seq = %d\n",
		     message->error, message->sleepers_sequence_num));
    
    down(&chan->channel_sem);
    for (; t != sleepq; t = t->next)
	if (t->message->sequence_num == message->sleepers_sequence_num) {
	    if (t->message->size < size) {
		printk(KERN_EMERG 
		       "XFS Panic: Could not wakeup requestor with "
		       "opcode = %d properly, too small receive buffer.\n",
		       t->message->opcode);
		t->error_or_size = -ENOMEM;
	    } else {
		memmove(t->message, message, size);
		if (message->error < 0 || message->error > 1000)
		    t->error_or_size = -EPROTO;
		else
		    t->error_or_size = -message->error;
	    }
	    wake_up(&t->wait_queue);
	    t->woken = 1;
	    break;
	}
    up(&chan->channel_sem);

    return 0;
}

struct file_operations xfs_fops = {
    read:		xfs_devread,
    write:		xfs_devwrite,
    poll:		xfs_devpoll,
    ioctl:		xfs_devioctl,
    release:		xfs_devclose,
    open:		xfs_devopen,
};

int
xfs_init_device(void)
{
    int i;
    
    for (i = 0; i < NXFS; i++) {
	XFSDEB(XDEBDEV, ("before initq(messageq and sleepq)\n"));
	init_waitqueue_head(&(xfs_channel[i].wait_queue));
	xfs_initq(&xfs_channel[i].messageq);
	xfs_initq(&xfs_channel[i].sleepq);
#ifdef HAVE_INIT_MUTEX	
	init_MUTEX(&(xfs_channel[i].channel_sem));
#else
	xfs_channel[i].channel_sem = MUTEX;
#endif
    }
    return 0;
}

void
xfs_print_sleep_queue(void)
{
    int i;

    for (i = 0; i < NXFS; ++i) {
	struct xfs_channel *chan = &xfs_channel[i];
	struct xfs_link *sleepq = &chan->sleepq;
	struct xfs_link *t = chan->sleepq.next; /* Really first in q */

	printk("Sleeping queue %d :", i);
	for (; t != sleepq; t = t->next) {
	    printk(" %d", t->message->sequence_num);

#ifdef HAVE_WAIT_QUEUE_TASK_LIST
	    if (!list_empty(&t->wait_queue.task_list)) {
		const wait_queue_t *w = (const wait_queue_t *)
		    list_entry(t->wait_queue.task_list.next,
			       wait_queue_t, task_list);

		printk(" (pid %d)", w->task->pid);
	    }
#else /* !HAVE_WAIT_QUEUE_TASK_LIST */
	    if (t->wait_queue && t->wait_queue->task)
		printk(" (pid %d)", t->wait_queue->task->pid);
#endif /* !HAVE_WAIT_QUEUE_TASK_LIST */
	}
    }
}
