r3297 - in trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian: . patches patches/series

Sven Luther luther@costa.debian.org
Fri, 03 Jun 2005 15:44:14 +0000


Author: luther
Date: 2005-06-03 15:44:13 +0000 (Fri, 03 Jun 2005)
New Revision: 3297

Added:
   trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/powerpc-ppc64-ibmvscsi.patch
Modified:
   trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/changelog
   trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/series/2.6.11-6
Log:
Added IBM eServer i/pSeries Virtual SCSI Target Driver patch.


Modified: trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/changelog
===================================================================
--- trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/changelog	2005-06-03 03:50:17 UTC (rev 3296)
+++ trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/changelog	2005-06-03 15:44:13 UTC (rev 3297)
@@ -19,8 +19,12 @@
   * Fixed FTBFS when using a powerpc biarch compiler, and specifying a command
     line CC override, which killed the -m64 arcgument needed for ppc64.
     (Sven Luther)
+  * Added patch for IBM eServer i/pSeries Virtual SCSI Target Driver from 
+    Dave Boutcher <boutcher@us.ibm.com>, needed for ppc64 boxes with virtual
+    partitions, Thanks to Cajus Pollmeier for providing it.
+    (Sven Luther)
 
- -- Sven Luther <luther@debian.org>  Fri, 27 May 2005 15:48:45 +0200
+ -- Sven Luther <luther@debian.org>  Fri,  3 Jun 2005 17:41:12 +0200
 
 kernel-source-2.6.11 (2.6.11-5) unstable; urgency=low
 

Added: trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/powerpc-ppc64-ibmvscsi.patch
===================================================================
--- trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/powerpc-ppc64-ibmvscsi.patch	2005-06-03 03:50:17 UTC (rev 3296)
+++ trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/powerpc-ppc64-ibmvscsi.patch	2005-06-03 15:44:13 UTC (rev 3297)
@@ -0,0 +1,2868 @@
+#! /bin/sh -e 
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Description: Enables IBM eServer i/pSeries Virtual SCSI Target Driver 
+## DP: Description: Needed for i/pSeries with logical partitions (LPAR).
+## DP: Patch author: Dave Boutcher (boutcher@us.ibm.com)
+## DP: Upstream status: unknown, sent to me by Cajus Pollmeier.
+
+diff -Naur linux-2.6.11/drivers/scsi/ibmvscsi/ibmvscsis.c linux-2.6.11.patched/drivers/scsi/ibmvscsi/ibmvscsis.c
+--- linux-2.6.11/drivers/scsi/ibmvscsi/ibmvscsis.c	1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.11.patched/drivers/scsi/ibmvscsi/ibmvscsis.c	2005-03-09 23:13:51.000000000 +0100
+@@ -0,0 +1,2818 @@
++/**************************************************************************/
++/* -*- -linux- -*-                                                        */
++/* IBM eServer i/pSeries Virtual SCSI Target Driver                       */
++/* Copyright (C) 2003 Dave Boutcher (boutcher@us.ibm.com) IBM Corp.       */
++/*                                                                        */
++/*  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  */
++/*                                                                        */
++/* This module contains the eServer virtual SCSI target code.  The driver */
++/* takes SRP requests from the virtual SCSI client (the linux version is  */
++/* int ibmvscsi.c, but there can be other clients, like AIX or OF) and    */
++/* passes them on to real devices in this system.                         */
++/*                                                                        */
++/* The basic hierarchy (and somewhat the organization of this file) is    */
++/* that SCSI CDBs are in SRPs are in CRQs.                                */
++/*                                                                        */
++/**************************************************************************/
++/*
++  TODO:
++  - Support redirecting SRP SCSI requests to a real SCSI driver
++*/
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/interrupt.h>
++#include <linux/list.h>
++#include <linux/pagemap.h>
++#include <linux/dma-mapping.h>
++#include <linux/sched.h>
++#include <linux/blkdev.h>
++#include <linux/fs.h>
++#include <linux/bio.h>
++
++#include <asm/hvcall.h>
++#include <asm/vio.h>
++#include <asm/iommu.h>
++
++#include "../scsi.h"
++#include "viosrp.h"
++
++#define IBMVSCSIS_VERSION "1.2"
++
++MODULE_DESCRIPTION("IBM Virtual SCSI Target");
++MODULE_AUTHOR("Dave Boutcher");
++MODULE_LICENSE("GPL");
++MODULE_VERSION(IBMVSCSIS_VERSION);
++
++static int ibmvscsis_debug = 0;
++
++/* These are fixed and come from the device tree...we
++ * just store them here to save getting them every time.
++ */
++static char system_id[64] = "";
++static char partition_name[97] = "UNKNOWN";
++static unsigned int partition_number = -1;
++
++/*
++ * Quick macro to enable/disable interrupts
++ * TODO: move to vio.h to be common with ibmvscsi.c
++ */
++#define h_vio_signal(ua, mode) \
++  plpar_hcall_norets(H_VIO_SIGNAL, ua, mode)
++
++/* 
++ * These are indexes into the following table, and have to match!!!
++ */
++#define SENSE_SUCCESS       0
++#define SENSE_ABORT         1
++#define SENSE_INVALID_ID    2
++#define SENSE_DEVICE_FAULT  3
++#define SENSE_DEVICE_BUSY   4
++#define SENSE_UNIT_OFFLINE  5
++#define SENSE_INVALID_CMD   6
++#define SENSE_INTERMEDIATE  7
++#define SENSE_WRITE_PROT    8
++#define SENSE_INVALID_FIELD 9
++
++#define TARGET_MAX_NAME_LEN 128
++
++static unsigned char ibmvscsis_sense_data[][3] = {
++/*
++ * Sense key lookup table
++ * Format: SenseKey,AdditionalSenseCode,AdditionalSenseCodeQualifier
++ * Adapted from 3w-xxxx.h
++ */
++	{0x00, 0x00, 0x00},	/* Success           */
++	{0x0b, 0x00, 0x00},	/* Aborted command   */
++	{0x0b, 0x14, 0x00},	/* ID not found      */
++	{0x04, 0x00, 0x00},	/* Device fault      */
++	{0x0b, 0x00, 0x00},	/* Device busy       */
++	{0x02, 0x04, 0x00},	/* Unit offline      */
++	{0x05, 0x20, 0x00},	/* Invalid Command   */
++	{0x10, 0x00, 0x00},	/* Intermediate      */
++	{0x07, 0x27, 0x00},	/* Write Protected   */
++	{0x05, 0x24, 0x00},	/* Invalid field     */
++};
++
++/*
++ * SCSI defined structure for inquiry data
++ * TODO: Seral number is currently messed up if you do
++ *       scsiinfo.  I'm not sure why and I think it comes out of 
++ *       here
++ */
++struct inquiry_data {
++	u8 qual_type;
++	u8 rmb_reserve;
++	u8 version;
++	u8 aerc_naca_hisup_format;
++	u8 addl_len;
++	u8 sccs_reserved;
++	u8 bque_encserv_vs_multip_mchngr_reserved;
++	u8 reladr_reserved_linked_cmdqueue_vs;
++	char vendor[8];
++	char product[16];
++	char revision[4];
++	char vendor_specific[20];
++	char reserved1[2];
++	char version_descriptor[16];
++	char reserved2[22];
++	char unique[158];
++};
++
++extern int vio_num_address_cells;
++
++/* 
++ * an RPA command/response transport queue.  This is our structure
++ * that points to the actual queue.  feel free to modify this structure
++ * as needed
++ */
++struct crq_queue {
++	struct viosrp_crq *msgs;
++	int size, cur;
++	dma_addr_t msg_token;
++	spinlock_t lock;
++};
++
++/*
++ * This structure tracks our fundamental unit of work.  Whenever
++ * an SRP Information Unit (IU) arrives, we track all the good stuff
++ * here
++ */
++struct iu_entry {
++	union viosrp_iu *iu;
++	struct server_adapter *adapter;
++	struct list_head next;
++	dma_addr_t iu_token;
++	int aborted;
++	struct {
++		dma_addr_t remote_token;
++		char *data_buffer;
++		dma_addr_t data_token;
++		long data_len;
++		struct vdev *vd;
++		char in_use:1;
++		char diunder:1;
++		char diover:1;
++		char dounder:1;
++		char doover:1;
++		char write:1;
++		char linked:1;
++		int data_out_residual_count;
++		int data_in_residual_count;
++		int ioerr;
++	} req;
++};
++
++/* 
++ * a pool of ius for use 
++ */
++struct iu_pool {
++	spinlock_t lock;
++	struct list_head iu_entries;
++	struct iu_entry *list;
++	union viosrp_iu *iu_storage;
++	dma_addr_t iu_token;
++	u32 size;
++};
++
++/*
++ * Represents a single device that someone told us about
++ * that we treat as a LUN
++ */
++struct vdev {
++	struct list_head list;
++	char type;		/* 'B' for block, 'S' for SCSI */
++	atomic_t refcount;
++	int disabled;
++	u64 lun;
++	struct kobject kobj;
++	struct {
++		char device_name[TARGET_MAX_NAME_LEN];
++		struct block_device *bdev;
++		long blksize;
++		long lastlba;
++		int ro;
++	} b;
++};
++
++/*
++ * Represents a bus.  target #'s in SCSI are 6 bits long,
++ * so you can have 64 targets per bus
++ */
++#define TARGETS_PER_BUS (64)
++#define BUS_PER_ADAPTER (8)
++struct vbus {
++	struct vdev *vdev[TARGETS_PER_BUS];
++	atomic_t num_targets;
++	struct kobject kobj;
++	int bus_num;
++};
++
++/*
++ * Buffer cache
++ */
++struct dma_buffer {
++	dma_addr_t token;
++	char *addr;
++	size_t len;
++};
++#define DMA_BUFFER_CACHE_SIZE (16)
++#define DMA_BUFFER_INIT_COUNT (4)
++#define DMA_BUFFER_INIT_LEN (PAGE_SIZE*16)
++
++/* all driver data associated with a host adapter */
++struct server_adapter {
++	struct device *dev;
++	struct vio_dev *dma_dev;
++	struct crq_queue queue;
++	struct work_struct crq_task;
++	struct tasklet_struct endio_tasklet;
++	struct iu_pool pool;
++	spinlock_t lock;
++	struct bio *bio_done;
++	struct bio *bio_donetail;
++	struct list_head inflight;
++	struct vbus *vbus[8];
++	int nvdevs;
++	char name[32];
++	unsigned long liobn;
++	unsigned long riobn;
++
++	atomic_t num_buses;
++	struct kobject stats_kobj;
++
++	/* This ugly expression allocates a bit array of 
++	 * in-use flags large enough for the number of buffers
++	 */
++	unsigned long dma_buffer_use[(DMA_BUFFER_CACHE_SIZE +
++				      sizeof(unsigned long) - 1)
++				     / sizeof(unsigned long)];
++	struct dma_buffer dma_buffer[DMA_BUFFER_CACHE_SIZE];
++
++	/* Statistics only */
++	atomic_t iu_count;	/* number allocated */
++	atomic_t bio_count;	/* number allocated */
++	atomic_t crq_processed;
++	atomic_t interrupts;
++	atomic_t read_processed;
++	atomic_t write_processed;
++	atomic_t buffers_allocated;
++	atomic_t errors;
++};
++
++/* 
++ * Forward declarations
++ */
++static long send_rsp(struct iu_entry *iue, int status);
++
++/*
++ * The following are lifted from usb.h
++ */
++#define DEBUG 1
++#ifdef DEBUG
++#define dbg(format, arg...) if (ibmvscsis_debug) printk(KERN_WARNING __FILE__ ": " format , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) printk(KERN_ERR "ibmvscsis: " format , ## arg)
++#define info(format, arg...) printk(KERN_INFO "ibmvscsis: " format  , ## arg)
++#define warn(format, arg...) printk(KERN_WARNING "ibmvscsis: " format , ## arg)
++
++/* ==============================================================
++ * Utility Routines
++ * ==============================================================
++ */
++/*
++ * return an 8 byte lun given a bus, target, lun.  
++ * Today this only supports single level luns.  Should we add a level or a
++ * 64 bit LUN as input to support multi-level luns?
++ */
++u64 make_lun(unsigned int bus, unsigned int target, unsigned int lun)
++{
++	u16 result = (0x8000 |
++		      ((target & 0x003f) << 8) |
++		      ((bus & 0x0007) << 5) | (lun & 0x001f));
++	return ((u64) result) << 48;
++}
++
++/*
++ * Given an 8 byte LUN, return the first level bus/target/lun.
++ * Today this doesn't support multi-level LUNs
++ */
++#define GETBUS(x) ((int)((((u64)(x)) >> 53) & 0x0007))
++#define GETTARGET(x) ((int)((((u64)(x)) >> 56) & 0x003f))
++#define GETLUN(x) ((int)((((u64)(x)) >> 48) & 0x001f))
++
++static u8 getcontrolbyte(u8 * cdb)
++{
++	return cdb[COMMAND_SIZE(cdb[0]) - 1];
++}
++
++static u8 getlink(struct iu_entry *iue)
++{
++	return (getcontrolbyte(iue->iu->srp.cmd.cdb) & 0x01);
++}
++
++/*
++ * Given an SRP, figure out the data in length
++ */
++static int did_len(struct srp_cmd *cmd)
++{
++	struct memory_descriptor *md;
++	struct indirect_descriptor *id;
++	int offset = cmd->additional_cdb_len * 4;
++
++	switch (cmd->data_out_format) {
++	case SRP_NO_BUFFER:
++		offset += 0;
++		break;
++	case SRP_DIRECT_BUFFER:
++		offset += sizeof(struct memory_descriptor);
++		break;
++	case SRP_INDIRECT_BUFFER:
++		offset += sizeof(struct indirect_descriptor)
++		    +
++		    ((cmd->data_out_count -
++		      1) * sizeof(struct memory_descriptor));
++		break;
++	default:
++		err("client error. Invalid data_out_format %d\n",
++		    cmd->data_out_format);
++		return 0;
++	}
++
++	switch (cmd->data_in_format) {
++	case SRP_NO_BUFFER:
++		return 0;
++	case SRP_DIRECT_BUFFER:
++		md = (struct memory_descriptor *)(cmd->additional_data +
++						  offset);
++		return md->length;
++	case SRP_INDIRECT_BUFFER:
++		id = (struct indirect_descriptor *)(cmd->additional_data +
++						    offset);
++		return id->total_length;
++	default:
++		err("client error.  Invalid data_in_format %d\n",
++		    cmd->data_in_format);
++		return 0;
++	}
++}
++
++/* 
++ * We keep a pool of IUs, this routine builds the pool.  The pool is 
++ * per-adapter.  The size of the pool is negotiated as part of the SRP
++ * login, where we negotiate the number of requests (IUs) the client
++ * can send us.  This routine is not synchronized.
++ */
++static int initialize_iu_pool(struct server_adapter *adapter, int size)
++{
++	struct iu_pool *pool = &adapter->pool;
++	int i;
++
++	pool->size = size;
++	pool->lock = SPIN_LOCK_UNLOCKED;
++	INIT_LIST_HEAD(&pool->iu_entries);
++
++	pool->list = kmalloc(pool->size * sizeof(*pool->list), GFP_KERNEL);
++	if (!pool->list) {
++		err("Error: Cannot allocate memory for IU list\n");
++		return -ENOMEM;
++	}
++	memset(pool->list, 0x00, pool->size * sizeof(*pool->list));
++
++	pool->iu_storage =
++	    dma_alloc_coherent(adapter->dev,
++			       pool->size * sizeof(*pool->iu_storage),
++			       &pool->iu_token, 0);
++	if (!pool->iu_storage) {
++		err("Error: Cannot allocate memory for IU pool\n");
++		kfree(pool->list);
++		return -ENOMEM;
++	}
++
++	for (i = 0; i < pool->size; ++i) {
++		pool->list[i].iu = pool->iu_storage + i;
++		pool->list[i].iu_token =
++		    pool->iu_token + sizeof(*pool->iu_storage) * i;
++		pool->list[i].adapter = adapter;
++		list_add_tail(&pool->list[i].next, &pool->iu_entries);
++	}
++
++	return 0;
++}
++
++/*
++ * Free the pool we allocated in initialize_iu_pool
++ */
++static void release_iu_pool(struct server_adapter *adapter)
++{
++	struct iu_pool *pool = &adapter->pool;
++	int i, in_use = 0;
++	for (i = 0; i < pool->size; ++i)
++		if (pool->list[i].req.in_use)
++			++in_use;
++	if (in_use)
++		err("Releasing event pool with %d events still in use?\n", 
++		    in_use);
++	kfree(pool->list);
++	dma_free_coherent(adapter->dev, pool->size * sizeof(*pool->iu_storage),
++			  pool->iu_storage, pool->iu_token);
++}
++
++/*
++ * Get an IU from the pool.  Return NULL of the pool is empty.  This
++ * routine is syncronized by a lock.  The routine sets all the important
++ * fields to 0
++ */
++static struct iu_entry *get_iu(struct server_adapter *adapter)
++{
++	struct iu_entry *e;
++	unsigned long flags;
++
++	spin_lock_irqsave(&adapter->pool.lock, flags);
++	if (!list_empty(&adapter->pool.iu_entries)) {
++		e = list_entry(adapter->pool.iu_entries.next, struct iu_entry,
++			       next);
++		list_del(adapter->pool.iu_entries.next);
++
++		if (e->req.in_use) {
++			err("Found in-use iue in free pool!");
++		}
++
++		memset(&e->req, 0x00, sizeof(e->req));
++
++		e->req.in_use = 1;
++	} else {
++		e = NULL;
++	}
++
++	spin_unlock_irqrestore(&adapter->pool.lock, flags);
++	atomic_inc(&adapter->iu_count);
++	return e;
++}
++
++/* 
++ * Return an IU to the pool.  This routine is synchronized
++ */
++static void free_iu(struct iu_entry *iue)
++{
++	unsigned long flags;
++	if (iue->req.vd) {
++		atomic_dec(&iue->req.vd->refcount);
++	}
++
++	spin_lock_irqsave(&iue->adapter->pool.lock, flags);
++	if (iue->req.in_use == 0) {
++		warn("Internal error, freeing iue twice!\n");
++	} else {
++		iue->req.in_use = 0;
++		list_add_tail(&iue->next, &iue->adapter->pool.iu_entries);
++	}
++	spin_unlock_irqrestore(&iue->adapter->pool.lock, flags);
++	atomic_dec(&iue->adapter->iu_count);
++}
++
++/*
++ * Get a CRQ from the inter-partition queue.
++ */
++static struct viosrp_crq *crq_queue_next_crq(struct crq_queue *queue)
++{
++	struct viosrp_crq *crq;
++	unsigned long flags;
++
++	spin_lock_irqsave(&queue->lock, flags);
++	crq = &queue->msgs[queue->cur];
++	if (crq->valid & 0x80) {
++		if (++queue->cur == queue->size)
++			queue->cur = 0;
++	} else
++		crq = NULL;
++	spin_unlock_irqrestore(&queue->lock, flags);
++
++	return crq;
++}
++
++/* 
++ * Make the RDMA hypervisor call.  There should be a better way to do this
++ * than inline assembler.
++ * TODO: Fix the inline assembler
++ */
++static long h_copy_rdma(long length,
++			unsigned long sliobn, unsigned long slioba,
++			unsigned long dliobn, unsigned long dlioba)
++{
++	long lpar_rc = 0;
++	__asm__ __volatile__(" li 3,0x110 \n\t"
++			     " mr 4, %1 \n\t"
++			     " mr 5, %2 \n\t"
++			     " mr 6, %3 \n\t"
++			     " mr 7, %4 \n\t"
++			     " mr 8, %5 \n\t"
++			     " .long 0x44000022 \n\t"
++			     " mr %0, 3 \n\t":"=&r"(lpar_rc)
++			     :"r"(length), "r"(sliobn), "r"(slioba),
++			     "r"(dliobn), "r"(dlioba)
++			     :"r0", "r3", "r4", "r5", "r6", "r7", "r8", "cr0",
++			     "cr1", "ctr", "xer", "memory");
++	return lpar_rc;
++}
++
++/*
++ * Send an SRP to another partition using the CRQ.
++ */
++static int send_srp(struct iu_entry *iue, u64 length)
++{
++	long rc, rc1;
++	union {
++		struct viosrp_crq cooked;
++		u64 raw[2];
++	} crq;
++
++	/* First copy the SRP */
++	rc = h_copy_rdma(length,
++			 iue->adapter->liobn,
++			 iue->iu_token,
++			 iue->adapter->riobn, iue->req.remote_token);
++
++	if (rc) {
++		err("Error %ld transferring data to client\n", rc);
++	}
++
++	crq.cooked.valid = 0x80;
++	crq.cooked.format = VIOSRP_SRP_FORMAT;
++	crq.cooked.reserved = 0x00;
++	crq.cooked.timeout = 0x00;
++	crq.cooked.IU_length = length;
++	crq.cooked.IU_data_ptr = iue->iu->srp.generic.tag;
++
++	if (rc == 0) {
++		crq.cooked.status = 0x99;	/* TODO: is this right? */
++	} else {
++		crq.cooked.status = 0x00;
++	}
++
++	rc1 =
++	    plpar_hcall_norets(H_SEND_CRQ, iue->adapter->dma_dev->unit_address,
++			       crq.raw[0], crq.raw[1]);
++
++	if (rc1) {
++		err("Error %ld sending response to client\n", rc1);
++		return rc1;
++	}
++
++	return rc;
++}
++
++/*
++ * Send data to a single SRP memory descriptor
++ * Returns amount of data sent, or negative value on error
++ */
++static long send_md_data(dma_addr_t stoken, int len,
++			 struct memory_descriptor *md,
++			 struct server_adapter *adapter)
++{
++	int tosend;
++	long rc;
++
++	if (len < md->length)
++		tosend = len;
++	else
++		tosend = md->length;
++
++	rc = h_copy_rdma(tosend,
++			 adapter->liobn,
++			 stoken, adapter->riobn, md->virtual_address);
++
++	if (rc != H_Success) {
++		err(" Error %ld transferring data to client\n", rc);
++		return -1;
++	}
++
++	return tosend;
++}
++
++/*
++ * Send data to the SRP data_in buffers
++ * Returns amount of data sent, or negative value on error
++ */
++static long send_cmd_data(dma_addr_t stoken, int len, struct iu_entry *iue)
++{
++	struct srp_cmd *cmd = &iue->iu->srp.cmd;
++	struct memory_descriptor *md;
++	struct indirect_descriptor *id;
++	int offset = 0;
++	int total_length = 0;
++	int i;
++	int thislen;
++	int bytes;
++	int sentlen = 0;
++
++	offset = cmd->additional_cdb_len * 4;
++
++	switch (cmd->data_out_format) {
++	case SRP_NO_BUFFER:
++		offset += 0;
++		break;
++	case SRP_DIRECT_BUFFER:
++		offset += sizeof(struct memory_descriptor);
++		break;
++	case SRP_INDIRECT_BUFFER:
++		offset += sizeof(struct indirect_descriptor)
++		    +
++		    ((cmd->data_out_count -
++		      1) * sizeof(struct memory_descriptor));
++		break;
++	default:
++		err("client error: Invalid data_out_format %d\n",
++		    cmd->data_out_format);
++		return 0;
++	}
++
++	switch (cmd->data_in_format) {
++	case SRP_NO_BUFFER:
++		return 0;
++	case SRP_DIRECT_BUFFER:
++		md = (struct memory_descriptor *)(cmd->additional_data +
++						  offset);
++		sentlen = send_md_data(stoken, len, md, iue->adapter);
++		len -= sentlen;
++		if (len) {
++			iue->req.diover = 1;
++			iue->req.data_in_residual_count = len;
++		}
++		return sentlen;
++	}
++
++	if (cmd->data_in_format != SRP_INDIRECT_BUFFER) {
++		err("client error Invalid data_in_format %d\n",
++		    cmd->data_in_format);
++		return 0;
++	}
++
++	id = (struct indirect_descriptor *)(cmd->additional_data + offset);
++
++	total_length = id->total_length;
++
++	/* Work through the partial memory descriptor list */
++	for (i = 0; ((i < cmd->data_in_count) && (len)); i++) {
++		if (len > id->list[i].length) {
++			thislen = id->list[i].length;
++		} else {
++			thislen = len;
++		}
++
++		bytes =
++		    send_md_data(stoken + sentlen, thislen, id->list + i,
++				 iue->adapter);
++		if (bytes < 0)
++			return bytes;
++
++		if (bytes != thislen) {
++			warn("Error: Tried to send %d, sent %d\n", thislen,
++			     bytes);
++		}
++
++		sentlen += bytes;
++		total_length -= bytes;
++		len -= bytes;
++	}
++
++	if (len) {
++		iue->req.diover = 1;
++		iue->req.data_in_residual_count = len;
++	}
++
++	return sentlen;
++}
++
++/*
++ * Get data from the other partition from a single SRP memory descriptor
++ * Returns amount of data sent, or negative value on error
++ */
++static long get_md_data(dma_addr_t ttoken, int len,
++			struct memory_descriptor *md,
++			struct server_adapter *adapter)
++{
++	int toget;
++	long rc;
++
++	if (len < md->length)
++		toget = len;
++	else
++		toget = md->length;
++
++	rc = h_copy_rdma(toget,
++			 adapter->riobn,
++			 md->virtual_address, adapter->liobn, ttoken);
++
++	if (rc != H_Success) {
++		err("Error %ld transferring data to client\n", rc);
++		return -1;
++	}
++
++	return toget;
++}
++
++/*
++ * Get data from an SRP data in area.
++ * Returns amount of data sent, or negative value on error
++ */
++static long get_cmd_data(dma_addr_t stoken, int len, struct iu_entry *iue)
++{
++	struct srp_cmd *cmd = &iue->iu->srp.cmd;
++	struct memory_descriptor *md;
++	struct indirect_descriptor *id;
++	int offset = 0;
++	int total_length = 0;
++	int i;
++	int thislen;
++	int bytes;
++	int sentlen = 0;
++
++	offset = cmd->additional_cdb_len * 4;
++
++	switch (cmd->data_out_format) {
++	case SRP_NO_BUFFER:
++		return 0;
++		break;
++	case SRP_DIRECT_BUFFER:
++		md = (struct memory_descriptor *)(cmd->additional_data +
++						  offset);
++		return get_md_data(stoken, len, md, iue->adapter);
++		break;
++	}
++
++	if (cmd->data_out_format != SRP_INDIRECT_BUFFER) {
++		err("client error: Invalid data_out_format %d\n",
++		    cmd->data_out_format);
++		return 0;
++	}
++
++	id = (struct indirect_descriptor *)(cmd->additional_data + offset);
++
++	total_length = id->total_length;
++
++	/* Work through the partial memory descriptor list */
++	for (i = 0; ((i < cmd->data_out_count) && (len)); i++) {
++		if (len > id->list[i].length) {
++			thislen = id->list[i].length;
++		} else {
++			thislen = len;
++		}
++
++		bytes =
++		    get_md_data(stoken + sentlen, thislen, id->list + i,
++				iue->adapter);
++		if (bytes < 0)
++			return bytes;
++
++		if (bytes != thislen) {
++			err("Partial data sent to client (%d/%d)\n", bytes, thislen);
++		}
++
++		sentlen += bytes;
++		total_length -= bytes;
++		len -= bytes;
++	}
++
++	return sentlen;
++}
++
++/*
++ * Get some data buffers to start.  This doesn't lock the adapter structure!
++ */
++static void init_data_buffer(struct server_adapter *adapter)
++{
++	int i;
++
++	for (i = 0; i < DMA_BUFFER_INIT_COUNT; i++) {
++		if (adapter->dma_buffer[i].addr == NULL) {
++			adapter->dma_buffer[i].addr = (char *)
++			    dma_alloc_coherent(adapter->dev,
++					       DMA_BUFFER_INIT_LEN,
++					       &adapter->dma_buffer[i].token,
++					       0);
++			adapter->dma_buffer[i].len = DMA_BUFFER_INIT_LEN;
++			dbg("data buf %p token %8.8x, len %ld\n",
++			    adapter->dma_buffer[i].addr,
++			    adapter->dma_buffer[i].token,
++			    adapter->dma_buffer[i].len);
++			atomic_inc(&adapter->buffers_allocated);
++		}
++	}
++
++	return;
++}
++
++/*
++ * Get a memory buffer that includes a mapped TCE.  
++ */
++static void get_data_buffer(char **buffer, dma_addr_t * data_token, size_t len,
++			    struct server_adapter *adapter)
++{
++	int i;
++
++	for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) {
++		if ((adapter->dma_buffer[i].addr) &&
++		    (adapter->dma_buffer[i].len >= len) &&
++		    (!test_and_set_bit(i, adapter->dma_buffer_use))) {
++			*buffer = adapter->dma_buffer[i].addr;
++			*data_token = adapter->dma_buffer[i].token;
++			return;
++		}
++	}
++
++	/* Couldn't get a buffer!  Try and get a new one */
++	*buffer = (char *)dma_alloc_coherent(adapter->dev, len, data_token, 0);
++	atomic_inc(&adapter->buffers_allocated);
++	dbg("get:  %p, %8.8x, %ld\n", *buffer, *data_token, len);
++	return;
++}
++
++/*
++ * Free a memory buffer that includes a mapped TCE.  
++ */
++static void free_data_buffer(char *buffer, dma_addr_t data_token, size_t len,
++			     struct server_adapter *adapter)
++{
++	int i;
++
++	/* First see if this buffer is already in the cache */
++	for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) {
++		if (adapter->dma_buffer[i].addr == buffer) {
++			if (adapter->dma_buffer[i].token != data_token) {
++				err("Inconsistent data buffer pool info!\n");
++			}
++			if (!test_and_clear_bit(i, adapter->dma_buffer_use)) {
++				err("Freeing data buffer twice!\n");
++			}
++			return;
++		}
++	}
++
++	/* See if there is an empty slot in our list */
++	for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) {
++		if (!test_and_set_bit(i, adapter->dma_buffer_use)) {
++			if (adapter->dma_buffer[i].addr == NULL) {
++				adapter->dma_buffer[i].addr = buffer;
++				adapter->dma_buffer[i].token = data_token;
++				adapter->dma_buffer[i].len = len;
++				clear_bit(i, adapter->dma_buffer_use);
++				return;
++			} else {
++				clear_bit(i, adapter->dma_buffer_use);
++			}
++		}
++	}
++
++	/* Now see if there is a smaller buffer we should throw out */
++	for (i = 0; i < DMA_BUFFER_CACHE_SIZE; i++) {
++		if (!test_and_set_bit(i, adapter->dma_buffer_use)) {
++			if (adapter->dma_buffer[i].len < len) {
++				dbg("fre1: %p, %8.8x, %ld\n",
++				    adapter->dma_buffer[i].addr,
++				    adapter->dma_buffer[i].token,
++				    adapter->dma_buffer[i].len);
++
++				dma_free_coherent(adapter->dev,
++						  adapter->dma_buffer[i].len,
++						  adapter->dma_buffer[i].addr,
++						  adapter->dma_buffer[i].token);
++
++				atomic_dec(&adapter->buffers_allocated);
++
++				adapter->dma_buffer[i].addr = buffer;
++				adapter->dma_buffer[i].token = data_token;
++				adapter->dma_buffer[i].len = len;
++				clear_bit(i, adapter->dma_buffer_use);
++				return;
++			} else {
++				clear_bit(i, adapter->dma_buffer_use);
++			}
++		}
++	}
++
++	/* No space to cache this.  Give it back to the kernel */
++	dbg("fre2: %p, %8.8x, %ld\n", buffer, data_token, len);
++	dma_free_coherent(adapter->dev, len, buffer, data_token);
++	atomic_dec(&adapter->buffers_allocated);
++}
++
++/*
++ * Release all the data buffers
++ */
++static void release_data_buffer(struct server_adapter *adapter)
++{
++	int i;
++	int free_in_use = 0;
++
++	for (i = 0; i < DMA_BUFFER_INIT_COUNT; i++) {
++		if (adapter->dma_buffer[i].addr != NULL) {
++			if (test_bit(i, adapter->dma_buffer_use)) {
++				free_in_use++;
++			}
++			dma_free_coherent(adapter->dev,
++					  adapter->dma_buffer[i].len,
++					  adapter->dma_buffer[i].addr,
++					  adapter->dma_buffer[i].token);
++
++			atomic_dec(&adapter->buffers_allocated);
++		}
++	}
++
++	if (free_in_use) {
++		err("Freeing %d in-use data buffers\n", free_in_use);
++	}
++	return;
++}
++
++/*
++ * the routine that gets called on end_io of our bios.  We basically
++ * schedule the processing to be done in our task, since we don't want
++ * do things like RDMA in someone else's interrupt handler
++ *
++ * Each iu request may result in multiple bio requests.  only proceed
++ * when all the bio requests have done.
++ */
++static int ibmvscsis_end_io(struct bio *bio, unsigned int nbytes, int error)
++{
++	struct iu_entry *iue = (struct iu_entry *)bio->bi_private;
++	struct server_adapter *adapter = iue->adapter;
++	unsigned long flags;
++
++	if (bio->bi_size)
++		return 1;
++
++	if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) {
++		iue->req.ioerr = 1;
++	};
++
++	/* Add the bio to the done queue */
++	spin_lock_irqsave(&adapter->lock, flags);
++	if (adapter->bio_donetail) {
++		adapter->bio_donetail->bi_next = bio;
++		adapter->bio_donetail = bio;
++	} else
++		adapter->bio_done = adapter->bio_donetail = bio;
++	bio->bi_next = NULL;
++	spin_unlock_irqrestore(&adapter->lock, flags);
++
++	/* Schedule the task */
++	tasklet_schedule(&adapter->endio_tasklet);
++
++	return 0;
++}
++
++/*
++ * Find the vdev structure from the LUN field in an SRP IUE
++ * Note that this routine bumps a refcount field in the vdev.
++ * Normally this is done when free_iu is called.
++ */
++static struct vdev *find_device(struct iu_entry *iue)
++{
++	u16 *lun = (u16 *) & iue->iu->srp.cmd.lun;
++	u32 bus = (lun[0] & 0x00E0) >> 5;
++	u32 target = (lun[0] & 0x3F00) >> 8;
++	u32 slun = (lun[0] & 0x001F);
++	struct vdev *vd;
++	unsigned long flags;
++
++	/* If asking for a lun other than 0, return nope */
++	if (slun) {
++		return NULL;
++	}
++
++	/* Only from SRP CMD */
++	if (iue->iu->srp.generic.type != SRP_CMD_TYPE)
++		return NULL;
++
++	/* if not a recognized LUN format, return NULL */
++	if ((lun[0] & 0xC000) != 0x8000)
++		return NULL;
++
++	spin_lock_irqsave(&iue->adapter->lock, flags);
++	if (iue->adapter->vbus[bus] == NULL) {
++		spin_unlock_irqrestore(&iue->adapter->lock, flags);
++		return NULL;
++	}
++
++	vd = iue->adapter->vbus[bus]->vdev[target];
++
++	if ((vd == NULL) || (vd->disabled)) {
++		spin_unlock_irqrestore(&iue->adapter->lock, flags);
++		return NULL;
++	}
++
++	if (vd) {
++		atomic_inc(&vd->refcount);
++	}
++	spin_unlock_irqrestore(&iue->adapter->lock, flags);
++
++	return vd;
++}
++
++/*
++ * Process BH buffer completions.  When the end_io routine gets called 
++ * we queue the bio on an internal queue and start a task to process them
++ */
++static void endio_task(unsigned long data)
++{
++	struct server_adapter *adapter = (struct server_adapter *)data;
++	struct iu_entry *iue= NULL;
++	struct bio *bio;
++	int bytes;
++	unsigned long flags;
++
++	do {
++		spin_lock_irqsave(&adapter->lock, flags);
++		if ((bio = adapter->bio_done)) {
++			if (bio == adapter->bio_donetail)
++				adapter->bio_donetail = NULL;
++			adapter->bio_done = bio->bi_next;
++			bio->bi_next = NULL;
++		}
++		if (bio) {
++			/* Remove this iue from the in-flight list */
++			iue = (struct iu_entry *)bio->bi_private;
++			if (!iue->req.in_use) {
++				err("Internal error! freed iue in bio!!!\n");
++				spin_unlock_irqrestore(&adapter->lock, flags);
++				return;
++			}
++				
++			list_del(&iue->next);
++		}
++
++		spin_unlock_irqrestore(&adapter->lock, flags);
++
++		if (bio) {
++			/* Send back the SRP and data if this request was NOT
++			 * aborted 
++			 */
++			if (!iue->aborted) {
++
++				if (!iue->req.ioerr) {
++					/* return data if this was a read */
++					if (!iue->req.write) {
++						bytes =
++						    send_cmd_data(iue->req.
++								  data_token,
++								  iue->req.
++								  data_len,
++								  iue);
++						if (bytes != iue->req.data_len) {
++							err("Error sending data "
++							    "on response "
++							    "(tried %d, sent %d\n", 
++							    bio->bi_size, bytes);
++							send_rsp(iue,
++								 SENSE_ABORT);
++						} else {
++							send_rsp(iue,
++								 SENSE_SUCCESS);
++						}
++					} else {
++						send_rsp(iue, SENSE_SUCCESS);
++					}
++				} else {
++					err("Block operation failed\n");
++					print_command(iue->iu->srp.cmd.cdb);
++					send_rsp(iue, SENSE_DEVICE_FAULT);
++				}
++			}
++
++			spin_lock_irqsave(&adapter->lock, flags);
++			free_data_buffer(iue->req.data_buffer,
++					 iue->req.data_token, iue->req.data_len,
++					 adapter);
++			spin_unlock_irqrestore(&adapter->lock, flags);
++
++			free_iu(iue);
++
++			bio_put(bio);
++			atomic_dec(&adapter->bio_count);
++		}
++	} while (bio);
++}
++
++/* ==============================================================
++ * SCSI Command Emulation Routines
++ * ==============================================================
++ */
++
++/*
++ * Process an inquiry SCSI Command
++ */
++static void process_inquiry(struct iu_entry *iue)
++{
++	struct inquiry_data *id;
++	dma_addr_t data_token;
++	u8 *raw_id;
++	int bytes;
++
++	id = (struct inquiry_data *)dma_alloc_coherent(iue->adapter->dev,
++						       sizeof(*id),
++						       &data_token, 0);
++	raw_id = (u8 *)id;
++	memset(id, 0x00, sizeof(*id));
++
++	/* If we have a valid device */
++	if (iue->req.vd) {
++		/* Standard inquiry page */
++		if ((iue->iu->srp.cmd.cdb[1] == 0x00) &&
++		    (iue->iu->srp.cmd.cdb[2] == 0x00)) {
++			dbg("  inquiry returning device\n");
++			id->qual_type = 0x00;	/* Direct Access    */
++			id->rmb_reserve = 0x00;	/* TODO: CD is removable  */
++			id->version = 0x84;	/* ISO/IE                 */
++			id->aerc_naca_hisup_format = 0x22;/* naca & fmt 0x02 */
++			id->addl_len = sizeof(*id) - 4;	
++			id->bque_encserv_vs_multip_mchngr_reserved = 0x00;
++			id->reladr_reserved_linked_cmdqueue_vs = 0x02;/*CMDQ*/
++			memcpy(id->vendor, "IBM     ", 8);
++			memcpy(id->product, "VSCSI blkdev    ", 16);
++			memcpy(id->revision, "0001", 4);
++			snprintf(id->unique,sizeof(id->unique),
++				 "IBM-VSCSI-%s-P%d-%x-%d-%d-%d\n",
++				 system_id,
++				 partition_number,
++				 iue->adapter->dma_dev->unit_address,
++				 GETBUS(iue->req.vd->lun),
++				 GETTARGET(iue->req.vd->lun),
++				 GETLUN(iue->req.vd->lun));
++		} else if ((iue->iu->srp.cmd.cdb[1] == 0x01) &&
++			   (iue->iu->srp.cmd.cdb[2] == 0x00)) {
++			/* Supported VPD pages */
++			raw_id[0] = 0x00; /* qualifier & type */
++			raw_id[1] = 0x80; /* page */
++			raw_id[2] = 0x00; /* reserved */
++			raw_id[3] = 0x03; /* length */
++			raw_id[4] = 0x00; /* page 0 */
++			raw_id[5] = 0x80; /* serial number page */
++		} else if ((iue->iu->srp.cmd.cdb[1] == 0x01) &&
++			   (iue->iu->srp.cmd.cdb[2] == 0x80)) {
++			/* serial number page */
++			raw_id[0] = 0x00; /* qualifier & type */
++			raw_id[1] = 0x80; /* page */
++			raw_id[2] = 0x00; /* reserved */
++			snprintf((char *)(raw_id+4),
++				 sizeof(*id)-4,
++				 "IBM-VSCSI-%s-P%d-%x-%d-%d-%d\n",
++				 system_id,
++				 partition_number,
++				 iue->adapter->dma_dev->unit_address,
++				 GETBUS(iue->req.vd->lun),
++				 GETTARGET(iue->req.vd->lun),
++				 GETLUN(iue->req.vd->lun));
++			raw_id[3] = strlen((char *)raw_id+4);
++		} else {
++			/* Some unsupported data */
++			send_rsp(iue, SENSE_INVALID_FIELD);
++			free_iu(iue);
++			return;
++		}
++	} else {
++		dbg("  inquiry returning no device\n");
++		id->qual_type = 0x7F;	/* Not supported, no device */
++	}
++
++	bytes = send_cmd_data(data_token, sizeof(*id), iue);
++
++	dma_free_coherent(iue->adapter->dev, sizeof(*id), id, data_token);
++
++	if (bytes < 0) {
++		send_rsp(iue, SENSE_DEVICE_FAULT);
++	} else {
++		send_rsp(iue, SENSE_SUCCESS);
++	}
++
++	free_iu(iue);
++}
++
++/*
++ * Handle an I/O.  Called by WRITE6, WRITE10, etc
++ */
++static void process_rw(char *cmd, int rw, struct iu_entry *iue, long lba,
++		       long len)
++{
++	char *buffer;
++	struct bio *bio;
++	int bytes;
++	int num_biovec;
++	int cur_biovec;
++	long flags;
++
++	dbg("%s %16.16lx[%d:%d:%d][%s] lba %ld len %ld reladr %d link %d\n",
++	    cmd,
++	    iue->iu->srp.cmd.lun,
++	    GETBUS(iue->iu->srp.cmd.lun),
++	    GETTARGET(iue->iu->srp.cmd.lun),
++	    GETLUN(iue->iu->srp.cmd.lun),
++	    iue->req.vd->b.device_name,
++	    lba,
++	    len / iue->req.vd->b.blksize,
++	    iue->iu->srp.cmd.cdb[1] & 0x01, iue->req.linked);
++
++	if (rw == WRITE) {
++		atomic_inc(&iue->adapter->write_processed);
++	} else if (rw == READ) {
++		atomic_inc(&iue->adapter->read_processed);
++	} else {
++		err("Major internal error...rw not read or write\n");
++		send_rsp(iue, SENSE_DEVICE_FAULT);
++
++		free_iu(iue);
++		return;
++	}
++
++	if (len == 0) {
++		warn("Zero length I/O\n");
++		send_rsp(iue, SENSE_INVALID_CMD);
++
++		free_iu(iue);
++		return;
++	}
++
++	/* Writing to a read-only device */
++	if ((rw == WRITE) && (iue->req.vd->b.ro)) {
++		warn("WRITE to read-only device\n");
++		send_rsp(iue, SENSE_WRITE_PROT);
++
++		free_iu(iue);
++		return;
++	}
++
++	get_data_buffer(&buffer, &iue->req.data_token, len, iue->adapter);
++	iue->req.data_buffer = buffer;
++	iue->req.data_len = len;
++	if (buffer == NULL) {
++		err("Not able to get a data buffer (%lu pages)\n",
++		    len / PAGE_SIZE);
++		send_rsp(iue, SENSE_DEVICE_FAULT);
++
++		free_iu(iue);
++		return;
++	}
++
++	/* if reladr */
++	if (iue->iu->srp.cmd.cdb[1] & 0x01) {
++		lba = lba + iue->req.vd->b.lastlba;
++	}
++
++	/* If this command is linked, Keep this lba */
++	if (iue->req.linked) {
++		iue->req.vd->b.lastlba = lba;
++	} else {
++		iue->req.vd->b.lastlba = 0;
++	}
++
++	if (rw == WRITE) {
++		iue->req.write = 1;
++		/* Get the data */
++		bytes = get_cmd_data(iue->req.data_token, len, iue);
++		if (bytes != len) {
++			err("Error transferring data\n");
++			send_rsp(iue, SENSE_DEVICE_FAULT);
++
++			free_iu(iue);
++			return;
++		}
++	}
++
++	num_biovec = (len - 1) / PAGE_CACHE_SIZE + 1;
++
++	bio = bio_alloc(GFP_ATOMIC, num_biovec);
++	if (!bio) {
++		/* Ouch.  couldn't get a bio.  Mark this I/O as 
++		 * in error, then decrement the outstanding bio.
++		 * If there are still outstanding bio, they will send
++		 * the error and free the IU.  If there are none, we
++		 * should do it here
++		 */
++		iue->req.ioerr = 1;
++		err("Not able to allocate a bio\n");
++		send_rsp(iue, SENSE_DEVICE_FAULT);
++		free_iu(iue);
++		return;
++	}
++
++	iue->aborted = 0;
++	spin_lock_irqsave(&iue->adapter->lock, flags);
++	list_add_tail(&iue->next, &iue->adapter->inflight);
++	spin_unlock_irqrestore(&iue->adapter->lock, flags);
++
++	atomic_inc(&iue->adapter->bio_count);
++	bio->bi_size = len;
++	bio->bi_bdev = iue->req.vd->b.bdev;
++	bio->bi_sector = lba;
++	bio->bi_end_io = &ibmvscsis_end_io;
++	bio->bi_private = iue;
++	bio->bi_rw = (rw == WRITE) ? 1 : 0;
++	bio->bi_phys_segments = 1;
++	bio->bi_hw_segments = 1;
++
++	/* This all assumes that the buffers we get are page-aligned */
++	for (cur_biovec = 0; cur_biovec < num_biovec; cur_biovec++) {
++		long thislen;
++
++		if (len > PAGE_CACHE_SIZE) {
++			thislen = PAGE_CACHE_SIZE;
++		} else {
++			thislen = len;
++		}
++
++		bio->bi_io_vec[cur_biovec].bv_page = virt_to_page(buffer);
++		bio->bi_io_vec[cur_biovec].bv_len = thislen;
++		bio->bi_io_vec[cur_biovec].bv_offset =
++		    (unsigned long)buffer & PAGE_OFFSET_MASK;
++		bio->bi_vcnt++;
++
++		len -= thislen;
++		buffer += thislen;
++	}
++	generic_make_request(bio);
++}
++
++/*
++ * Process a READ6
++ */
++static void processRead6(struct iu_entry *iue)
++{
++	long lba = (*((u32 *) (iue->iu->srp.cmd.cdb))) & 0x001FFFFF;
++	long len = iue->iu->srp.cmd.cdb[4];
++
++	/* Length of 0 indicates 256 */
++	if (len == 0) {
++		len = 256;
++	}
++
++	len = len * iue->req.vd->b.blksize;
++
++	process_rw("Read6", READ, iue, lba, len);
++}
++
++/*
++ * Process a READ10
++ */
++static void processRead10(struct iu_entry *iue)
++{
++	long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2));
++	long len =
++	    *((u16 *) (iue->iu->srp.cmd.cdb + 7)) * iue->req.vd->b.blksize;
++
++	process_rw("Read10", READ, iue, lba, len);
++}
++
++/*
++ * Process a READ10
++ */
++static void processRead12(struct iu_entry *iue)
++{
++	long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2));
++	long len =
++	    *((u32 *) (iue->iu->srp.cmd.cdb + 6)) * iue->req.vd->b.blksize;
++
++	process_rw("Read12", READ, iue, lba, len);
++}
++
++static void processWrite6(struct iu_entry *iue)
++{
++	long lba = (*((u32 *) (iue->iu->srp.cmd.cdb))) & 0x001FFFFF;
++	long len = iue->iu->srp.cmd.cdb[4];
++
++	/* Length of 0 indicates 256 */
++	if (len == 0) {
++		len = 256;
++	}
++
++	len = len * iue->req.vd->b.blksize;
++
++	process_rw("Write6", WRITE, iue, lba, len);
++}
++
++static void processWrite10(struct iu_entry *iue)
++{
++	long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2));
++	long len =
++	    *((u16 *) (iue->iu->srp.cmd.cdb + 7)) * iue->req.vd->b.blksize;
++
++	process_rw("Write10", WRITE, iue, lba, len);
++}
++
++static void processWrite12(struct iu_entry *iue)
++{
++	long lba = *((u32 *) (iue->iu->srp.cmd.cdb + 2));
++	long len =
++	    *((u32 *) (iue->iu->srp.cmd.cdb + 6)) * iue->req.vd->b.blksize;
++
++	process_rw("Write12", WRITE, iue, lba, len);
++}
++
++/*
++ * Handle Read Capacity
++ */
++static void processReadCapacity(struct iu_entry *iue)
++{
++	struct ReadCapacityData {
++		u32 blocks;
++		u32 blocksize;
++	} *cap;
++	dma_addr_t data_token;
++	int bytes;
++
++	cap = (struct ReadCapacityData *)dma_alloc_coherent(iue->adapter->dev,
++							    sizeof(*cap),
++							    &data_token, 0);
++
++	/* return block size and last valid block */
++	cap->blocksize = iue->req.vd->b.blksize;
++	cap->blocks = iue->req.vd->b.bdev->bd_inode->i_size
++	    / iue->req.vd->b.blksize 
++	    - 1;
++
++	info("Reporting capacity as %u block of size %u\n", cap->blocks,
++	     cap->blocksize);
++
++	bytes = send_cmd_data(data_token, sizeof(*cap), iue);
++
++	dma_free_coherent(iue->adapter->dev, sizeof(*cap), cap, data_token);
++
++	if (bytes != sizeof(*cap)) {
++		err("Error sending read capacity data. bytes %d, wanted %ld\n",
++		    bytes, sizeof(*cap));
++	}
++
++	send_rsp(iue, SENSE_SUCCESS);
++
++	free_iu(iue);
++}
++
++/*
++ * Process Mode Sense
++ * TODO: I know scsiinfo asks for a bunch of mode pages not implemented here.
++ *       Also, we need to act differently for virtual disk and virtual CD
++ */
++#define MODE_SENSE_BUFFER_SIZE (512)
++static void processModeSense(struct iu_entry *iue)
++{
++	dma_addr_t data_token;
++	int bytes;
++
++	u8 *mode = (u8 *) dma_alloc_coherent(iue->adapter->dev,
++					     MODE_SENSE_BUFFER_SIZE,
++					     &data_token, 0);
++	/* which page */
++	switch (iue->iu->srp.cmd.cdb[2]) {
++	case 0:
++	case 0x3f:
++		mode[1] = 0x00;	/* Default medium */
++		if (iue->req.vd->b.ro) {
++			mode[2] = 0x80;	/* device specific  */
++		} else {
++			mode[2] = 0x00;	/* device specific  */
++		}
++		/* note the DPOFUA bit is set to zero! */
++		mode[3] = 0x08;	/* block descriptor length */
++		*((u32 *) & mode[4]) = iue->req.vd->b.bdev->bd_inode->i_size /
++		    iue->req.vd->b.blksize;
++		*((u32 *) & mode[8]) = iue->req.vd->b.blksize;
++		bytes = mode[0] = 12;	/* length */
++		break;
++
++	case 0x08:		/* Cache page */
++		/* length should be 4 */
++		if (iue->iu->srp.cmd.cdb[4] != 4
++		    && iue->iu->srp.cmd.cdb[4] != 0x20) {
++			send_rsp(iue, SENSE_INVALID_CMD);
++			dma_free_coherent(iue->adapter->dev,
++					  MODE_SENSE_BUFFER_SIZE,
++					  mode, data_token);
++			free_iu(iue);
++			return;
++		}
++
++		mode[1] = 0x00;	/* Default medium */
++		if (iue->req.vd->b.ro) {
++			mode[2] = 0x80;	/* device specific */
++		} else {
++			mode[2] = 0x00;	/* device specific */
++		}
++		/* note the DPOFUA bit is set to zero! */
++		mode[3] = 0x08;	/* block descriptor length */
++		*((u32 *) & mode[4]) = iue->req.vd->b.bdev->bd_inode->i_size /
++		    iue->req.vd->b.blksize;
++		*((u32 *) & mode[8]) = iue->req.vd->b.blksize;
++
++		/* Cache page */
++		mode[12] = 0x08;	/* page */
++		mode[13] = 0x12;	/* page length */
++		mode[14] = 0x01;	/* no cache (0x04 for read/write cache) */
++
++		bytes = mode[0] = 12 + mode[13];	/* length */
++		break;
++	default:
++		warn("Request for unknown mode page %d\n",
++		     iue->iu->srp.cmd.cdb[2]);
++		send_rsp(iue, SENSE_INVALID_CMD);
++		dma_free_coherent(iue->adapter->dev,
++				  MODE_SENSE_BUFFER_SIZE, mode, data_token);
++		free_iu(iue);
++		return;
++	}
++
++	bytes = send_cmd_data(data_token, bytes, iue);
++
++	dma_free_coherent(iue->adapter->dev,
++			  MODE_SENSE_BUFFER_SIZE, mode, data_token);
++
++	send_rsp(iue, SENSE_SUCCESS);
++
++	free_iu(iue);
++	return;
++}
++
++/*
++ * Report LUNS command.
++ */
++static void processReportLUNs(struct iu_entry *iue)
++{
++	int listsize = did_len(&iue->iu->srp.cmd);
++	dma_addr_t data_token;
++	int index = 2;		/* Start after the two entries (length and LUN0) */
++	int bus;
++	int target;
++	int bytes;
++	unsigned long flags;
++
++	u64 *lunlist = (u64 *) dma_alloc_coherent(iue->adapter->dev,
++						  listsize,
++						  &data_token, 0);
++
++	memset(lunlist, 0x00, listsize);
++
++	/* work out list size in units of u64 */
++	listsize = listsize / 8;
++
++	if (listsize < 1) {
++		send_rsp(iue, SENSE_INVALID_CMD);
++		free_iu(iue);
++	}
++
++	spin_lock_irqsave(&iue->adapter->lock, flags);
++
++	/* send lunlist of size 1 when requesting lun is not all zeros */
++	if (iue->iu->srp.cmd.lun != 0x0LL) {
++		*lunlist = ((u64) 1 * 8) << 32;
++		goto send_lunlist;
++	}
++
++	/* return the total number of luns plus LUN0 in bytes */
++	*lunlist = (((u64) ((iue->adapter->nvdevs + 1) * 8)) << 32);
++
++	dbg("reporting %d luns\n", iue->adapter->nvdevs + 1);
++	/* loop through the bus */
++	for (bus = 0; bus < BUS_PER_ADAPTER; bus++) {
++		/* If this bus exists */
++		if (iue->adapter->vbus[bus]) {
++			/* loop through the targets */
++			for (target = 0; target < TARGETS_PER_BUS; target++) {
++				/* If the target exists */
++				if (iue->adapter->vbus[bus]->vdev[target]) {
++					if ((index < listsize) &&
++					    (!iue->adapter->vbus[bus]->
++					     vdev[target]->disabled)) {
++						lunlist[index++] =
++						    iue->adapter->vbus[bus]->
++						    vdev[target]->lun;
++						dbg("  lun %16.16lx\n",
++						    iue->adapter->vbus[bus]->
++						    vdev[target]->lun);
++					}
++				}
++			}
++		}
++	}
++
++      send_lunlist:
++	spin_unlock_irqrestore(&iue->adapter->lock, flags);
++
++	bytes = send_cmd_data(data_token, (index * 8), iue);
++
++	dma_free_coherent(iue->adapter->dev, listsize * 8, lunlist, data_token);
++
++	if (bytes != (index * 8)) {
++		err("Error sending report luns data. bytes %d, wanted %d\n",
++		    bytes, index * 4);
++		send_rsp(iue, SENSE_ABORT);
++	} else {
++		send_rsp(iue, SENSE_SUCCESS);
++	}
++
++	free_iu(iue);
++	return;
++}
++
++/*
++ * Process an IU.  
++ *
++ * Note that THIS routine is responsible for returning the IU from the pool
++ * The current assumption is that all the process routines called from here
++ * are, in turn, responsible for freeing the IU
++ */
++static void process_cmd(struct iu_entry *iue)
++{
++	union viosrp_iu *iu = iue->iu;
++
++	iue->req.vd = find_device(iue);
++
++	if ((iue->req.vd == NULL) &&
++	    (iu->srp.cmd.cdb[0] != REPORT_LUNS) &&
++	    (iu->srp.cmd.cdb[0] != INQUIRY)) {
++		dbg("Cmd %2.2x for unknown LUN %16.16lx\n",
++		    iu->srp.cmd.cdb[0], iue->iu->srp.cmd.lun);
++		send_rsp(iue, SENSE_INVALID_ID);
++		free_iu(iue);
++		return;
++	}
++
++	iue->req.linked = getlink(iue);
++
++	switch (iu->srp.cmd.cdb[0]) {
++	case READ_6:
++		processRead6(iue);
++		break;
++	case READ_10:
++		processRead10(iue);
++		break;
++	case READ_12:
++		processRead12(iue);
++		break;
++	case WRITE_6:
++		processWrite6(iue);
++		break;
++	case WRITE_10:
++		processWrite10(iue);
++		break;
++	case WRITE_12:
++		processWrite12(iue);
++		break;
++	case REPORT_LUNS:
++		dbg("REPORT LUNS lun %16.16lx\n", iue->iu->srp.cmd.lun);
++		processReportLUNs(iue);
++		break;
++	case INQUIRY:
++		dbg("INQUIRY lun %16.16lx\n", iue->iu->srp.cmd.lun);
++		process_inquiry(iue);
++		break;
++	case READ_CAPACITY:
++		dbg("READ CAPACITY lun %16.16lx\n", iue->iu->srp.cmd.lun);
++		processReadCapacity(iue);
++		break;
++	case MODE_SENSE:
++		dbg("MODE SENSE lun %16.16lx\n", iue->iu->srp.cmd.lun);
++		processModeSense(iue);
++		break;
++	case TEST_UNIT_READY:
++		/* we already know the device exists */
++		dbg("TEST UNIT READY lun %16.16lx\n", iue->iu->srp.cmd.lun);
++		send_rsp(iue, SENSE_SUCCESS);
++		free_iu(iue);
++		break;
++	case START_STOP:
++		/* just respond OK */
++		dbg("START_STOP lun %16.16lx\n", iue->iu->srp.cmd.lun);
++		send_rsp(iue, SENSE_SUCCESS);
++		free_iu(iue);
++		break;
++	default:
++		warn("Unsupported SCSI Command 0x%2.2x\n", iu->srp.cmd.cdb[0]);
++		send_rsp(iue, SENSE_INVALID_CMD);
++		free_iu(iue);
++	}
++}
++
++u16 send_adapter_info(struct iu_entry *iue,
++		      dma_addr_t remote_buffer, u16 length)
++{
++	dma_addr_t data_token;
++	struct mad_adapter_info_data *info =
++	    (struct mad_adapter_info_data *)dma_alloc_coherent(iue->adapter->
++							       dev,
++							       sizeof(*info),
++							       &data_token, 0);
++
++	dbg("in send_adapter_info\n ");
++	if ((info) && (!dma_mapping_error(data_token))) {
++		int rc;
++		memset(info, 0x00, sizeof(*info));
++
++		dbg("building adapter_info\n ");
++		strcpy(info->srp_version, "1.6a");
++		strncpy(info->partition_name, partition_name,
++			sizeof(info->partition_name));
++		info->partition_number = partition_number;
++		info->mad_version = 1;
++		info->os_type = 3;
++
++		rc = h_copy_rdma(sizeof(*info),
++				 iue->adapter->liobn,
++				 data_token,
++				 iue->adapter->riobn,
++				 remote_buffer);
++
++		dma_free_coherent(iue->adapter->dev,
++				  sizeof(*info), info, data_token);
++
++		if (rc != H_Success) {
++			err("Error sending adapter info rc %d\n",rc);
++			return 1;
++		}
++	} else {
++		dbg("bad dma_alloc_cohereint in adapter_info\n ");
++		return 1;
++	}
++	return 0;
++
++}
++
++/* ==============================================================
++ * SRP Processing Routines
++ * ==============================================================
++ */
++/*
++ * Process an incoming SRP Login request
++ */
++static void process_login(struct iu_entry *iue)
++{
++	union viosrp_iu *iu = iue->iu;
++	u64 tag = iu->srp.generic.tag;
++
++	/* TODO handle case that requested size is wrong and buffer format is wrong */
++	memset(iu, 0x00, sizeof(struct srp_login_rsp));
++	iu->srp.login_rsp.type = SRP_LOGIN_RSP_TYPE;
++	iu->srp.login_rsp.request_limit_delta = iue->adapter->pool.size;
++	iu->srp.login_rsp.tag = tag;
++	iu->srp.login_rsp.max_initiator_to_target_iulen = sizeof(union srp_iu);
++	iu->srp.login_rsp.max_target_to_initiator_iulen = sizeof(union srp_iu);
++	iu->srp.login_rsp.supported_buffer_formats = 0x0006;	/* direct and indirect */
++	iu->srp.login_rsp.multi_channel_result = 0x00;	/* TODO fix if we were already logged in */
++
++	send_srp(iue, sizeof(iu->srp.login_rsp));
++}
++
++/*
++ * Send an SRP response that includes sense data
++ */
++static long send_rsp(struct iu_entry *iue, int status)
++{
++	u8 *sense = iue->iu->srp.rsp.sense_and_response_data;
++	u64 tag = iue->iu->srp.generic.tag;
++	union viosrp_iu *iu = iue->iu;
++
++	if (status != SENSE_SUCCESS) {
++		atomic_inc(&iue->adapter->errors);
++	}
++
++	/* If the linked bit is on and status is good */
++	if ((iue->req.linked) && (status == SENSE_SUCCESS)) {
++		status = SENSE_INTERMEDIATE;
++	}
++
++	memset(iu, 0x00, sizeof(struct srp_rsp));
++	iu->srp.rsp.type = SRP_RSP_TYPE;
++	iu->srp.rsp.request_limit_delta = 1;
++	iu->srp.rsp.tag = tag;
++
++	iu->srp.rsp.diunder = iue->req.diunder;
++	iu->srp.rsp.diover = iue->req.diover;
++	iu->srp.rsp.dounder = iue->req.dounder;
++	iu->srp.rsp.doover = iue->req.doover;
++
++	iu->srp.rsp.data_in_residual_count = iue->req.data_in_residual_count;
++	iu->srp.rsp.data_out_residual_count = iue->req.data_out_residual_count;
++
++	iu->srp.rsp.rspvalid = 0;
++
++	iu->srp.rsp.response_data_list_length = 0;
++
++	if (status) {
++		iu->srp.rsp.status = SAM_STAT_CHECK_CONDITION;
++		iu->srp.rsp.snsvalid = 1;
++		iu->srp.rsp.sense_data_list_length = 18;	/* TODO be smarter about this */
++
++		/* Valid bit and 'current errors' */
++		sense[0] = (0x1 << 7 | 0x70);
++
++		/* Sense key */
++		sense[2] = ibmvscsis_sense_data[status][0];
++
++		/* Additional sense length */
++		sense[7] = 0xa;	/* 10 bytes */
++
++		/* Additional sense code */
++		sense[12] = ibmvscsis_sense_data[status][1];
++
++		/* Additional sense code qualifier */
++		sense[13] = ibmvscsis_sense_data[status][2];
++	} else {
++		iu->srp.rsp.status = 0;
++	}
++
++	send_srp(iue, sizeof(iu->srp.rsp));
++
++	return 0;
++}
++
++static void process_device_reset(struct iu_entry *iue)
++{
++	struct iu_entry *tmp_iue;
++	unsigned long flags;
++	union viosrp_iu *iu = iue->iu;
++
++	info("device reset for lun %16.16lx\n", iu->srp.tsk_mgmt.lun);
++
++	spin_lock_irqsave(&iue->adapter->lock, flags);
++
++	list_for_each_entry(tmp_iue, &iue->adapter->inflight, next) {
++		if (iu->srp.tsk_mgmt.lun == tmp_iue->iu->srp.cmd.lun) {
++			{
++				tmp_iue->aborted = 1;
++			}
++		}
++
++	}
++
++	spin_unlock_irqrestore(&iue->adapter->lock, flags);
++	send_rsp(iue, SENSE_SUCCESS);
++}
++
++static void process_abort(struct iu_entry *iue)
++{
++	struct iu_entry *tmp_iue;
++	unsigned long flags;
++	union viosrp_iu *iu = iue->iu;
++
++	info("aborting task with tag %16.16lx, lun %16.16lx\n",
++	     iu->srp.tsk_mgmt.managed_task_tag, iu->srp.tsk_mgmt.lun);
++
++	spin_lock_irqsave(&iue->adapter->lock, flags);
++
++	list_for_each_entry(tmp_iue, &iue->adapter->inflight, next) {
++		if (tmp_iue->iu->srp.cmd.tag ==
++		    iu->srp.tsk_mgmt.managed_task_tag) {
++			{
++				tmp_iue->aborted = 1;
++				info("abort successful\n");
++				spin_unlock_irqrestore(&iue->adapter->lock,
++						       flags);
++				send_rsp(iue, SENSE_SUCCESS);
++				return;
++			}
++		}
++	}
++	info("unable to abort cmd\n");
++
++	spin_unlock_irqrestore(&iue->adapter->lock, flags);
++	send_rsp(iue, SENSE_INVALID_ID);
++}
++
++static void process_tsk_mgmt(struct iu_entry *iue)
++{
++	union viosrp_iu *iu = iue->iu;
++
++	if (iu->srp.tsk_mgmt.task_mgmt_flags == 0x01) {
++		process_abort(iue);
++	} else if (iu->srp.tsk_mgmt.task_mgmt_flags == 0x08) {
++		process_device_reset(iue);
++	} else {
++		send_rsp(iue, SENSE_INVALID_CMD);
++	}
++}
++
++static void process_iu(struct viosrp_crq *crq, struct server_adapter *adapter)
++{
++	struct iu_entry *iue = get_iu(adapter);
++	union viosrp_iu *iu;
++	int queued = 0;
++	long rc;
++
++	if (iue == NULL) {
++		/* TODO Yikes! */
++		warn("Error getting IU from pool, other side exceeded limit\n");
++		return;
++	}
++
++	iue->req.remote_token = crq->IU_data_ptr;
++
++	rc = h_copy_rdma(crq->IU_length,
++			 iue->adapter->riobn,
++			 iue->req.remote_token, adapter->liobn, iue->iu_token);
++
++	iu = iue->iu;
++
++	if (rc) {
++		err("Error %ld transferring data to client\n", rc);
++	}
++
++	if (crq->format == VIOSRP_MAD_FORMAT) {
++		switch (iu->mad.empty_iu.common.type) {
++		case VIOSRP_EMPTY_IU_TYPE:
++			warn("Unsupported EMPTY MAD IU\n");
++			break;
++		case VIOSRP_ERROR_LOG_TYPE:
++			warn("Unsupported ERROR LOG MAD IU\n");
++			iu->mad.error_log.common.status = 1;
++			send_srp(iue, sizeof(iu->mad.error_log));
++			break;
++		case VIOSRP_ADAPTER_INFO_TYPE:
++			iu->mad.adapter_info.common.status =
++			    send_adapter_info(iue,
++					      iu->mad.adapter_info.buffer,
++					      iu->mad.adapter_info.common.
++					      length);
++
++			send_srp(iue, sizeof(iu->mad.adapter_info));
++			break;
++		case VIOSRP_HOST_CONFIG_TYPE:
++			iu->mad.host_config.common.status = 1;
++			send_srp(iue, sizeof(iu->mad.host_config));
++			break;
++		default:
++			warn("Unsupported MAD type %d\n", iu->srp.generic.type);
++		}
++	} else {
++		switch (iu->srp.generic.type) {
++		case SRP_LOGIN_REQ_TYPE:
++			dbg("SRP LOGIN\n");
++			process_login(iue);
++			break;
++		case SRP_LOGIN_RSP_TYPE:
++			warn("Unsupported LOGIN_RSP SRP IU\n");
++			break;
++		case SRP_I_LOGOUT_TYPE:
++			warn("Unsupported I_LOGOUT SRP IU\n");
++			break;
++		case SRP_T_LOGOUT_TYPE:
++			warn("Unsupported T_LOGOUT SRP IU\n");
++			break;
++		case SRP_TSK_MGMT_TYPE:
++			process_tsk_mgmt(iue);
++			break;
++		case SRP_CMD_TYPE:
++			process_cmd(iue);
++			queued = 1;
++			break;
++		case SRP_RSP_TYPE:
++			warn("Unsupported RSP SRP IU\n");
++			break;
++		case SRP_CRED_REQ_TYPE:
++			warn("Unsupported CRED_REQ SRP IU\n");
++			break;
++		case SRP_CRED_RSP_TYPE:
++			warn("Unsupported CRED_RSP SRP IU\n");
++			break;
++		case SRP_AER_REQ_TYPE:
++			warn("Unsupported AER_REQ SRP IU\n");
++			break;
++		case SRP_AER_RSP_TYPE:
++			warn("Unsupported AER_RSP SRP IU\n");
++			break;
++		default:
++			warn("Unsupported SRP type %d\n", iu->srp.generic.type);
++		}
++	}
++
++	/* 
++	 * If no one has queued the IU for further work, free it 
++	 * Note that this is kind of an ugly design based on setting
++	 * this variable up above in cases where the routine we call
++	 * is responsible for freeing the IU
++	 */
++	if (!queued)
++		free_iu(iue);
++}
++
++/* ==============================================================
++ * CRQ Processing Routines
++ * ==============================================================
++ */
++
++/*
++ * Handle a CRQ event
++ */
++static void handle_crq(struct viosrp_crq *crq, struct server_adapter *adapter)
++{
++	switch (crq->valid) {
++	case 0xC0:		/* initialization */
++		switch (crq->format) {
++		case 0x01:
++			info("Client just initialized\n");
++			plpar_hcall_norets(H_SEND_CRQ,
++					   adapter->dma_dev->unit_address,
++					   0xC002000000000000, 0);
++			break;
++		case 0x02:
++			info("Client initialization complete\n");
++			break;
++		default:
++			err("Client error: Unknwn msg format %d\n", 
++			    crq->format);
++		}
++		return;
++	case 0xFF:		/* transport event */
++		info("Client closed\n");
++		return;
++	case 0x80:		/* real payload */
++		{
++			switch (crq->format) {
++			case VIOSRP_SRP_FORMAT:
++			case VIOSRP_MAD_FORMAT:
++				process_iu(crq, adapter);
++				break;
++			case VIOSRP_OS400_FORMAT:
++				warn("Unsupported OS400 format CRQ\n");
++				break;
++
++			case VIOSRP_AIX_FORMAT:
++				warn("Unsupported AIX format CRQ\n");
++				break;
++
++			case VIOSRP_LINUX_FORMAT:
++				warn("Unsupported LINUX format CRQ\n");
++				break;
++
++			case VIOSRP_INLINE_FORMAT:
++				warn("Unsupported _INLINE_ format CRQ\n");
++				break;
++
++			default:
++				err("Client error: Unsupported  msg format %d\n", 
++				    crq->format);
++			}
++		}
++		break;
++	default:
++		err("Client error: unknown message type 0x%02x!?\n",
++		    crq->valid);
++		return;
++	}
++
++}
++
++/*
++ * Task to handle CRQs and completions
++ */
++static void crq_task(void *data)
++{
++	struct server_adapter *adapter = (struct server_adapter *)data;
++	struct viosrp_crq *crq;
++	long rc;
++	int done = 0;
++
++	while (!done) {
++
++		/* Loop through and process CRQs */
++		while ((crq = crq_queue_next_crq(&adapter->queue)) != NULL) {
++			atomic_inc(&adapter->crq_processed);
++			handle_crq(crq, adapter);
++			crq->valid = 0x00;
++		}
++
++		rc = h_vio_signal(adapter->dma_dev->unit_address, 1);
++		if (rc != 0) {
++			err("Error %ld enabling interrupts!!!\n", rc);
++		}
++		if ((crq = crq_queue_next_crq(&adapter->queue)) != NULL) {
++			rc = h_vio_signal(adapter->dma_dev->unit_address, 0);
++			if (rc != 0) {
++				err("Error %ld enabling interrupts!!!\n", rc);
++			}
++			handle_crq(crq, adapter);
++			crq->valid = 0x00;
++		} else {
++			done = 1;
++		}
++	}
++}
++
++/*
++ * Handle the interrupt that occurs when something is placed on our CRQ
++ */
++static irqreturn_t handle_interrupt(int irq, void *dev_instance,
++				    struct pt_regs *regs)
++{
++	struct server_adapter *adapter = (struct server_adapter *)dev_instance;
++	long rc;
++
++	rc = h_vio_signal(adapter->dma_dev->unit_address, 0);
++	if (rc != 0) {
++		err(" Error %ld disabling interrupts!!!\n", rc);
++	}
++
++	atomic_inc(&adapter->interrupts);
++
++	kblockd_schedule_work(&adapter->crq_task);
++
++	return IRQ_HANDLED;
++}
++
++/* 
++ * Initialize our CRQ
++ * return zero on success, non-zero on failure 
++ */
++static int initialize_crq_queue(struct crq_queue *queue,
++				struct server_adapter *adapter)
++{
++	int rc;
++
++	queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL);
++	if (!queue->msgs)
++		goto malloc_failed;
++	queue->size = PAGE_SIZE / sizeof(*queue->msgs);
++
++	queue->msg_token = dma_map_single(adapter->dev, queue->msgs,
++					  queue->size * sizeof(*queue->msgs),
++					  DMA_BIDIRECTIONAL);
++
++	if (dma_mapping_error(queue->msg_token))
++		goto map_failed;
++
++	rc = plpar_hcall_norets(H_REG_CRQ, adapter->dma_dev->unit_address,
++				queue->msg_token, PAGE_SIZE);
++	
++	if ((rc != 0) && (rc != 2)) {
++		err("Error 0x%x opening virtual adapter\n", rc);
++		goto reg_crq_failed;
++	}
++
++	if (request_irq
++	    (adapter->dma_dev->irq, &handle_interrupt, SA_INTERRUPT,
++	     "ibmvscsis", adapter) != 0)
++		goto req_irq_failed;
++
++	rc = h_vio_signal(adapter->dma_dev->unit_address, 1);
++	if (rc != 0) {
++		err("Error %d enabling interrupts!!!\n", rc);
++		goto req_irq_failed;
++	}
++
++	plpar_hcall_norets(H_SEND_CRQ, adapter->dma_dev->unit_address,
++				   0xC001000000000000, 0);
++
++	queue->cur = 0;
++	queue->lock = SPIN_LOCK_UNLOCKED;
++
++	return 0;
++
++      req_irq_failed:
++	do {
++		rc = plpar_hcall_norets(H_FREE_CRQ, adapter->dma_dev->unit_address);
++	} while ((rc == H_Busy) || (H_isLongBusy(rc)));
++	
++      reg_crq_failed:
++	dma_unmap_single(adapter->dev, queue->msg_token,
++			 queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
++      map_failed:
++	free_page((unsigned long)queue->msgs);
++      malloc_failed:
++	return -1;
++}
++
++/*
++ * Release the CRQ
++ */
++static void release_crq_queue(struct crq_queue *queue,
++			      struct server_adapter *adapter)
++{
++	int rc;
++
++	info("releasing adapter\n");
++	free_irq(adapter->dma_dev->irq, adapter);
++	do {
++		rc = plpar_hcall_norets(H_FREE_CRQ, adapter->dma_dev->unit_address);
++	} while ((rc == H_Busy) || (H_isLongBusy(rc)));
++	dma_unmap_single(adapter->dev, queue->msg_token,
++			 queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
++	free_page((unsigned long)queue->msgs);
++}
++
++/* ==============================================================
++ * Module Management
++ * ==============================================================
++ */
++/*
++ * Add a block device as a SCSI LUN
++ */
++static int activate_block_device(struct vdev *vdev)
++{
++	struct block_device *bdev;
++	char *name = vdev->b.device_name;
++	int ro = vdev->b.ro;
++
++	bdev = open_bdev_excl(name, ro, activate_block_device);
++	if (IS_ERR(bdev))
++		return PTR_ERR(bdev);;
++
++	vdev->b.bdev = bdev;
++	vdev->disabled = 0;
++
++	info("Activating block device %s as %sLUN 0x%lx\n",
++	     name, ro ? "read only " : "", vdev->lun);
++
++	return 0;
++}
++
++static void deactivate_block_device(struct vdev *vdev)
++{
++	info("Deactivating block device, LUN 0x%lx\n", vdev->lun);
++
++	/* Wait while any users of this device finish.  Note there should
++	 * be no new users, since we have marked this disabled
++	 *
++	 * We just poll here, since we are blocking write
++	 */
++	while (atomic_read(&vdev->refcount)) {
++		schedule_timeout(HZ / 4);	/* 1/4 second */
++	}
++
++	vdev->disabled = 1;
++	close_bdev_excl(vdev->b.bdev);
++}
++
++
++#define ATTR(_type, _name, _mode)      \
++struct attribute vscsi_##_type##_##_name##_attr = {               \
++.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE \
++};
++
++static struct kobj_type ktype_vscsi_target;
++static struct kobj_type ktype_vscsi_bus;
++static struct kobj_type ktype_vscsi_stats;
++
++static void set_num_targets(struct vbus* vbus, long value)
++{
++	struct device *dev = 
++		container_of(vbus->kobj.parent, struct device , kobj);
++	struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data;
++	int cur_num_targets = atomic_read(&vbus->num_targets);
++	unsigned long flags;
++
++	spin_lock_irqsave(&adapter->lock, flags);
++
++	if (cur_num_targets < value) { //growing
++		int i;
++		for (i = cur_num_targets; i < value; i++) {
++			vbus->vdev[i] = (struct vdev *)
++				kmalloc(sizeof(struct vdev), GFP_KERNEL);
++			if (!vbus->vdev[i]) {
++				spin_unlock_irqrestore(&adapter->lock, flags);
++				err("Couldn't allocate target memory %d\n", i);
++				return;
++			}
++			memset(vbus->vdev[i], 0x00, sizeof(struct vdev));
++
++			vbus->vdev[i]->lun = make_lun(vbus->bus_num, i, 0);
++			vbus->vdev[i]->b.blksize = 512;
++			vbus->vdev[i]->disabled = 1;
++
++			vbus->vdev[i]->kobj.parent = &vbus->kobj;
++			sprintf(vbus->vdev[i]->kobj.name, "target%d", i);
++			vbus->vdev[i]->kobj.ktype = &ktype_vscsi_target;
++			kobject_register(&vbus->vdev[i]->kobj);
++			adapter->nvdevs++;
++			atomic_inc(&vbus->num_targets);
++		}
++	} else { //shrinking
++		int i;
++		for (i = cur_num_targets - 1; i >= value; i--)
++		{
++			if (!vbus->vdev[i]->disabled) {
++				spin_unlock_irqrestore(&adapter->lock, flags);
++				err("Can't remove active target %d\n", i);
++				return;
++			}
++
++			kobject_unregister(&vbus->vdev[i]->kobj);
++
++			kfree(vbus->vdev[i]);
++
++			adapter->nvdevs--;
++			atomic_dec(&vbus->num_targets);
++		}
++	}
++	spin_unlock_irqrestore(&adapter->lock, flags);
++}
++
++static void set_num_buses(struct device *dev, long value)
++{
++	struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data;
++	int cur_num_buses = atomic_read(&adapter->num_buses);
++	unsigned long flags= 0L;
++
++
++	if (cur_num_buses < value) { // growing
++		int i;
++		for (i = cur_num_buses; i < value; i++) {
++			adapter->vbus[i] = (struct vbus *)
++				kmalloc(sizeof(struct vbus), GFP_KERNEL);
++			if (!adapter->vbus[i]) {
++				spin_unlock_irqrestore(&adapter->lock, flags);
++				err("Couldn't allocate bus %d memory\n", i);
++				return;
++			}
++			memset(adapter->vbus[i], 0x00, sizeof(struct vbus));
++
++			spin_lock_irqsave(&adapter->lock, flags);
++
++			adapter->vbus[i]->bus_num = i;
++
++			adapter->vbus[i]->kobj.parent = &dev->kobj;
++			sprintf(adapter->vbus[i]->kobj.name, "bus%d", i);
++			adapter->vbus[i]->kobj.ktype = &ktype_vscsi_bus; 
++			kobject_register(&adapter->vbus[i]->kobj);
++			
++			atomic_inc(&adapter->num_buses);
++			spin_unlock_irqrestore(&adapter->lock, flags);
++
++			set_num_targets(adapter->vbus[i], 1);
++		}
++
++	} else if (cur_num_buses > value) { //shrinking
++		int i, j, active_target;
++		for (i = cur_num_buses - 1; i >= value; i--) {
++			active_target = -1;
++			for (j = 0; j < TARGETS_PER_BUS; j++) {
++				if (adapter->vbus[i]->vdev[j] && 
++				    !adapter->vbus[i]->vdev[j]->disabled) {
++					active_target = j;
++					break;
++				}
++			}
++			if (active_target != -1) {
++				err("Can't remove bus%d, target%d active\n", 
++					i, active_target);
++				return ;
++			}
++
++			set_num_targets(adapter->vbus[i], 0);
++
++			spin_lock_irqsave(&adapter->lock, flags);
++			atomic_dec(&adapter->num_buses);
++			kobject_unregister(&adapter->vbus[i]->kobj);
++			kfree(adapter->vbus[i]);
++			adapter->vbus[i] = NULL;
++			spin_unlock_irqrestore(&adapter->lock, flags);
++		}
++	}
++}
++
++
++/* Target sysfs stuff */
++static ATTR(target, type, 0644);
++static ATTR(target, device, 0644);
++static ATTR(target, active, 0644);
++static ATTR(target, ro, 0644);
++
++static ssize_t vscsi_target_show(struct kobject * kobj, struct attribute * attr, char * buf)
++{
++	struct vdev *vdev = container_of(kobj, struct vdev, kobj);
++	struct device *dev = container_of(kobj->parent->parent, struct device, kobj);
++	struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data;
++	unsigned long flags;
++	ssize_t returned= (ssize_t)0;
++
++	spin_lock_irqsave(&adapter->lock, flags);
++
++	if (attr == &vscsi_target_type_attr)
++		returned = sprintf(buf, "%c\n", vdev->type);
++	else if (attr == &vscsi_target_device_attr)
++		returned = sprintf(buf, "%s\n", vdev->b.device_name);
++	else if (attr == &vscsi_target_active_attr)
++		returned = sprintf(buf, "%d\n", !vdev->disabled);
++	else if (attr == &vscsi_target_ro_attr)
++		returned = sprintf(buf, "%d\n", vdev->b.ro);
++	else {
++		spin_unlock_irqrestore(&adapter->lock, flags);
++		BUG();
++	}
++
++	spin_unlock_irqrestore(&adapter->lock, flags);
++
++	return returned;
++}
++
++static ssize_t vscsi_target_store(struct kobject * kobj, struct attribute * attr, const char * buf, size_t count)
++{
++	struct vdev *vdev = container_of(kobj, struct vdev, kobj);
++	struct device *dev = container_of(kobj->parent->parent, struct device, kobj);
++	struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data;
++	long flags;
++	long value = simple_strtol(buf, NULL, 10);
++
++	if (attr != &vscsi_target_active_attr && !vdev->disabled) {
++		err("Error: Can't modify properties while target is active.\n");
++		return -EPERM;
++	}
++
++	if (attr == &vscsi_target_type_attr) {
++		if (buf[0] == 'B' ||  buf[0] == 'b')
++			vdev->type = 'B';
++		else if (buf[0] == 'S' || buf[0] == 's') {
++			// TODO
++			err ("SCSI mode not supported yet\n");		
++			return -EINVAL;
++		} else 
++			return -EINVAL;
++	} else if (attr == &vscsi_target_device_attr) {
++		int i;
++		spin_lock_irqsave(&adapter->lock, flags);
++		i  = strlcpy(vdev->b.device_name, buf, TARGET_MAX_NAME_LEN);
++		for (; i >= 0; i--)
++			if (vdev->b.device_name[i] == '\n')
++				vdev->b.device_name[i] = '\0';
++		spin_unlock_irqrestore(&adapter->lock, flags);
++	} else if (attr == &vscsi_target_active_attr) {
++		if (value) {
++			int rc;
++			if (!vdev->disabled) {
++				warn("Warning: Target was already active\n");
++				return -EINVAL;
++			}
++			if (vdev->type == '\0') {
++				err("Error: Type not specified\n");
++				return -EPERM;
++			}
++			rc = activate_block_device(vdev);
++			if (rc) {
++				err("Error opening block device=%d\n", rc);
++				return rc;
++			}
++		} else {
++			if (!vdev->disabled) 
++				deactivate_block_device(vdev);
++		}
++	} else if (attr == &vscsi_target_ro_attr)
++		vdev->b.ro = value > 0 ? 1 : 0;
++	else 
++		BUG();
++
++	return count;
++}
++
++static struct attribute * vscsi_target_attrs[] = {
++	&vscsi_target_type_attr,
++	&vscsi_target_device_attr,
++	&vscsi_target_active_attr,
++	&vscsi_target_ro_attr,
++	NULL,
++};
++
++static struct sysfs_ops vscsi_target_ops = {
++	.show   = vscsi_target_show,
++	.store  = vscsi_target_store,
++};
++
++static struct kobj_type ktype_vscsi_target = {
++	.release        = NULL,
++	.sysfs_ops      = &vscsi_target_ops, 
++	.default_attrs  = vscsi_target_attrs,
++};
++
++
++
++/* Bus sysfs stuff */
++static ssize_t vscsi_bus_show(struct kobject * kobj, struct attribute * attr, char * buf)
++{
++	struct vbus *vbus = container_of(kobj, struct vbus, kobj);
++	return sprintf(buf, "%d\n", atomic_read(&vbus->num_targets));
++}
++
++static ssize_t vscsi_bus_store(struct kobject * kobj, struct attribute * attr, 
++const char * buf, size_t count)
++{
++	struct vbus *vbus = container_of(kobj, struct vbus, kobj);
++	long value = simple_strtol(buf, NULL, 10);
++	
++	if (value < 0 || value > TARGETS_PER_BUS)
++		return -EINVAL;
++	
++	set_num_targets(vbus, value);
++
++	return count;
++}
++
++
++static ATTR(bus, num_targets, 0644);
++
++static struct attribute * vscsi_bus_attrs[] = {
++	&vscsi_bus_num_targets_attr,
++	NULL,
++};
++
++static struct sysfs_ops vscsi_bus_ops = {
++	.show   = vscsi_bus_show,
++	.store  = vscsi_bus_store,
++};
++
++static struct kobj_type ktype_vscsi_bus = {
++	.release        = NULL,
++	.sysfs_ops      = &vscsi_bus_ops, 
++	.default_attrs  = vscsi_bus_attrs,
++};
++
++
++/* Device attributes */
++static ssize_t vscsi_dev_bus_show(struct device * dev, char * buf)
++{
++	struct server_adapter *adapter = (struct server_adapter *)to_vio_dev(dev)->driver_data;
++
++	return sprintf(buf, "%d\n", atomic_read(&adapter->num_buses));
++}
++
++static ssize_t vscsi_dev_bus_store(struct device * dev, const char * buf, size_t count)
++{
++	long value = simple_strtol(buf, NULL, 10);
++	
++	if (value < 0 || value > BUS_PER_ADAPTER)
++		return -EINVAL;
++
++	set_num_buses(dev, value);
++	return count;
++}
++
++static DEVICE_ATTR(num_buses, 0644, vscsi_dev_bus_show, vscsi_dev_bus_store);
++
++
++/* Stats kobj stuff */
++
++static ATTR(stats, interrupts, 0444);
++static ATTR(stats, read_ops, 0444);
++static ATTR(stats, write_ops, 0444);
++static ATTR(stats, crq_msgs, 0444);
++static ATTR(stats, iu_allocs, 0444);
++static ATTR(stats, bio_allocs, 0444);
++static ATTR(stats, buf_allocs, 0444);
++static ATTR(stats, errors, 0444);
++
++static struct attribute * vscsi_stats_attrs[] = {
++	&vscsi_stats_interrupts_attr,
++	&vscsi_stats_read_ops_attr,
++	&vscsi_stats_write_ops_attr,
++	&vscsi_stats_crq_msgs_attr,
++	&vscsi_stats_iu_allocs_attr,
++	&vscsi_stats_bio_allocs_attr,
++	&vscsi_stats_buf_allocs_attr,
++	&vscsi_stats_errors_attr,
++	NULL,
++};
++
++
++static ssize_t vscsi_stats_show(struct kobject * kobj, struct attribute * attr, char * buf)
++{
++	struct server_adapter *adapter= container_of(kobj, struct server_adapter, stats_kobj);
++	if (attr == &vscsi_stats_interrupts_attr)
++		return sprintf(buf, "%d\n", 
++		 atomic_read(&adapter->interrupts));
++	if (attr == &vscsi_stats_read_ops_attr)
++		return sprintf(buf, "%d\n", 
++		 atomic_read(&adapter->read_processed));
++	if (attr == &vscsi_stats_write_ops_attr)
++		return sprintf(buf, "%d\n", 
++		 atomic_read(&adapter->write_processed));
++	if (attr == &vscsi_stats_crq_msgs_attr)
++		return sprintf(buf, "%d\n", 
++		 atomic_read(&adapter->crq_processed));
++	if (attr == &vscsi_stats_iu_allocs_attr)
++		return sprintf(buf, "%d\n", 
++		 atomic_read(&adapter->iu_count));
++	if (attr == &vscsi_stats_bio_allocs_attr)
++		return sprintf(buf, "%d\n", 
++		 atomic_read(&adapter->bio_count));
++	if (attr == &vscsi_stats_buf_allocs_attr)
++		return sprintf(buf, "%d\n", 
++		 atomic_read(&adapter->buffers_allocated));
++	if (attr == &vscsi_stats_errors_attr)
++		return sprintf(buf, "%d\n", 
++		 atomic_read(&adapter->errors));
++	
++	BUG();
++	return 0;
++}
++
++static struct sysfs_ops vscsi_stats_ops = {
++	.show   = vscsi_stats_show,
++	.store  = NULL,
++};
++
++static struct kobj_type ktype_vscsi_stats = {
++	.release        = NULL,
++	.sysfs_ops      = &vscsi_stats_ops, 
++	.default_attrs  = vscsi_stats_attrs,
++};
++
++
++static int ibmvscsis_probe(struct vio_dev *dev, const struct vio_device_id *id)
++{
++	struct server_adapter *adapter;
++	int rc;
++	unsigned int *dma_window;
++	unsigned int dma_window_property_size;
++
++	adapter = kmalloc(sizeof(*adapter), GFP_KERNEL);
++	if (!adapter) {
++		err("couldn't allocate adapter memory\n");
++		return -1;
++	}
++	memset(adapter, 0x00, sizeof(*adapter));
++	adapter->dma_dev = dev;
++	adapter->dev = &dev->dev;
++	dev->driver_data = adapter;
++	sprintf(adapter->name, "%x", dev->unit_address);
++	adapter->lock = SPIN_LOCK_UNLOCKED;
++
++	dma_window =
++	    (unsigned int *)vio_get_attribute(dev, "ibm,my-dma-window",
++					      &dma_window_property_size);
++	if (!dma_window) {
++		warn("Couldn't find ibm,my-dma-window property\n");
++	}
++
++	adapter->liobn = dma_window[0];
++	/* RPA docs say that #address-cells is always 1 for virtual
++	   devices, but some older boxes' OF returns 2.  This should
++	   be removed by GA, unless there is legacy OFs that still
++	   have 2 or 3 for #address-cells */
++	/*adapter->riobn = dma_window[2+vio_num_address_cells]; */
++
++	/* This is just an ugly kludge. Remove as soon as the OF for all
++	   machines actually follow the spec and encodes the offset field
++	   as phys-encode (that is, #address-cells wide) */
++	if (dma_window_property_size == 24) {
++		adapter->riobn = dma_window[3];
++	} else if (dma_window_property_size == 40) {
++		adapter->riobn = dma_window[5];
++	} else {
++		warn("Invalid size of ibm,my-dma-window=%i\n",
++		     dma_window_property_size);
++	}
++
++	INIT_WORK(&adapter->crq_task, crq_task, adapter);
++
++	tasklet_init(&adapter->endio_tasklet,
++		     endio_task, (unsigned long)adapter);
++
++	INIT_LIST_HEAD(&adapter->inflight);
++
++	/* Initialize the buffer cache */
++	init_data_buffer(adapter);
++
++	/* Arbitrarily support 16 IUs right now */
++	rc = initialize_iu_pool(adapter, 16);
++	if (rc) {
++		kfree(adapter);
++		return rc;
++	}
++
++	rc = initialize_crq_queue(&adapter->queue, adapter);
++	if (rc != 0) {
++		kfree(adapter);
++		return rc;
++	}
++
++	set_num_buses(&dev->dev, 1);
++	device_create_file(&dev->dev, &dev_attr_num_buses);
++
++	adapter->stats_kobj.parent = &dev->dev.kobj;
++	strcpy(adapter->stats_kobj.name, "stats");
++	adapter->stats_kobj.ktype = & ktype_vscsi_stats;
++	kobject_register(&adapter->stats_kobj);
++
++	return 0;
++}
++
++static int ibmvscsis_remove(struct vio_dev *dev)
++{
++	int bus;
++	int target;
++	unsigned long flags;
++	struct server_adapter *adapter =
++	    (struct server_adapter *)dev->driver_data;
++
++	spin_lock_irqsave(&adapter->lock, flags);
++
++	/* 
++	 * Loop through the bus
++	 */
++	for (bus = 0; bus < BUS_PER_ADAPTER; bus++) {
++		/* If this bus exists */
++		if (adapter->vbus[bus]) {
++			/* loop through the targets */
++			for (target = 0; target < TARGETS_PER_BUS; target++) {
++				/* If the target exists */
++				if (adapter->vbus[bus]->vdev[target] &&
++				    !adapter->vbus[bus]->vdev[target]
++				     ->disabled) {
++					deactivate_block_device(adapter->
++					 vbus[bus]->vdev[target]);
++				}
++			}
++			spin_unlock_irqrestore(&adapter->lock, flags);
++			set_num_targets(adapter->vbus[bus], 0);
++			spin_lock_irqsave(&adapter->lock, flags);
++		}
++	}
++
++	spin_unlock_irqrestore(&adapter->lock, flags);
++	set_num_buses(adapter->dev, 0);
++	release_crq_queue(&adapter->queue, adapter);
++
++	release_iu_pool(adapter);
++
++	release_data_buffer(adapter);
++
++	kobject_unregister(&adapter->stats_kobj);
++	device_remove_file(&dev->dev, &dev_attr_num_buses);
++
++	kfree(adapter);
++
++	return 0;
++}
++
++static struct vio_device_id ibmvscsis_device_table[] __devinitdata = {
++	{"v-scsi-host", "IBM,v-scsi-host"},
++	{0,}
++};
++
++MODULE_DEVICE_TABLE(vio, ibmvscsis_device_table);
++
++static struct vio_driver ibmvscsis_driver = {
++	.name = "ibmvscsis",
++	.id_table = ibmvscsis_device_table,
++	.probe = ibmvscsis_probe,
++	.remove = ibmvscsis_remove,
++};
++
++static int mod_init(void)
++{
++	struct device_node *rootdn;
++	char *ppartition_name;
++	char *psystem_id;
++	char *pmodel;
++	unsigned int *p_number_ptr;
++	int rc;
++
++	/* Retrieve information about this partition */
++	rootdn = find_path_device("/");
++	if (rootdn) {
++		pmodel = get_property(rootdn, "model", NULL);
++		psystem_id = get_property(rootdn, "system-id", NULL);
++		if (pmodel && psystem_id) 
++			snprintf(system_id,sizeof(system_id),
++				 "%s-%s",
++				 pmodel, psystem_id);
++		ppartition_name =
++			get_property(rootdn, "ibm,partition-name", NULL);
++		if (ppartition_name)
++			strncpy(partition_name, ppartition_name,
++				sizeof(partition_name));
++		p_number_ptr =
++			(unsigned int *)get_property(rootdn, "ibm,partition-no",
++						     NULL);
++		if (p_number_ptr)
++			partition_number = *p_number_ptr;
++	}
++
++	info("initialized version "IBMVSCSIS_VERSION"\n");
++
++	rc = vio_register_driver(&ibmvscsis_driver);
++
++	if (rc) {
++		warn("rc %d from vio_register_driver\n", rc);
++	}
++
++	return rc;
++}
++
++static void mod_exit(void)
++{
++	info("terminated\n");
++
++	vio_unregister_driver(&ibmvscsis_driver);
++}
++
++module_init(mod_init);
++module_exit(mod_exit);
+diff -Naur linux-2.6.11/drivers/scsi/ibmvscsi/Makefile linux-2.6.11.patched/drivers/scsi/ibmvscsi/Makefile
+--- linux-2.6.11/drivers/scsi/ibmvscsi/Makefile	2005-03-02 08:38:12.000000000 +0100
++++ linux-2.6.11.patched/drivers/scsi/ibmvscsi/Makefile	2005-03-09 17:39:34.000000000 +0100
+@@ -3,3 +3,5 @@
+ ibmvscsic-y			+= ibmvscsi.o
+ ibmvscsic-$(CONFIG_PPC_ISERIES)	+= iseries_vscsi.o 
+ ibmvscsic-$(CONFIG_PPC_PSERIES)	+= rpa_vscsi.o 
++
++obj-$(CONFIG_SCSI_IBMVSCSIS)    += ibmvscsis.o
+diff -Naur linux-2.6.11/drivers/scsi/Kconfig linux-2.6.11.patched/drivers/scsi/Kconfig
+--- linux-2.6.11/drivers/scsi/Kconfig	2005-03-02 08:38:25.000000000 +0100
++++ linux-2.6.11.patched/drivers/scsi/Kconfig	2005-03-09 17:38:53.000000000 +0100
+@@ -813,6 +813,14 @@
+ 	  To compile this driver as a module, choose M here: the
+ 	  module will be called ibmvscsic.
+ 
++config SCSI_IBMVSCSIS
++        tristate "IBM Virtual SCSI Server support"
++        depends on PPC_PSERIES
++        help
++          This is the IBM Virtual SCSI Server
++          To compile this driver as a module, choose M here: the
++          module will be called ibmvscsis.
++
+ config SCSI_INITIO
+ 	tristate "Initio 9100U(W) support"
+ 	depends on PCI && SCSI
+diff -Naur linux-2.6.11/include/asm-ppc64/vio.h linux-2.6.11.patched/include/asm-ppc64/vio.h
+--- linux-2.6.11/include/asm-ppc64/vio.h	2005-03-02 08:38:13.000000000 +0100
++++ linux-2.6.11.patched/include/asm-ppc64/vio.h	2005-03-09 23:11:45.000000000 +0100
+@@ -116,6 +116,7 @@
+ 	char *type;
+ 	uint32_t unit_address;	
+ 	unsigned int irq;
++	void *driver_data;
+ 
+ 	struct device dev;
+ };

Modified: trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/series/2.6.11-6
===================================================================
--- trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/series/2.6.11-6	2005-06-03 03:50:17 UTC (rev 3296)
+++ trunk/kernel/source/kernel-source-2.6.11-2.6.11/debian/patches/series/2.6.11-6	2005-06-03 15:44:13 UTC (rev 3297)
@@ -1,2 +1,4 @@
 + drivers-scsi-megaraid_splitup.patch
 + powerpc-ppc64-biarch-override.patch
++ powerpc-ppc64-ibmvscsi.patch
+