view cp/nucleus/io.c @ 618:535aec703236

cp: define a FIXME macro that leaves a sclp message There are far too many fixmes in the code. Sadly, the compiler simply discards them. This usually isn't an issue until one accidentally hits a "weird" bug which just turns out to be an unhandled (but documented) case in another part of the code. Using a macro instead of a comment will let the compiler string-ify the text, and then at runtime use SCLP to print it out. This will immediatelly point at problem areas. So, keep an eye on SCLP from now on :) Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Tue, 13 Dec 2011 22:20:50 -0500
parents 9bbe38598c2f
children
line wrap: on
line source

/*
 * (C) Copyright 2007-2010  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
 *
 * This file is released under the GPLv2.  See the COPYING file for more
 * details.
 */

#include <channel.h>
#include <io.h>
#include <interrupt.h>
#include <device.h>
#include <sched.h>
#include <atomic.h>
#include <spinlock.h>
#include <buddy.h>
#include <sched.h>

/*
 * Helper function to make sure the io_op has everything set right
 */
static int __verify_io_op(struct io_op *ioop)
{
	FIXME("check everything that makes sense to check");
	return 0;
}

static void __reset_reserved_fields(struct io_op *ioop)
{
	ioop->orb.__zero1 = 0;

	ioop->orb.__reserved1 = 0;
	ioop->orb.__reserved2 = 0;
	ioop->orb.__reserved3 = 0;
	ioop->orb.__reserved4 = 0;
	ioop->orb.__reserved5 = 0;
	ioop->orb.__reserved6 = 0;
}

/* NOTE: assumes dev->q_lock is held */
static void __submit_io(struct device *dev)
{
	struct io_op *ioop;
	int err;

	if (dev->q_cur)
		return;

	if (list_empty(&dev->q_out))
		return;

	ioop = list_entry(dev->q_out.next, struct io_op, list);

	err = start_sch(dev->sch, &ioop->orb);
	if (!err) {
		list_del(&ioop->list);
		dev->q_cur = ioop;
	} else
		ioop->err = err;
}

/*
 * Submit an I/O request to a subchannel, and set up everything needed to
 * handle the operation
 */
int submit_io(struct device *dev, struct io_op *ioop, int flags)
{
	static atomic_t op_id_counter;
	unsigned long intmask;
	int err = -EBUSY;

	err = __verify_io_op(ioop);
	if (err)
		return 0;

	/* make sure all reserved fields have the right values */
	__reset_reserved_fields(ioop);

	ioop->err = 0;
	ioop->orb.param = atomic_inc_return(&op_id_counter);
	atomic_set(&ioop->done, 0);

	/* add it to the list of ops */
	spin_lock_intsave(&dev->q_lock, &intmask);
	list_add_tail(&ioop->list, &dev->q_out);

	__submit_io(dev); /* try to submit an IO right now */
	spin_unlock_intrestore(&dev->q_lock, intmask);

	if (flags & CAN_LOOP) {
		while(!atomic_read(&ioop->done))
			;
	} else if (flags & CAN_SLEEP) {
		while(!atomic_read(&ioop->done))
			schedule();
	}

	return 0;
}

/*
 * Initialize the channel I/O subsystem
 */
void init_io(void)
{
	u64 cr6;

	cr6 = get_cr(6);

	/* enable all I/O interrupt classes */
	cr6 |= BIT64(32);
	cr6 |= BIT64(33);
	cr6 |= BIT64(34);
	cr6 |= BIT64(35);
	cr6 |= BIT64(36);
	cr6 |= BIT64(37);
	cr6 |= BIT64(38);
	cr6 |= BIT64(39);

	set_cr(6, cr6);
}

static int default_io_handler(struct device *dev, struct io_op *ioop, struct irb *irb)
{
	ioop->err = -EAGAIN;

	/* Unit check? */
	if (irb->scsw.dev_status & 0x02) {
		FIXME("we should bail");
		ioop->err = -EUCHECK;
	}

	/* Device End is set, we're done */
	if (irb->scsw.dev_status & 0x04)
		ioop->err = 0;

	return 0;
}

static void __cpu_initiated_io(struct device *dev, struct io_op *ioop, struct irb *irb)
{
	unsigned long intmask;

	ioop->err = test_sch(dev->sch, irb);

	if (!ioop->err && ioop->handler)
		ioop->handler(dev, ioop, irb);
	else if (!ioop->err)
		default_io_handler(dev, ioop, irb);

	/*
	 * We can do this, because the test_sch function sets ->err, and
	 * therefore regardless of ->handler being defined, ->err will have
	 * a reasonable value
	 */
	if (ioop->err == -EAGAIN)
		return; /* leave handler registered */

	/* ...and remove it form the list */
	spin_lock_intsave(&dev->q_lock, &intmask);
	dev->q_cur = NULL;

	__submit_io(dev); /* try to submit another IO */
	spin_unlock_intrestore(&dev->q_lock, intmask);

	/* flag io_op as done... */
	atomic_set(&ioop->done, 1);

	/* call the destructor if there is one */
	if (ioop->dtor)
		ioop->dtor(dev, ioop);
}

static void __dev_initiated_io(struct device *dev, struct irb *irb)
{
}

/*
 * I/O Interrupt handler (C portion)
 */
void __io_int_handler(void)
{
	unsigned long intmask;
	struct io_op *ioop;
	struct device *dev;
	struct irb irb;

	dev = find_device_by_sch(IO_INT_CODE->ssid);
	BUG_ON(IS_ERR(dev));

	spin_lock_intsave(&dev->q_lock, &intmask);
	ioop = dev->q_cur;
	spin_unlock_intrestore(&dev->q_lock, intmask);

	if (ioop && ioop->orb.param == IO_INT_CODE->param &&
	    dev->sch == IO_INT_CODE->ssid) {
		/*
		 * CPU-initiated operation
		 */

		__cpu_initiated_io(dev, ioop, &irb);
		dev_put(dev);
		return;
	}

	/*
	 * device-initiated operation
	 */
	BUG_ON(test_sch(dev->sch, &irb));

	atomic_inc(&dev->attention);

	if (dev->dev->interrupt)
		dev->dev->interrupt(dev, &irb);
	else
		__dev_initiated_io(dev, &irb);
	dev_put(dev);
}