public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
From: "Adam Radford" <aradford@amcc.com>
To: Jeff Garzik <jgarzik@pobox.com>
Cc: akpm@osdl.org, james.bottomley@steeleye.com, hch@lst.de,
	linux-scsi@vger.kernel.org
Subject: RE: [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver v2.26.00.009
Date: Wed, 26 May 2004 14:59:36 -0700	[thread overview]
Message-ID: <HYCD4000.31K@hadar.amcc.com> (raw)

Jeff,

Thanks for the useful feedback.

- The card can take up to 256 io's at a time, but 2 are marked off
  for internal use.  There are no 'per device' limits.  The firmware queue
  depth is 256 total for all devices.
- PAE fix involves removing #ifdef BITS_PER_LONG's, and using sizeof(dma_addr_t)
  or some such mechanism to tell the firmware to use 64-bit sgl mode even when
  sizeof(long) = 4 bytes.
- We are not calling do_gettimeofday() on every command sent to hardware.  It is
  only sent when firmware requests an AEN_SYNC_TIME, which happens at startup, and
  probably once a day thereafter.  This is so we can have scheduled events in the
  firmware, such as rebuild events, scrub events, etc, _without_ an annoying daemon
  running.  There is clock drift so we periodically sync the time.
- The ioctl routine _does_ copy down data buffers.  This isn't in the main IO path.
- twa_poll_status_gone() is called to prevent return from a function until the
  hardware is ready.  How else do you propose delaying when the scsi midlayer called
  your entrypoint with spin_lock_irqsave(host->host_lock, flags) ?

I will fix some of the issues you mention and re-send.  Thanks!

--
Adam Radford
Staff Software Engineer
AMCC


-----Original Message-----
From: Jeff Garzik [mailto:jgarzik@pobox.com]
Sent: Wednesday, May 26, 2004 2:42 PM
To: Adam Radford
Cc: akpm@osdl.org; james.bottomley@steeleye.com; hch@lst.de;
linux-scsi@vger.kernel.org
Subject: Re: [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver v2.26.00.009


Adam Radford wrote:


Overall:  your driver duplicates some SCSI infrastructure for sending
SCSI commands, and waiting for their replies.



> Changes in this release:
>    - Convert driver to pci_driver format.

yay!


>    - Return SCSI_MLQUEUE_HOST_BUSY when card status is busy.

Does the hardware have some sort of metric, like "queued commands < 32",
to better determine when card is busy?

Normally, you want to set up the system such that SCSI stops calling
your ->queuecommand() hook when there is no more room.

SCSI layer provides device-wide and host-wide queueing limits.


> Future versions may include:
>    - PAE fix

What does this involve?


>    - 80 column wrap fixes
>    - Remove more prototypes by re-ordering functions.

Bah, who cares...  Maintainer's choice, IMO.



> diff -Naur linux-2.6.6-mm4/drivers/scsi/3w-9xxx.c linux-2.6.6-mm5/drivers/scsi/3w-9xxx.c
> --- linux-2.6.6-mm4/drivers/scsi/3w-9xxx.c	1969-12-31 16:00:00.000000000 -0800
> +++ linux-2.6.6-mm5/drivers/scsi/3w-9xxx.c	2004-05-26 13:17:24.000000000 -0700
> @@ -0,0 +1,2144 @@
> +/*
> +   3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux.
> +
> +   Written By: Adam Radford <linuxraid@amcc.com>
> +
> +   Copyright (C) 2004 Applied Micro Circuits Corporation.
> +
> +   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; version 2 of the License.
> +
> +   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.
> +
> +   NO WARRANTY
> +   THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
> +   CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
> +   LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
> +   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
> +   solely responsible for determining the appropriateness of using and
> +   distributing the Program and assumes all risks associated with its
> +   exercise of rights under this Agreement, including but not limited to
> +   the risks and costs of program errors, damage to or loss of data,
> +   programs or equipment, and unavailability or interruption of operations.
> +
> +   DISCLAIMER OF LIABILITY
> +   NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
> +   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> +   DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
> +   HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
> +
> +   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
> +
> +   Bugs/Comments/Suggestions should be mailed to:
> +   linuxraid@amcc.com
> +
> +   For more information, goto:
> +   http://www.amcc.com
> +
> +   Note: This version of the driver does not contain a bundled firmware
> +         image.
> +
> +   History
> +   -------
> +   2.26.00.005 - Initial release.
> +   2.26.00.006 - Remove TW_WRITEL macro, replace with writeq().
> +   2.26.00.007 - Skip lun and channel probes.
> +   2.26.00.008 - Fix bug in attention/command interrupt handling.
> +   2.26.00.009 - Convert driver to pci_driver format.
> +                 Remove proc interface, add sysfs attributes.
> +                 Return SCSI_MLQUEUE_HOST_BUSY when card status is busy.
> +*/
> +
> +#include <linux/module.h>
> +#include <linux/reboot.h>
> +#include <linux/spinlock.h>
> +#include <linux/interrupt.h>
> +#include <linux/moduleparam.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/delay.h>
> +#include <linux/pci.h>
> +#include <linux/time.h>
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/uaccess.h>
> +#include <scsi/scsi.h>
> +#include <scsi/scsi_host.h>
> +#include <scsi/scsi_tcq.h>
> +#include <scsi/scsi_cmnd.h>
> +#include "3w-9xxx.h"
> +
> +/* Globals */
> +char *twa_driver_version="2.26.00.009";

mark 'static', possibly 'const' too.


> +TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];

static


> +static unsigned int twa_device_extension_count;
> +static int twa_major = -1;
> +extern struct timezone sys_tz;
> +static int cmds_per_lun;
> +
> +/* Module parameters */
> +MODULE_AUTHOR ("AMCC");
> +MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver");
> +MODULE_LICENSE("GPL");
> +module_param(cmds_per_lun, int, 0);
> +MODULE_PARM_DESC(cmds_per_lun, "Maximum commands per LUN");
> +
> +/* Function prototypes */
> +static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int check_reset);
> +static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header);
> +static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id);
> +static char *twa_aen_severity_lookup(unsigned char severity_code);
> +static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which);
> +static int twa_check_bits(u32 status_reg_value);
> +static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed);
> +static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
> +static int twa_chrdev_open(struct inode *inode, struct file *file);
> +static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value);
> +static int twa_empty_response_queue(TW_Device_Extension *tw_dev);
> +static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host);
> +static void twa_free_device_extension(TW_Device_Extension *tw_dev);
> +static void twa_free_request_id(TW_Device_Extension *tw_dev,int request_id);
> +static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes);
> +static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id);
> +static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
> + 			      u32 set_features, unsigned short current_fw_srl,
> +			      unsigned short current_fw_arch_id,
> +			      unsigned short current_fw_branch,
> +			      unsigned short current_fw_build,
> +			      unsigned short *fw_on_ctlr_srl,
> +			      unsigned short *fw_on_ctlr_arch_id,
> +			      unsigned short *fw_on_ctlr_branch,
> +			      unsigned short *fw_on_ctlr_build,
> +			      u32 *init_connect_result);
> +static int twa_initialize_device_extension(TW_Device_Extension *tw_dev);
> +static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
> +static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length);
> +static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id);
> +static dma_addr_t twa_map_scsi_single_data(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds);
> +static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds);
> +static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds);
> +static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal);
> +static int twa_reset_device_extension(TW_Device_Extension *tw_dev);
> +static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset);
> +static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]);
> +static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id);
> +static int twa_scsi_eh_abort(struct scsi_cmnd *SCpnt);
> +static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt);
> +static int twa_scsi_queue(struct scsi_cmnd *cmd, void (*done) (struct scsi_cmnd *));
> +static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Apache *sglistarg);
> +static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id);
> +static int twa_setup_irq(TW_Device_Extension *tw_dev);
> +static int twa_slave_configure(struct scsi_device *SDptr);
> +static char *twa_string_lookup(twa_message_type *table, unsigned int aen_code);
> +static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id);
> +
> +/* Functions */
> +
> +/* Show some statistics about the card */
> +static ssize_t twa_show_stats(struct class_device *class_dev, char *buf)
> +{
> +	struct Scsi_Host *host = class_to_shost(class_dev);
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
> +	unsigned long flags = 0;
> +	ssize_t len;
> +
> +	spin_lock_irqsave(tw_dev->host->host_lock, flags);
> +	len = snprintf(buf, PAGE_SIZE, "Driver version: %s\n"
> +		       "Current commands posted:   %4d\n"
> +		       "Max commands posted:       %4d\n"
> +		       "Current pending commands:  %4d\n"
> +		       "Max pending commands:      %4d\n"
> +		       "Last sgl length:           %4d\n"
> +		       "Max sgl length:            %4d\n"
> +		       "Last sector count:         %4d\n"
> +		       "Max sector count:          %4d\n"
> +		       "SCSI Host Resets:          %4d\n"
> +		       "SCSI Aborts/Timeouts:      %4d\n"
> +		       "AEN's:                     %4d\n",
> +		       twa_driver_version,
> +		       tw_dev->posted_request_count,
> +		       tw_dev->max_posted_request_count,
> +		       tw_dev->pending_request_count,
> +		       tw_dev->max_pending_request_count,
> +		       tw_dev->sgl_entries,
> +		       tw_dev->max_sgl_entries,
> +		       tw_dev->sector_count,
> +		       tw_dev->max_sector_count,
> +		       tw_dev->num_resets,
> +		       tw_dev->num_aborts,
> +		       tw_dev->aen_count);
> +	spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
> +	return len;
> +} /* End twa_show_stats() */

surely sysfs allows use of seq_file API???


> +/* File operations struct for character device */
> +static struct file_operations twa_fops = {
> +	.owner		= THIS_MODULE,
> +	.ioctl		= twa_chrdev_ioctl,
> +	.open		= twa_chrdev_open,
> +	.release	= NULL
> +};
> +
> +/* This function will complete an aen request from the isr */
> +static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id)
> +{
> +	TW_Command_Full *full_command_packet;
> +	TW_Command *command_packet;
> +	TW_Command_Apache_Header *header;
> +	unsigned short aen;
> +	int retval = 1;
> +
> +	header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
> +	tw_dev->posted_request_count--;
> +	aen = header->status_block.error;
> +	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
> +	command_packet = &full_command_packet->command.oldcommand;

kill unneeded casts


> +	/* First check for internal completion of set param for time sync */
> +	if (command_packet->byte0_offset.opcode == TW_OP_SET_PARAM) {
> +		/* Keep reading the queue in case there are more aen's */
> +		if (twa_aen_read_queue(tw_dev, request_id))
> +			goto out2;
> +	        else {
> +			retval = 0;
> +			goto out;
> +		}
> +	}
> +
> +	switch (aen) {
> +	case TW_AEN_QUEUE_EMPTY:
> +		/* Quit reading the queue if this is the last one */
> +		break;
> +	case TW_AEN_SYNC_TIME_WITH_HOST:
> +		twa_aen_sync_time(tw_dev, request_id);
> +		retval = 0;
> +		goto out;
> +	default:
> +		twa_aen_queue_event(tw_dev, header);
> +
> +		/* If there are more aen's, keep reading the queue */
> +		if (twa_aen_read_queue(tw_dev, request_id))
> +			goto out2;
> +		else {
> +			retval = 0;
> +			goto out;
> +		}
> +	}
> +	retval = 0;
> +out2:
> +	tw_dev->state[request_id] = TW_S_COMPLETED;
> +	twa_free_request_id(tw_dev, request_id);
> +	clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
> +out:
> +	return retval;
> +} /* End twa_aen_complete() */
> +
> +/* This function will drain aen queue */
> +static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
> +{
> +	int request_id = 0;
> +	char cdb[TW_MAX_CDB_LEN];
> +	TW_SG_Apache sglist[1];
> +	int finished = 0, count = 0;
> +	TW_Command_Full *full_command_packet;
> +	TW_Command_Apache_Header *header;
> +	unsigned short aen;
> +	int first_reset = 0, queue = 0, retval = 1;
> +
> +	if (no_check_reset)
> +		first_reset = 0;
> +	else
> +		first_reset = 1;
> +
> +	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
> +	memset(full_command_packet, 0, sizeof(TW_Command_Full));
> +
> +	/* Initialize cdb */
> +	memset(&cdb, 0, TW_MAX_CDB_LEN);
> +	cdb[0] = REQUEST_SENSE; /* opcode */
> +	cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
> +
> +	/* Initialize sglist */
> +	memset(&sglist, 0, sizeof(TW_SG_Apache));
> +	sglist[0].length = TW_SECTOR_SIZE;
> +	sglist[0].address = tw_dev->generic_buffer_phys[request_id];
> +
> +	if (sglist[0].address & TW_ALIGNMENT_9000_SGL) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Found unaligned address during AEN drain");
> +		goto out;
> +	}
> +
> +	/* Mark internal command */
> +	tw_dev->srb[request_id] = NULL;
> +
> +	do {
> +		/* Send command to the board */
> +		if (twa_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
> +			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Error posting request sense");
> +			goto out;
> +		}
> +
> +		/* Now poll for completion */
> +		if (twa_poll_response(tw_dev, request_id, 30)) {
> +			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "No valid response while draining AEN queue");
> +			tw_dev->posted_request_count--;
> +			goto out;
> +		}
> +
> +		tw_dev->posted_request_count--;
> +		header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
> +		aen = header->status_block.error;
> +		queue = 0;
> +		count++;
> +
> +		switch (aen) {
> +		case TW_AEN_QUEUE_EMPTY:
> +			if (first_reset != 1)
> +				goto out;
> +			else
> +				finished = 1;
> +			break;
> +		case TW_AEN_SOFT_RESET:
> +			if (first_reset == 0)
> +				first_reset = 1;
> +			else
> +				queue = 1;
> +			break;
> +		case TW_AEN_SYNC_TIME_WITH_HOST:
> +			break;
> +		default:
> +			queue = 1;
> +		}
> +
> +		/* Now queue an event info */
> +		if (queue)
> +			twa_aen_queue_event(tw_dev, header);
> +	} while ((finished == 0) && (count < TW_MAX_AEN_DRAIN));
> +
> +	if (count == TW_MAX_AEN_DRAIN)
> +		goto out;
> +
> +	retval = 0;
> +out:
> +	tw_dev->state[request_id] = TW_S_INITIAL;
> +	return retval;
> +} /* End twa_aen_drain_queue() */
> +
> +/* This function will queue an event */
> +static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header)
> +{
> +	u32 local_time;
> +	struct timeval time;
> +	TW_Event *event;
> +	unsigned short aen;
> +	char host[16];
> +
> +	tw_dev->aen_count++;
> +
> +	/* Fill out event info */
> +	event = tw_dev->event_queue[tw_dev->error_index];
> +
> +	/* Check for clobber */
> +	host[0] = '\0';
> +	if (tw_dev->host) {
> +		sprintf(host, " scsi%d:", tw_dev->host->host_no);
> +		if (event->retrieved == TW_AEN_NOT_RETRIEVED)
> +			tw_dev->aen_clobber = 1;
> +	}
> +
> +	aen = header->status_block.error;
> +	memset(event, 0, sizeof(TW_Event));
> +
> +	event->severity = header->status_block.substatus_block.severity;
> +	do_gettimeofday(&time);

are we really calling do_gettimeofday() on every command sent to
hardware?  Can this be avoided?


> +	local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
> +	event->time_stamp_sec = local_time;
> +	event->aen_code = aen;
> +	event->retrieved = TW_AEN_NOT_RETRIEVED;
> +	event->sequence_id = tw_dev->error_sequence_id;
> +	tw_dev->error_sequence_id++;
> +
> +	header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
> +	event->parameter_len = strlen(header->err_specific_desc);
> +	memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len);
> +	if (event->severity != TW_AEN_SEVERITY_DEBUG)
> +		printk(KERN_WARNING "3w-9xxx:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n", host, twa_aen_severity_lookup(header->status_block.substatus_block.severity), TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen, twa_string_lookup(twa_aen_table, aen), header->err_specific_desc);
> +	else
> +		tw_dev->aen_count--;
> +
> +	if ((tw_dev->error_index + 1) == TW_Q_LENGTH)
> +		tw_dev->event_queue_wrapped = 1;
> +	tw_dev->error_index = (tw_dev->error_index + 1 ) % TW_Q_LENGTH;
> +} /* End twa_aen_queue_event() */
> +
> +/* This function will read the aen queue from the isr */
> +static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
> +{
> +	char cdb[TW_MAX_CDB_LEN];
> +	TW_SG_Apache sglist[1];
> +	TW_Command_Full *full_command_packet;
> +	int retval = 1;
> +
> +	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
> +	memset(full_command_packet, 0, sizeof(TW_Command_Full));
> +
> +	/* Initialize cdb */
> +	memset(&cdb, 0, TW_MAX_CDB_LEN);
> +	cdb[0] = REQUEST_SENSE; /* opcode */
> +	cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
> +
> +	/* Initialize sglist */
> +	memset(&sglist, 0, sizeof(TW_SG_Apache));
> +	sglist[0].length = TW_SECTOR_SIZE;
> +	sglist[0].address = tw_dev->generic_buffer_phys[request_id];
> +
> +	/* Mark internal command */
> +	tw_dev->srb[request_id] = NULL;
> +
> +	/* Now post the command packet */
> +	if (twa_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "Post failed while reading AEN queue");
> +		goto out;
> +	}
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_aen_read_queue() */
> +
> +/* This function will look up an AEN severity string */
> +static char *twa_aen_severity_lookup(unsigned char severity_code)
> +{
> +	char *retval = NULL;
> +
> +	if ((severity_code < (unsigned char) TW_AEN_SEVERITY_ERROR) ||
> +	    (severity_code > (unsigned char) TW_AEN_SEVERITY_DEBUG))
> +		goto out;
> +
> +	retval = twa_aen_severity_table[severity_code];
> +out:
> +	return retval;
> +} /* End twa_aen_severity_lookup() */
> +
> +/* This function will sync firmware time with the host time */
> +static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
> +{
> +	u32 schedulertime;
> +	struct timeval utc;
> +	TW_Command_Full *full_command_packet;
> +	TW_Command *command_packet;
> +	TW_Param_Apache *param;
> +	u32 local_time;
> +
> +	/* Fill out the command packet */
> +	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
> +	memset(full_command_packet, 0, sizeof(TW_Command_Full));
> +	command_packet = &full_command_packet->command.oldcommand;
> +	command_packet->byte0_offset.opcode = TW_OP_SET_PARAM;
> +	command_packet->byte0_offset.sgl_offset = 2;
> +	command_packet->request_id = request_id;
> +	command_packet->byte8_offset.param.sgl[0].address = tw_dev->generic_buffer_phys[request_id];
> +	command_packet->byte8_offset.param.sgl[0].length = TW_SECTOR_SIZE;
> +	command_packet->size = TW_COMMAND_SIZE;
> +	command_packet->byte6_offset.parameter_count = 1;
> +
> +	/* Setup the param */
> +	param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
> +	memset(param, 0, TW_SECTOR_SIZE);
> +	param->table_id = TW_TIMEKEEP_TABLE | 0x8000; /* Controller time keep table */
> +	param->parameter_id = 0x3; /* SchedulerTime */
> +	param->parameter_size_bytes = 4;
> +
> +	/* Convert system time in UTC to local time seconds since last
> +           Sunday 12:00AM */
> +	do_gettimeofday(&utc);
> +	local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60));
> +	schedulertime = local_time - (3 * 86400);
> +	schedulertime = schedulertime % 604800;
> +
> +	memcpy(param->data, &schedulertime, sizeof(u32));
> +
> +	/* Mark internal command */
> +	tw_dev->srb[request_id] = NULL;
> +
> +	/* Now post the command */
> +	twa_post_command_packet(tw_dev, request_id, 1);
> +} /* End twa_aen_sync_time() */
> +
> +/* This function will allocate memory and check if it is correctly aligned */
> +static int twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
> +{
> +	int i;
> +	dma_addr_t dma_handle;
> +	unsigned long *cpu_addr;
> +	int retval = 1;
> +
> +	cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle);
> +	if (!cpu_addr) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed");
> +		goto out;
> +	}
> +
> +	if ((unsigned long)cpu_addr % (TW_ALIGNMENT_9000)) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x6, "Failed to allocate correctly aligned memory");
> +		pci_free_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, cpu_addr, dma_handle);
> +		goto out;
> +	}
> +
> +	memset(cpu_addr, 0, size*TW_Q_LENGTH);
> +
> +	for (i = 0; i < TW_Q_LENGTH; i++) {
> +		switch(which) {
> +		case 0:
> +			tw_dev->command_packet_phys[i] = dma_handle+(i*size);
> +			tw_dev->command_packet_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
> +			break;
> +		case 1:
> +			tw_dev->generic_buffer_phys[i] = dma_handle+(i*size);
> +			tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
> +			break;
> +		}
> +	}
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_allocate_memory() */
> +
> +/* This function will check the status register for unexpected bits */
> +static int twa_check_bits(u32 status_reg_value)
> +{
> +	int retval = 1;
> +
> +	if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS)
> +		goto out;
> +	if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0)
> +		goto out;
> +
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_check_bits() */
> +
> +/* This function will check the srl and decide if we are compatible  */
> +static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed)
> +{
> +	int retval = 1;
> +	unsigned short fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0;
> +	unsigned short fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0;
> +	u32 init_connect_result = 0;
> +
> +	if (twa_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS, TW_EXTENDED_INIT_CONNECT, TW_CURRENT_FW_SRL, TW_9000_ARCH_ID, TW_CURRENT_FW_BRANCH, TW_CURRENT_FW_BUILD, &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result)) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x7, "Initconnection failed while checking SRL");
> +		goto out;
> +	}
> +
> +	tw_dev->working_srl = TW_CURRENT_FW_SRL;
> +	tw_dev->working_branch = TW_CURRENT_FW_BRANCH;
> +	tw_dev->working_build = TW_CURRENT_FW_BUILD;
> +
> +	/* Try base mode compatibility */
> +	if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) {
> +		if (twa_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS, TW_EXTENDED_INIT_CONNECT, TW_BASE_FW_SRL, TW_9000_ARCH_ID, TW_BASE_FW_BRANCH, TW_BASE_FW_BUILD, &fw_on_ctlr_srl, &fw_on_ctlr_arch_id, &fw_on_ctlr_branch, &fw_on_ctlr_build, &init_connect_result)) {
> +			TW_PRINTK(tw_dev->host, TW_DRIVER, 0xa, "Initconnection (base mode) failed while checking SRL");
> +			goto out;
> +		}
> +		if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) {
> +			if (TW_CURRENT_FW_SRL > fw_on_ctlr_srl) {
> +				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x32, "Firmware and driver incompatibility: please upgrade firmware");
> +			} else {
> +				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x33, "Firmware and driver incompatibility: please upgrade driver");
> +			}
> +			goto out;
> +		}
> +		tw_dev->working_srl = TW_BASE_FW_SRL;
> +		tw_dev->working_branch = TW_BASE_FW_BRANCH;
> +		tw_dev->working_build = TW_BASE_FW_BUILD;
> +	}
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_check_srl() */
> +
> +/* This function handles ioctl for the character device */
> +static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
> +{
> +	long timeout;
> +	unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0;
> +	dma_addr_t dma_handle;
> +	int request_id = 0;
> +	unsigned int sequence_id = 0;
> +	unsigned char event_index, start_index;
> +	TW_Ioctl_Driver_Command driver_command;
> +	TW_Ioctl_Buf_Apache *tw_ioctl;
> +	TW_Lock *tw_lock;
> +	TW_Command_Full *full_command_packet;
> +	TW_Compatibility_Info *tw_compat_info;
> +	TW_Event *event;
> +	struct timeval current_time;
> +	u32 current_time_ms;
> +	TW_Device_Extension *tw_dev = twa_device_extension_list[iminor(inode)];
> +	int retval = TW_IOCTL_ERROR_OS_EFAULT;
> +
> +	/* Only let one of these through at a time */
> +	if (down_interruptible(&tw_dev->ioctl_sem)) {
> +		retval = TW_IOCTL_ERROR_OS_EINTR;
> +		goto out;
> +	}
> +
> +	/* First copy down the driver command */
> +	if (copy_from_user(&driver_command, (void *)arg, sizeof(TW_Ioctl_Driver_Command)))
> +		goto out2;
> +
> +	/* Check data buffer size */
> +	if (driver_command.buffer_length > TW_MAX_SECTORS * 512) {
> +		retval = TW_IOCTL_ERROR_OS_EINVAL;
> +		goto out2;
> +	}
> +
> +	/* Hardware can only do multiple of 512 byte transfers */
> +	data_buffer_length_adjusted = (driver_command.buffer_length + 511) & ~511;

why not return -EINVAL and require userland to align?


> +	/* Now allocate ioctl buf memory */
> +	cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, &dma_handle);
> +	if (!cpu_addr) {
> +		retval = TW_IOCTL_ERROR_OS_ENOMEM;
> +		goto out2;
> +	}
> +
> +	tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr;
> +
> +	/* Now copy down the entire ioctl */
> +	if (copy_from_user(tw_ioctl, (void *)arg, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1))
> +		goto out3;

are you really copying all the user data buffers here?

That will be godawful slow.

I would suggest building and sending a struct request instead.


> +	/* See which ioctl we are doing */
> +	switch (cmd) {
> +	case TW_IOCTL_FIRMWARE_PASS_THROUGH:
> +		spin_lock_irqsave(tw_dev->host->host_lock, flags);
> +		twa_get_request_id(tw_dev, &request_id);
> +
> +		/* Flag internal command */
> +		tw_dev->srb[request_id] = 0;
> +
> +		/* Flag chrdev ioctl */
> +		tw_dev->chrdev_request_id = request_id;
> +
> +		full_command_packet = &tw_ioctl->firmware_command;
> +
> +		/* Load request id and sglist for both command types */
> +		twa_load_sgl(full_command_packet, request_id, dma_handle, data_buffer_length_adjusted);
> +
> +		memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full));
> +
> +		/* Now post the command packet to the controller */
> +		twa_post_command_packet(tw_dev, request_id, 1);
> +		spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
> +
> +		timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
> +
> +		/* Now wait for command to complete */
> +		timeout = wait_event_interruptible_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);

surely there is a better way to do this... semaphore?

down() here, and up() in irq handler or timer.


> +		/* Check if we timed out, got a signal, or didn't get
> +                   an interrupt */
> +		if ((timeout <= 0) && (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE)) {
> +			/* Now we need to reset the board */
> +			if (timeout == TW_IOCTL_ERROR_OS_ERESTARTSYS) {
> +				retval = timeout;
> +			} else {
> +				printk(KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, TW_DRIVER, 0xc, cmd);
> +				retval = TW_IOCTL_ERROR_OS_EIO;
> +			}
> +			spin_lock_irqsave(tw_dev->host->host_lock, flags);
> +			tw_dev->state[request_id] = TW_S_COMPLETED;
> +			twa_free_request_id(tw_dev, request_id);
> +			tw_dev->posted_request_count--;
> +			twa_reset_device_extension(tw_dev);
> +			spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
> +			goto out3;
> +		}
> +
> +		/* Now copy in the command packet response */
> +		memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full));
> +
> +		/* Now complete the io */
> +		spin_lock_irqsave(tw_dev->host->host_lock, flags);
> +		tw_dev->posted_request_count--;
> +		tw_dev->state[request_id] = TW_S_COMPLETED;
> +		twa_free_request_id(tw_dev, request_id);
> +		spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
> +		break;
> +	case TW_IOCTL_GET_COMPATIBILITY_INFO:
> +		tw_ioctl->driver_command.status = 0;
> +		/* Copy compatiblity struct into ioctl data buffer */
> +		tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer;
> +		strncpy(tw_compat_info->driver_version, twa_driver_version, strlen(twa_driver_version));
> +		tw_compat_info->working_srl = tw_dev->working_srl;
> +		tw_compat_info->working_branch = tw_dev->working_branch;
> +		tw_compat_info->working_build = tw_dev->working_build;
> +		break;
> +	case TW_IOCTL_GET_LAST_EVENT:
> +		if (tw_dev->event_queue_wrapped) {
> +			if (tw_dev->aen_clobber) {
> +				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
> +				tw_dev->aen_clobber = 0;
> +			} else
> +				tw_ioctl->driver_command.status = 0;
> +		} else {
> +			if (!tw_dev->error_index) {
> +				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
> +				break;
> +			}
> +			tw_ioctl->driver_command.status = 0;
> +		}
> +		event_index = (tw_dev->error_index - 1 + TW_Q_LENGTH) % TW_Q_LENGTH;
> +		memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
> +		tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
> +		break;
> +	case TW_IOCTL_GET_FIRST_EVENT:
> +		if (tw_dev->event_queue_wrapped) {
> +			if (tw_dev->aen_clobber) {
> +				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
> +				tw_dev->aen_clobber = 0;
> +			} else
> +				tw_ioctl->driver_command.status = 0;
> +			event_index = tw_dev->error_index;
> +		} else {
> +			if (!tw_dev->error_index) {
> +				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
> +				break;
> +			}
> +			tw_ioctl->driver_command.status = 0;
> +			event_index = 0;
> +		}
> +		memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
> +		tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
> +		break;
> +	case TW_IOCTL_GET_NEXT_EVENT:
> +		event = (TW_Event *)tw_ioctl->data_buffer;
> +		sequence_id = event->sequence_id;
> +		tw_ioctl->driver_command.status = 0;
> +
> +		if (tw_dev->event_queue_wrapped) {
> +			if (tw_dev->aen_clobber) {
> +				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
> +				tw_dev->aen_clobber = 0;
> +			}
> +			start_index = tw_dev->error_index;
> +		} else {
> +			if (!tw_dev->error_index) {
> +				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
> +				break;
> +			}
> +			start_index = 0;
> +		}
> +		event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id + 1) % TW_Q_LENGTH;
> +
> +		if (!(tw_dev->event_queue[event_index]->sequence_id > sequence_id)) {
> +			if (tw_ioctl->driver_command.status == TW_IOCTL_ERROR_STATUS_AEN_CLOBBER)
> +				tw_dev->aen_clobber = 1;
> +			tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
> +			break;
> +		}
> +		memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
> +		tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
> +		break;
> +	case TW_IOCTL_GET_PREVIOUS_EVENT:
> +		event = (TW_Event *)tw_ioctl->data_buffer;
> +		sequence_id = event->sequence_id;
> +		tw_ioctl->driver_command.status = 0;
> +
> +		if (tw_dev->event_queue_wrapped) {
> +			if (tw_dev->aen_clobber) {
> +				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_AEN_CLOBBER;
> +				tw_dev->aen_clobber = 0;
> +			}
> +			start_index = tw_dev->error_index;
> +		} else {
> +			if (!tw_dev->error_index) {
> +				tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
> +				break;
> +			}
> +			start_index = 0;
> +		}
> +		event_index = (start_index + sequence_id - tw_dev->event_queue[start_index]->sequence_id - 1) % TW_Q_LENGTH;
> +
> +		if (!(tw_dev->event_queue[event_index]->sequence_id < sequence_id)) {
> +			if (tw_ioctl->driver_command.status == TW_IOCTL_ERROR_STATUS_AEN_CLOBBER)
> +				tw_dev->aen_clobber = 1;
> +			tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NO_MORE_EVENTS;
> +			break;
> +		}
> +		memcpy(tw_ioctl->data_buffer, tw_dev->event_queue[event_index], sizeof(TW_Event));
> +		tw_dev->event_queue[event_index]->retrieved = TW_AEN_RETRIEVED;
> +		break;
> +	case TW_IOCTL_GET_LOCK:
> +		tw_lock = (TW_Lock *)tw_ioctl->data_buffer;
> +		do_gettimeofday(&current_time);
> +		current_time_ms = (current_time.tv_sec * 1000) + (current_time.tv_usec / 1000);
> +
> +		if ((tw_lock->force_flag == 1) || (tw_dev->ioctl_sem_lock == 0) || (current_time_ms >= tw_dev->ioctl_msec)) {
> +			tw_dev->ioctl_sem_lock = 1;
> +			tw_dev->ioctl_msec = current_time_ms + tw_lock->timeout_msec;
> +			tw_ioctl->driver_command.status = 0;
> +			tw_lock->time_remaining_msec = tw_lock->timeout_msec;
> +		} else {
> +			tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_LOCKED;
> +			tw_lock->time_remaining_msec = tw_dev->ioctl_msec - current_time_ms;
> +		}
> +		break;
> +	case TW_IOCTL_RELEASE_LOCK:
> +		if (tw_dev->ioctl_sem_lock == 1) {
> +			tw_dev->ioctl_sem_lock = 0;
> +			tw_ioctl->driver_command.status = 0;
> +		} else {
> +			tw_ioctl->driver_command.status = TW_IOCTL_ERROR_STATUS_NOT_LOCKED;
> +		}
> +		break;
> +	default:
> +		retval = TW_IOCTL_ERROR_OS_ENOTTY;
> +		goto out3;
> +	}
> +
> +	/* Now copy the entire response to userspace */
> +	if (copy_to_user((void *)arg, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0)
> +		retval = 0;
> +out3:
> +	/* Now free ioctl buf memory */
> +	pci_free_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, cpu_addr, dma_handle);
> +out2:
> +	up(&tw_dev->ioctl_sem);
> +out:
> +	return retval;
> +} /* End twa_chrdev_ioctl() */
> +
> +/* This function handles open for the character device */
> +static int twa_chrdev_open(struct inode *inode, struct file *file)
> +{
> +	unsigned int minor_number;
> +	int retval = TW_IOCTL_ERROR_OS_ENODEV;
> +
> +	minor_number = iminor(inode);
> +	if (minor_number >= twa_device_extension_count)
> +		goto out;
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_chrdev_open() */
> +
> +/* This function will print readable messages from status register errors */
> +static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value)
> +{
> +	int retval = 1;
> +
> +	/* Check for various error conditions and handle them appropriately */
> +	if (status_reg_value & TW_STATUS_PCI_PARITY_ERROR) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0xc, "PCI Parity Error: clearing");
> +		writel(TW_CONTROL_CLEAR_PARITY_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
> +	}
> +
> +	if (status_reg_value & TW_STATUS_PCI_ABORT) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0xd, "PCI Abort: clearing");
> +		writel(TW_CONTROL_CLEAR_PCI_ABORT, TW_CONTROL_REG_ADDR(tw_dev));
> +		pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT);
> +	}
> +
> +	if (status_reg_value & TW_STATUS_QUEUE_ERROR) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing");
> +		writel(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
> +	}
> +
> +	if (status_reg_value & TW_STATUS_SBUF_WRITE_ERROR) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0xf, "SBUF Write Error: clearing");
> +		writel(TW_CONTROL_CLEAR_SBUF_WRITE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
> +	}
> +
> +	if (status_reg_value & TW_STATUS_MICROCONTROLLER_ERROR) {
> +		if (tw_dev->reset_print == 0) {
> +			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x10, "Microcontroller Error: clearing");
> +			tw_dev->reset_print = 1;
> +		}
> +		goto out;
> +	}
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_decode_bits() */
> +
> +/* This function will empty the response queue */
> +static int twa_empty_response_queue(TW_Device_Extension *tw_dev)
> +{
> +	u32 status_reg_value, response_que_value;
> +	int count = 0, retval = 1;
> +
> +	status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
> +
> +	while (((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) && (count < TW_MAX_RESPONSE_DRAIN)) {
> +		response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
> +		status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
> +		count++;
> +	}
> +	if (count == TW_MAX_RESPONSE_DRAIN)
> +		goto out;
> +
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_empty_response_queue() */
> +
> +/* This function passes sense keys from firmware to scsi layer */
> +static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host)
> +{
> +	TW_Command_Full *full_command_packet;
> +	unsigned short error;
> +	int retval = 1;
> +
> +	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
> +	/* Don't print error for Logical unit not supported during rollcall */
> +	error = full_command_packet->header.status_block.error;
> +	if ((error != TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) && (error != TW_ERROR_UNIT_OFFLINE)) {
> +		if (print_host)
> +			printk(KERN_WARNING "3w-9xxx: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n", tw_dev->host->host_no, TW_MESSAGE_SOURCE_CONTROLLER_ERROR, full_command_packet->header.status_block.error, twa_string_lookup(twa_error_table, full_command_packet->header.status_block.error), full_command_packet->header.err_specific_desc);
> +		else
> +			printk(KERN_WARNING "3w-9xxx: ERROR: (0x%02X:0x%04X): %s:%s.\n", TW_MESSAGE_SOURCE_CONTROLLER_ERROR, full_command_packet->header.status_block.error, twa_string_lookup(twa_error_table, full_command_packet->header.status_block.error), full_command_packet->header.err_specific_desc);
> +	}
> +
> +	if (copy_sense) {
> +		memcpy(tw_dev->srb[request_id]->sense_buffer, full_command_packet->header.sense_data, TW_SENSE_DATA_LENGTH);
> +		tw_dev->srb[request_id]->result = (full_command_packet->command.newcommand.status << 1);
> +		retval = TW_ISR_DONT_RESULT;
> +		goto out;
> +	}
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_fill_sense() */
> +
> +/* This function will free up device extension resources */
> +static void twa_free_device_extension(TW_Device_Extension *tw_dev)
> +{
> +	if (tw_dev->command_packet_virt[0])
> +		pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Command_Full)*TW_Q_LENGTH, tw_dev->command_packet_virt[0], tw_dev->command_packet_phys[0]);
> +
> +	if (tw_dev->generic_buffer_virt[0])
> +		pci_free_consistent(tw_dev->tw_pci_dev, TW_SECTOR_SIZE*TW_Q_LENGTH, tw_dev->generic_buffer_virt[0], tw_dev->generic_buffer_phys[0]);
> +
> +	if (tw_dev->event_queue[0])
> +		kfree(tw_dev->event_queue[0]);
> +} /* End twa_free_device_extension() */
> +
> +/* This function will free a request id */
> +static void twa_free_request_id(TW_Device_Extension *tw_dev, int request_id)
> +{
> +	tw_dev->free_queue[tw_dev->free_tail] = request_id;
> +	tw_dev->state[request_id] = TW_S_FINISHED;
> +	tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
> +} /* End twa_free_request_id() */
> +
> +/* This function will get parameter table entires from the firmware */
> +static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes)
> +{
> +	TW_Command_Full *full_command_packet;
> +	TW_Command *command_packet;
> +	TW_Param_Apache *param;
> +	unsigned long param_value;
> +	void *retval = NULL;
> +
> +	/* Setup the command packet */
> +	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
> +	memset(full_command_packet, 0, sizeof(TW_Command_Full));
> +	command_packet = &full_command_packet->command.oldcommand;
> +
> +	command_packet->byte0_offset.opcode      = TW_OP_GET_PARAM;
> +	command_packet->byte0_offset.sgl_offset  = 2;
> +	command_packet->size              = TW_COMMAND_SIZE;
> +	command_packet->request_id        = request_id;
> +	command_packet->byte6_offset.block_count = 1;
> +
> +	/* Now setup the param */
> +	param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
> +	memset(param, 0, TW_SECTOR_SIZE);
> +	param->table_id = table_id | 0x8000;
> +	param->parameter_id = parameter_id;
> +	param->parameter_size_bytes = parameter_size_bytes;
> +	param_value = tw_dev->generic_buffer_phys[request_id];
> +
> +	command_packet->byte8_offset.param.sgl[0].address = param_value;
> +	command_packet->byte8_offset.param.sgl[0].length = TW_SECTOR_SIZE;
> +
> +	/* Post the command packet to the board */
> +	twa_post_command_packet(tw_dev, request_id, 1);
> +
> +	/* Poll for completion */
> +	if (twa_poll_response(tw_dev, request_id, 30))
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "No valid response during get param")
> +	else
> +		retval = (void *)&(param->data[0]);

just use standard SCSI code to issue a SCSI command, and wait for a
response.


> +	tw_dev->posted_request_count--;
> +	tw_dev->state[request_id] = TW_S_INITIAL;
> +
> +	return retval;
> +} /* End twa_get_param() */
> +
> +/* This function will assign an available request id */
> +static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id)
> +{
> +	*request_id = tw_dev->free_queue[tw_dev->free_head];
> +	tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
> +	tw_dev->state[*request_id] = TW_S_STARTED;
> +} /* End twa_get_request_id() */
> +
> +/* This function will send an initconnection command to controller */
> +static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits,
> + 			      u32 set_features, unsigned short current_fw_srl,
> +			      unsigned short current_fw_arch_id,
> +			      unsigned short current_fw_branch,
> +			      unsigned short current_fw_build,
> +			      unsigned short *fw_on_ctlr_srl,
> +			      unsigned short *fw_on_ctlr_arch_id,
> +			      unsigned short *fw_on_ctlr_branch,
> +			      unsigned short *fw_on_ctlr_build,
> +			      u32 *init_connect_result)
> +{
> +	TW_Command_Full *full_command_packet;
> +	TW_Initconnect *tw_initconnect;
> +	int request_id = 0, retval = 1;
> +
> +	/* Initialize InitConnection command packet */
> +	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
> +	memset(full_command_packet, 0, sizeof(TW_Command_Full));
> +	full_command_packet->header.header_desc.size_header = 128;
> +
> +	tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand;
> +	tw_initconnect->opcode = TW_OP_INIT_CONNECTION;
> +
> +	tw_initconnect->request_id = request_id;
> +	tw_initconnect->message_credits = message_credits;
> +	tw_initconnect->features = set_features;
> +#if BITS_PER_LONG > 32
> +	/* Turn on 64-bit sgl support */
> +	tw_initconnect->features |= 1;
> +#endif
> +
> +	if (set_features & TW_EXTENDED_INIT_CONNECT) {
> +		tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE_EXTENDED;
> +		tw_initconnect->fw_srl = current_fw_srl;
> +		tw_initconnect->fw_arch_id = current_fw_arch_id;
> +		tw_initconnect->fw_branch = current_fw_branch;
> +		tw_initconnect->fw_build = current_fw_build;
> +	} else
> +		tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE;
> +
> +	/* Send command packet to the board */
> +	twa_post_command_packet(tw_dev, request_id, 1);
> +
> +	/* Poll for completion */
> +	if (twa_poll_response(tw_dev, request_id, 30)) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x15, "No valid response during init connection");
> +	} else {
> +		if (set_features & TW_EXTENDED_INIT_CONNECT) {
> +			*fw_on_ctlr_srl = tw_initconnect->fw_srl;
> +			*fw_on_ctlr_arch_id = tw_initconnect->fw_arch_id;
> +			*fw_on_ctlr_branch = tw_initconnect->fw_branch;
> +			*fw_on_ctlr_build = tw_initconnect->fw_build;
> +			*init_connect_result = tw_initconnect->result;
> +		}
> +		retval = 0;
> +	}
> +
> +	tw_dev->posted_request_count--;
> +	tw_dev->state[request_id] = TW_S_INITIAL;
> +
> +	return retval;
> +} /* End twa_initconnection() */

ditto


> +/* This function will initialize the fields of a device extension */
> +static int twa_initialize_device_extension(TW_Device_Extension *tw_dev)
> +{
> +	int i, retval = 1;
> +
> +	/* Initialize command packet buffers */
> +	if (twa_allocate_memory(tw_dev, sizeof(TW_Command_Full), 0)) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x16, "Command packet memory allocation failed");
> +		goto out;
> +	}
> +
> +	/* Initialize generic buffer */
> +	if (twa_allocate_memory(tw_dev, TW_SECTOR_SIZE, 1)) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x17, "Generic memory allocation failed");
> +		goto out;
> +	}
> +
> +	/* Allocate event info space */
> +	tw_dev->event_queue[0] = kmalloc(sizeof(TW_Event) * TW_Q_LENGTH, GFP_ATOMIC);
> +	if (!tw_dev->event_queue[0]) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x18, "Event info memory allocation failed");
> +		goto out;
> +	}

allocate with GFP_KERNEL, GFP_ATOMIC is not necessary, and it depletes
the emergency pools



> +	memset(tw_dev->event_queue[0], 0, sizeof(TW_Event) * TW_Q_LENGTH);
> +
> +	for (i = 0; i < TW_Q_LENGTH; i++) {
> +		tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0] + (i * sizeof(TW_Event)));
> +		tw_dev->free_queue[i] = i;
> +		tw_dev->state[i] = TW_S_INITIAL;
> +	}
> +
> +	tw_dev->pending_head = TW_Q_START;
> +	tw_dev->pending_tail = TW_Q_START;
> +	tw_dev->free_head = TW_Q_START;
> +	tw_dev->free_tail = TW_Q_START;
> +	tw_dev->error_sequence_id = 1;
> +	tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
> +
> +	init_MUTEX(&tw_dev->ioctl_sem);
> +	init_waitqueue_head(&tw_dev->ioctl_wqueue);
> +
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_initialize_device_extension() */
> +
> +/* This function is the interrupt service routine */
> +static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
> +{
> +	int request_id, error = 0;
> +	u32 status_reg_value;
> +	TW_Response_Queue response_que;
> +	unsigned long flags = 0;
> +	TW_Command_Full *full_command_packet;
> +	TW_Command *command_packet;
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
> +	int handled = 0;
> +
> +	/* See if we are already running on another processor */
> +	if (test_and_set_bit(TW_IN_INTR, &tw_dev->flags))
> +		return IRQ_NONE;

completely bogus test.  remove.  remove TW_IN_INTR too.


> +	/* Get the per adapter lock */
> +	spin_lock_irqsave(tw_dev->host->host_lock, flags);

spin_lock(), not spin_lock_irqsave()


> +	/* See if the interrupt matches this instance */
> +	if (tw_dev->tw_pci_dev->irq == (unsigned int)irq) {
> +
> +		handled = 1;
> +
> +		/* Read the registers */
> +		status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
> +
> +		/* Check if this is our interrupt, otherwise bail */
> +		if (!(status_reg_value & TW_STATUS_VALID_INTERRUPT))
> +			goto twa_interrupt_bail;

also check for 0xffffffff return value, indicating hardware removed or
hardware fault.


> +		/* Check controller for errors */
> +		if (twa_check_bits(status_reg_value)) {
> +			if (twa_decode_bits(tw_dev, status_reg_value)) {
> +				TW_CLEAR_ALL_INTERRUPTS(tw_dev);
> +				goto twa_interrupt_bail;
> +			}
> +		}
> +
> +		/* Handle host interrupt */
> +		if (status_reg_value & TW_STATUS_HOST_INTERRUPT)
> +			TW_CLEAR_HOST_INTERRUPT(tw_dev);
> +
> +		/* Handle attention interrupt */
> +		if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT) {
> +			TW_CLEAR_ATTENTION_INTERRUPT(tw_dev);
> +			if (!(test_and_set_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags))) {
> +				twa_get_request_id(tw_dev, &request_id);
> +
> +				error = twa_aen_read_queue(tw_dev, request_id);
> +				if (error) {
> +					tw_dev->state[request_id] = TW_S_COMPLETED;
> +					twa_free_request_id(tw_dev, request_id);
> +					clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
> +				}
> +			}
> +		}
> +
> +		/* Handle command interrupt */
> +		if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) {
> +			TW_MASK_COMMAND_INTERRUPT(tw_dev);
> +			/* Drain as many pending commands as we can */
> +			while (tw_dev->pending_request_count > 0) {
> +				request_id = tw_dev->pending_queue[tw_dev->pending_head];
> +				if (tw_dev->state[request_id] != TW_S_PENDING) {
> +					TW_PRINTK(tw_dev->host, TW_DRIVER, 0x19, "Found request id that wasn't pending");
> +					TW_CLEAR_ALL_INTERRUPTS(tw_dev);
> +					goto twa_interrupt_bail;
> +				}
> +				if (twa_post_command_packet(tw_dev, request_id, 1)==0) {
> +					tw_dev->pending_head = (tw_dev->pending_head + 1) % TW_Q_LENGTH;
> +					tw_dev->pending_request_count--;
> +				} else {
> +					/* If we get here, we will continue re-posting on the next command interrupt */
> +					break;
> +				}
> +			}
> +		}
> +
> +		/* Handle response interrupt */
> +		if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT) {
> +
> +			/* Drain the response queue from the board */
> +			while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {
> +				/* Complete the response */
> +				response_que.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
> +				request_id = response_que.u.response_id;
> +				full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
> +				error = 0;
> +				command_packet = &full_command_packet->command.oldcommand;
> +				/* Check for command packet errors */
> +				if (full_command_packet->command.newcommand.status != 0) {
> +					if (tw_dev->srb[request_id] != 0) {
> +						error = twa_fill_sense(tw_dev, request_id, 1, 1);
> +					} else {
> +						/* Skip ioctl error prints */
> +						if (request_id != tw_dev->chrdev_request_id) {
> +							error = twa_fill_sense(tw_dev, request_id, 0, 1);
> +						}
> +					}
> +				}
> +
> +				/* Check for correct state */
> +				if (tw_dev->state[request_id] != TW_S_POSTED) {
> +					if (tw_dev->srb[request_id] != 0) {
> +						TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Received a request id that wasn't posted");
> +					        TW_CLEAR_ALL_INTERRUPTS(tw_dev);
> +						goto twa_interrupt_bail;
> +					}
> +				}
> +
> +				/* Check for internal command completion */
> +				if (tw_dev->srb[request_id] == 0) {
> +					if (request_id != tw_dev->chrdev_request_id) {
> +						if (twa_aen_complete(tw_dev, request_id))
> +							TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Error completing AEN during attention interrupt");
> +					} else {
> +						tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
> +						wake_up(&tw_dev->ioctl_wqueue);
> +					}
> +				} else {
> +					twa_scsiop_execute_scsi_complete(tw_dev, request_id);
> +					/* If no error command was a success */
> +					if (error == 0) {
> +						tw_dev->srb[request_id]->result = (DID_OK << 16);
> +					}
> +
> +					/* If error, command failed */
> +					if (error == 1) {
> +						/* Ask for a host reset */
> +						tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
> +					}
> +
> +					/* Now complete the io */
> +					tw_dev->state[request_id] = TW_S_COMPLETED;
> +					twa_free_request_id(tw_dev, request_id);
> +					tw_dev->posted_request_count--;
> +					tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
> +					twa_unmap_scsi_data(tw_dev, request_id);
> +				}
> +
> +				/* Check for valid status after each drain */
> +				status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
> +				if (twa_check_bits(status_reg_value)) {
> +					if (twa_decode_bits(tw_dev, status_reg_value)) {
> +						TW_CLEAR_ALL_INTERRUPTS(tw_dev);
> +						goto twa_interrupt_bail;
> +					}
> +				}
> +			}
> +		}
> +	}
> +twa_interrupt_bail:
> +	spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
> +	clear_bit(TW_IN_INTR, &tw_dev->flags);
> +	return IRQ_RETVAL(handled);
> +} /* End twa_interrupt() */
> +
> +/* This function will load the request id and various sgls for ioctls */
> +static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length)
> +{
> +	TW_Command *oldcommand;
> +	TW_Command_Apache *newcommand;
> +	TW_SG_Entry *sgl;
> +
> +	if (full_command_packet->command.newcommand.command.opcode == TW_OP_EXECUTE_SCSI) {
> +		newcommand = &full_command_packet->command.newcommand;
> +		newcommand->request_id = request_id;
> +		newcommand->sg_list[0].address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1;
> +		newcommand->sg_list[0].length = length;
> +	} else {
> +		oldcommand = &full_command_packet->command.oldcommand;
> +		oldcommand->request_id = request_id;
> +
> +		if (oldcommand->byte0_offset.sgl_offset) {
> +			/* Load the sg list */
> +			sgl = (TW_SG_Entry *)((u32 *)oldcommand+oldcommand->byte0_offset.sgl_offset);
> +			sgl->address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1;
> +			sgl->length = length;
> +		}
> +	}
> +} /* End twa_load_sgl() */
> +
> +/* This function will perform a pci-dma mapping for a scatter gather list */
> +static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id)
> +{
> +	int use_sg;
> +	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
> +	struct pci_dev *pdev = tw_dev->tw_pci_dev;
> +	int retval = 0;
> +
> +	if (cmd->use_sg == 0)
> +		goto out;
> +
> +	use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, DMA_BIDIRECTIONAL);
> +
> +	if (use_sg == 0) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list");
> +		goto out;
> +	}
> +
> +	cmd->SCp.phase = 2;

Don't use magic numbers for phases, use a descriptive constant



> +	cmd->SCp.have_data_in = use_sg;
> +	retval = use_sg;
> +out:
> +	return retval;
> +} /* End twa_map_scsi_sg_data() */
> +
> +/* This function will perform a pci-dma map for a single buffer */
> +static dma_addr_t twa_map_scsi_single_data(TW_Device_Extension *tw_dev, int request_id)
> +{
> +	dma_addr_t mapping;
> +	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
> +	struct pci_dev *pdev = tw_dev->tw_pci_dev;
> +	int retval = 0;
> +
> +	if (cmd->request_bufflen == 0) {
> +		retval = 0;
> +		goto out;
> +	}
> +
> +	mapping = pci_map_single(pdev, cmd->request_buffer, cmd->request_bufflen, DMA_BIDIRECTIONAL);
> +
> +	if (mapping == 0) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Failed to map page");
> +		goto out;
> +	}
> +
> +	cmd->SCp.phase = 1;
> +	cmd->SCp.have_data_in = mapping;
> +	retval = mapping;
> +out:
> +	return retval;
> +} /* End twa_map_scsi_single_data() */
> +
> +/* This function will poll for a response interrupt of a request */
> +static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
> +{
> +	int retval = 1, found = 0, response_request_id;
> +	TW_Response_Queue response_queue;
> +	TW_Command_Full *full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
> +
> +	if (twa_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, seconds) == 0) {
> +		response_queue.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));
> +		response_request_id = (unsigned char)response_queue.u.response_id;
> +		if (request_id != response_request_id) {
> +			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "Found unexpected request id while polling for response");
> +			goto out;
> +		}
> +		if (full_command_packet->command.newcommand.command.opcode == TW_OP_EXECUTE_SCSI) {
> +			if (full_command_packet->command.newcommand.status != 0) {
> +				/* bad response */
> +				twa_fill_sense(tw_dev, request_id, 0, 0);
> +				goto out;
> +			}
> +			found = 1;
> +		} else {
> +			if (full_command_packet->command.oldcommand.status != 0) {
> +				/* bad response */
> +				twa_fill_sense(tw_dev, request_id, 0, 0);
> +				goto out;
> +			}
> +			found = 1;
> +		}
> +	}
> +
> +	if (found)
> +		retval = 0;
> +out:
> +	return retval;
> +} /* End twa_poll_response() */
> +
> +/* This function will poll the status register for a flag */
> +static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
> +{
> +	u32 status_reg_value;
> +	unsigned long before;
> +	int retval = 1;
> +
> +	status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
> +	before = jiffies;
> +
> +	if (twa_check_bits(status_reg_value))
> +		twa_decode_bits(tw_dev, status_reg_value);
> +
> +	while ((status_reg_value & flag) != flag) {
> +		status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
> +
> +		if (twa_check_bits(status_reg_value))
> +			twa_decode_bits(tw_dev, status_reg_value);
> +
> +		if (time_after(jiffies, before + HZ * seconds))
> +			goto out;
> +
> +		mdelay(5);
> +	}

bzzzz.  this delay loop is _completely_ unacceptable.  It can hang the
machine for far too long.  Use a timer, or schedule_timeout() in process
context.



> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_poll_status() */
> +
> +/* This function will poll the status register for disappearance of a flag */
> +static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds)
> +{
> +	u32 status_reg_value;
> +	unsigned long before;
> +	int retval = 1;
> +
> +	status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
> +	before = jiffies;
> +
> +	if (twa_check_bits(status_reg_value))
> +		twa_decode_bits(tw_dev, status_reg_value);
> +
> +	while ((status_reg_value & flag) != 0) {
> +		status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
> +		if (twa_check_bits(status_reg_value))
> +			twa_decode_bits(tw_dev, status_reg_value);
> +
> +		if (time_after(jiffies, before + HZ * seconds))
> +			goto out;
> +
> +		mdelay(5);
> +	}
> +	retval = 0;
> +out:

another bogus delay loop



> +/* This function will attempt to post a command packet to the board */
> +static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal)
> +{
> +	u32 status_reg_value;
> +	unsigned long command_que_value;
> +	int retval = 1;
> +
> +	command_que_value = tw_dev->command_packet_phys[request_id];
> +	status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
> +
> +	if (twa_check_bits(status_reg_value))
> +		twa_decode_bits(tw_dev, status_reg_value);
> +
> +	if (((tw_dev->pending_request_count > 0) && (tw_dev->state[request_id] != TW_S_PENDING)) || (status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL)) {
> +
> +		/* Only pend internal driver commands */
> +		if (!internal) {
> +			retval = SCSI_MLQUEUE_HOST_BUSY;
> +			goto out;
> +		}
> +
> +		/* Couldn't post the command packet, so we do it later */
> +		if (tw_dev->state[request_id] != TW_S_PENDING) {
> +			tw_dev->state[request_id] = TW_S_PENDING;
> +			tw_dev->pending_request_count++;
> +			if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) {
> +				tw_dev->max_pending_request_count = tw_dev->pending_request_count;
> +			}
> +			tw_dev->pending_queue[tw_dev->pending_tail] = request_id;
> +			tw_dev->pending_tail = (tw_dev->pending_tail + 1) % TW_Q_LENGTH;
> +		}
> +		TW_UNMASK_COMMAND_INTERRUPT(tw_dev);
> +		goto out;
> +	} else {
> +		/* We successfully posted the command packet */
> +#if BITS_PER_LONG > 32
> +		writeq(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
> +#else
> +		writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
> +#endif

PCI posting?


> +		tw_dev->state[request_id] = TW_S_POSTED;
> +		tw_dev->posted_request_count++;
> +		if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) {
> +			tw_dev->max_posted_request_count = tw_dev->posted_request_count;
> +		}
> +	}
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_post_command_packet() */
> +
> +/* This function will reset a device extension */
> +static int twa_reset_device_extension(TW_Device_Extension *tw_dev)
> +{
> +	int i = 0;
> +	int retval = 1;
> +
> +	/* Abort all requests that are in progress */
> +	for (i = 0; i < TW_Q_LENGTH; i++) {
> +		if ((tw_dev->state[i] != TW_S_FINISHED) &&
> +		    (tw_dev->state[i] != TW_S_INITIAL) &&
> +		    (tw_dev->state[i] != TW_S_COMPLETED)) {
> +			if (tw_dev->srb[i]) {
> +				tw_dev->srb[i]->result = (DID_RESET << 16);
> +				tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
> +				twa_unmap_scsi_data(tw_dev, i);
> +			}
> +		}
> +	}
> +
> +	/* Reset queues and counts */
> +	for (i = 0; i < TW_Q_LENGTH; i++) {
> +		tw_dev->free_queue[i] = i;
> +		tw_dev->state[i] = TW_S_INITIAL;
> +	}
> +	tw_dev->free_head = TW_Q_START;
> +	tw_dev->free_tail = TW_Q_START;
> +	tw_dev->posted_request_count = 0;
> +	tw_dev->pending_request_count = 0;
> +	tw_dev->pending_head = TW_Q_START;
> +	tw_dev->pending_tail = TW_Q_START;
> +	tw_dev->reset_print = 0;
> +	tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
> +	tw_dev->flags = 0;
> +
> +	TW_DISABLE_INTERRUPTS(tw_dev);
> +
> +	if (twa_reset_sequence(tw_dev, 1))
> +		goto out;
> +
> +        TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
> +
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_reset_device_extension() */
> +
> +/* This function will reset a controller */
> +static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset)
> +{
> +	int tries = 0, retval = 1, flashed = 0, do_soft_reset = soft_reset;
> +
> +	while (tries < TW_MAX_RESET_TRIES) {
> +		if (do_soft_reset)
> +			TW_SOFT_RESET(tw_dev);
> +
> +		/* Make sure controller is in a good state */
> +		if (twa_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY | (do_soft_reset == 1 ? TW_STATUS_ATTENTION_INTERRUPT : 0), 30)) {
> +			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1f, "Microcontroller not ready during reset sequence");
> +			do_soft_reset = 1;
> +			tries++;
> +			continue;
> +		}
> +
> +		/* Empty response queue */
> +		if (twa_empty_response_queue(tw_dev)) {
> +			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x20, "Response queue empty failed during reset sequence");
> +			do_soft_reset = 1;
> +			tries++;
> +			continue;
> +		}
> +
> +		flashed = 0;
> +
> +		/* Check for compatibility/flash */
> +		if (twa_check_srl(tw_dev, &flashed)) {
> +			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x21, "Compatibility check failed during reset sequence");
> +			do_soft_reset = 1;
> +			tries++;
> +			continue;
> +		} else {
> +			if (flashed) {
> +				tries++;
> +				continue;
> +			}
> +		}
> +
> +		/* Drain the AEN queue */
> +		if (twa_aen_drain_queue(tw_dev, soft_reset)) {
> +			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x22, "AEN drain failed during reset sequence");
> +			do_soft_reset = 1;
> +			tries++;
> +			continue;
> +		}
> +
> +		/* If we got here, controller is in a good state */
> +		retval = 0;
> +		goto out;
> +	}
> +out:
> +	return retval;
> +} /* End twa_reset_sequence() */
> +
> +/* This funciton returns unit geometry in cylinders/heads/sectors */
> +static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
> +{
> +	int heads, sectors, cylinders;
> +	TW_Device_Extension *tw_dev;
> +
> +	tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
> +
> +	if (capacity >= 0x200000) {
> +		heads = 255;
> +		sectors = 63;
> +		cylinders = sector_div(capacity, heads * sectors);
> +	} else {
> +		heads = 64;
> +		sectors = 32;
> +		cylinders = sector_div(capacity, heads * sectors);
> +	}
> +
> +	geom[0] = heads;
> +	geom[1] = sectors;
> +	geom[2] = cylinders;
> +
> +	return 0;
> +} /* End twa_scsi_biosparam() */
> +
> +/* This is the new scsi eh abort function */
> +static int twa_scsi_eh_abort(struct scsi_cmnd *SCpnt)
> +{
> +	int i;
> +	TW_Device_Extension *tw_dev = NULL;
> +	int retval = FAILED;
> +
> +	tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
> +
> +	tw_dev->num_aborts++;
> +
> +	/* If we find any IO's in process, we have to reset the card */
> +	for (i = 0; i < TW_Q_LENGTH; i++) {
> +		if ((tw_dev->state[i] != TW_S_FINISHED) && (tw_dev->state[i] != TW_S_INITIAL)) {
> +			printk(KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Unit #%d: Command (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, TW_DRIVER, 0x2c, SCpnt->device->id, SCpnt->cmnd[0]);
> +			if (twa_reset_device_extension(tw_dev)) {
> +				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2a, "Controller reset failed during scsi abort");
> +				goto out;
> +			}
> +			break;
> +		}
> +	}
> +	retval = SUCCESS;
> +out:
> +	return retval;
> +} /* End twa_scsi_eh_abort() */
> +
> +/* This is the new scsi eh reset function */
> +static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt)
> +{
> +	TW_Device_Extension *tw_dev = NULL;
> +	int retval = FAILED;
> +
> +	tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
> +
> +	tw_dev->num_resets++;
> +
> +	printk(KERN_WARNING "3w-9xxx: scsi%d: SCSI host reset started.\n", tw_dev->host->host_no);
> +
> +	/* Now reset the card and some of the device extension data */
> +	if (twa_reset_device_extension(tw_dev)) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2b, "Controller reset failed during scsi host reset");
> +		goto out;
> +	}
> +	printk(KERN_WARNING "3w-9xxx: scsi%d: SCSI host reset succeeded.\n", tw_dev->host->host_no);
> +	retval = SUCCESS;
> +out:
> +	return retval;
> +} /* End twa_scsi_eh_reset() */
> +
> +/* This is the main scsi queue function to handle scsi opcodes */
> +static int twa_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
> +{
> +	int request_id, retval;
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
> +
> +	/* Save done function into scsi_cmnd struct */
> +	SCpnt->scsi_done = done;
> +
> +	/* Get a free request id */
> +	twa_get_request_id(tw_dev, &request_id);
> +
> +	/* Save the scsi command for use by the ISR */
> +	tw_dev->srb[request_id] = SCpnt;

you need to save the 'done' hook too


> +	/* Initialize phase to zero */
> +	SCpnt->SCp.phase = 0;
> +
> +	retval = twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
> +	switch (retval) {
> +	case SCSI_MLQUEUE_HOST_BUSY:
> +		twa_free_request_id(tw_dev, request_id);
> +		break;
> +	case 1:
> +		tw_dev->state[request_id] = TW_S_COMPLETED;
> +		twa_free_request_id(tw_dev, request_id);
> +		SCpnt->result = (DID_ERROR << 16);
> +		done(SCpnt);
> +	}
> +
> +	return retval;
> +} /* End twa_scsi_queue() */
> +
> +/* This function hands scsi cdb's to the firmware */
> +static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Apache *sglistarg)
> +{
> +	TW_Command_Full *full_command_packet;
> +	TW_Command_Apache *command_packet;
> +	u32 num_sectors = 0x0;
> +	int i, sg_count;
> +	struct scsi_cmnd *srb = NULL;
> +	struct scatterlist *sglist = NULL;
> +	u32 buffaddr = 0x0;
> +	int retval = 1;
> +
> +	if (tw_dev->srb[request_id]) {
> +		if (tw_dev->srb[request_id]->request_buffer) {
> +			sglist = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer;
> +		}
> +		srb = tw_dev->srb[request_id];
> +	}
> +
> +	/* Initialize command packet */
> +	full_command_packet = (TW_Command_Full *)tw_dev->command_packet_virt[request_id];
> +	full_command_packet->header.header_desc.size_header = 128;
> +	full_command_packet->header.status_block.error = 0;
> +	full_command_packet->header.status_block.substatus_block.severity = 0;
> +
> +	command_packet = &full_command_packet->command.newcommand;
> +	command_packet->status = 0;
> +	command_packet->command.opcode = TW_OP_EXECUTE_SCSI;
> +
> +	/* We forced 16 byte cdb use earlier */
> +	if (!cdb)
> +		memcpy(command_packet->cdb, srb->cmnd, TW_MAX_CDB_LEN);
> +	else
> +		memcpy(command_packet->cdb, cdb, TW_MAX_CDB_LEN);
> +
> +	if (srb)
> +		command_packet->unit = srb->device->id;
> +	else
> +		command_packet->unit = 0;
> +
> +	command_packet->request_id = request_id;
> +	command_packet->sgl_offset = 16;
> +
> +	if (!sglistarg) {
> +		/* Map sglist from scsi layer to cmd packet */
> +		if (tw_dev->srb[request_id]->use_sg == 0) {
> +			if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH) {
> +				command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id];
> +				command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH;
> +			} else {
> +				buffaddr = twa_map_scsi_single_data(tw_dev, request_id);
> +				if (buffaddr == 0)
> +					goto out;
> +
> +				command_packet->sg_list[0].address = buffaddr;
> +				command_packet->sg_list[0].length = tw_dev->srb[request_id]->request_bufflen;
> +			}
> +			command_packet->sgl_entries = 1;
> +
> +			if (command_packet->sg_list[0].address & TW_ALIGNMENT_9000_SGL) {
> +				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2d, "Found unaligned address during execute scsi");
> +				goto out;
> +			}
> +		}
> +
> +		if (tw_dev->srb[request_id]->use_sg > 0) {
> +			sg_count = twa_map_scsi_sg_data(tw_dev, request_id);
> +			if (sg_count == 0)
> +				goto out;
> +
> +			for (i = 0; i < sg_count; i++) {
> +				command_packet->sg_list[i].address = sg_dma_address(&sglist[i]);
> +				command_packet->sg_list[i].length = sg_dma_len(&sglist[i]);
> +				if (command_packet->sg_list[i].address & TW_ALIGNMENT_9000_SGL) {
> +					TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2e, "Found unaligned sgl address during execute scsi");
> +					goto out;
> +				}
> +			}
> +			command_packet->sgl_entries = tw_dev->srb[request_id]->use_sg;
> +		}
> +	} else {
> +		/* Internal cdb post */
> +		for (i = 0; i < use_sg; i++) {
> +			command_packet->sg_list[i].address = sglistarg[i].address;
> +			command_packet->sg_list[i].length = sglistarg[i].length;
> +			if (command_packet->sg_list[i].address & TW_ALIGNMENT_9000_SGL) {
> +				TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2f, "Found unaligned sgl address during internal post");
> +				goto out;
> +			}
> +		}
> +		command_packet->sgl_entries = use_sg;
> +	}
> +
> +	if (srb) {
> +		if (srb->cmnd[0] == READ_6 || srb->cmnd[0] == WRITE_6)
> +			num_sectors = (u32)srb->cmnd[4];
> +
> +		if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == WRITE_10)
> +			num_sectors = (u32)srb->cmnd[8] | ((u32)srb->cmnd[7] << 8);
> +	}
> +
> +	/* Update sector statistic */
> +	tw_dev->sector_count = num_sectors;
> +	if (tw_dev->sector_count > tw_dev->max_sector_count)
> +		tw_dev->max_sector_count = tw_dev->sector_count;
> +
> +	/* Update SG statistics */
> +	if (srb) {
> +		tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg;
> +		if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
> +			tw_dev->max_sgl_entries = tw_dev->sgl_entries;
> +	}
> +
> +	/* Now post the command to the board */
> +	if (srb) {
> +		retval = twa_post_command_packet(tw_dev, request_id, 0);
> +	} else {
> +		twa_post_command_packet(tw_dev, request_id, 1);
> +		retval = 0;
> +	}
> +out:
> +	return retval;
> +} /* End twa_scsiop_execute_scsi() */
> +
> +/* This function completes an execute scsi operation */
> +static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id)
> +{
> +	/* Copy the response if too small */
> +	if ((tw_dev->srb[request_id]->request_buffer) && (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH)) {
> +		memcpy(tw_dev->srb[request_id]->request_buffer, tw_dev->generic_buffer_virt[request_id], tw_dev->srb[request_id]->request_bufflen);
> +	}
> +} /* End twa_scsiop_execute_scsi_complete() */

this badly needs line wrapping

Greater-than-80 columns is ok, but completely invisible code off the
right of the screen is annoying to review.


> +
> +/* This function will setup the interrupt handler */
> +static int twa_setup_irq(TW_Device_Extension *tw_dev)
> +{
> +	char *device = TW_DEVICE_NAME;
> +	int retval = 1;
> +
> +	if (request_irq(tw_dev->tw_pci_dev->irq, twa_interrupt, SA_SHIRQ, device, tw_dev) < 0) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x30, "Error requesting IRQ");
> +		goto out;
> +	}
> +	retval = 0;
> +out:
> +	return retval;
> +} /* End twa_setup_irq() */

delete, and call directly


> +/* This function tells the controller to shut down */
> +static void __twa_shutdown(TW_Device_Extension *tw_dev)
> +{
> +	/* Disable interrupts */
> +	TW_DISABLE_INTERRUPTS(tw_dev);
> +
> +	printk(KERN_WARNING "3w-9xxx: Shutting down host %d.\n", tw_dev->host->host_no);
> +
> +	/* Tell the card we are shutting down */
> +	if (twa_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {

That's way too many arguments to the function.  Create a "struct
twa_initconnection_info" or similar...


> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x31, "Connection shutdown failed");
> +	} else {
> +		printk(KERN_WARNING "3w-9xxx: Shutdown complete.\n");
> +	}
> +
> +	/* Clear all interrupts just before exit */
> +	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
> +} /* End __twa_shutdown() */
> +
> +/* Wrapper for __twa_shutdown */
> +static void twa_shutdown(struct device *dev)
> +{
> +	struct Scsi_Host *host = pci_get_drvdata(to_pci_dev(dev));
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
> +
> +	__twa_shutdown(tw_dev);
> +} /* End twa_shutdown() */
> +
> +/* This function will configure individual target parameters */
> +static int twa_slave_configure(struct scsi_device *SDptr)
> +{
> +	int max_cmds;
> +
> +	if (cmds_per_lun) {
> +		max_cmds = cmds_per_lun;
> +		if (max_cmds > TW_MAX_CMDS_PER_LUN)
> +			max_cmds = TW_MAX_CMDS_PER_LUN;
> +	} else {
> +		max_cmds = TW_MAX_CMDS_PER_LUN;
> +	}
> +	scsi_adjust_queue_depth(SDptr, MSG_ORDERED_TAG, max_cmds);
> +
> +	return 0;
> +} /* End twa_slave_configure */
> +
> +/* This function will look up a string */
> +static char *twa_string_lookup(twa_message_type *table, unsigned int code)
> +{
> +	int index;
> +
> +	for (index = 0; ((code != table[index].code) &&
> +		      (table[index].text != (char *)0)); index++);
> +	return(table[index].text);
> +} /* End twa_string_lookup() */
> +
> +/* This function will perform a pci-dma unmap */
> +static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id)
> +{
> +	struct scsi_cmnd *cmd = tw_dev->srb[request_id];
> +	struct pci_dev *pdev = tw_dev->tw_pci_dev;
> +
> +	switch(cmd->SCp.phase) {
> +	case 1:
> +		pci_unmap_single(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, DMA_BIDIRECTIONAL);
> +		break;
> +	case 2:
> +		pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, DMA_BIDIRECTIONAL);
> +		break;
> +	}
> +} /* End twa_unmap_scsi_data() */
> +
> +/* Get informationa bout the card/driver */
> +static const char *twa_info(struct Scsi_Host *host)
> +{
> +	static char buffer[512];
> +
> +	sprintf(buffer, "3ware 9000 Storage Controller");
> +
> +	return buffer;
> +} /* End twa_info() */

I thought the ->info hook was deprecated?


> +/* scsi_host_template initializer */
> +static struct scsi_host_template driver_template = {
> +	.module			= THIS_MODULE,
> +	.name			= "3ware 9000 Storage Controller",
> +	.info			= twa_info,
> +	.queuecommand		= twa_scsi_queue,
> +	.eh_abort_handler	= twa_scsi_eh_abort,
> +	.eh_host_reset_handler	= twa_scsi_eh_reset,
> +	.bios_param		= twa_scsi_biosparam,
> +	.slave_configure	= twa_slave_configure,
> +	.can_queue		= TW_Q_LENGTH-2,
> +	.this_id		= -1,
> +	.sg_tablesize		= TW_APACHE_MAX_SGL_LENGTH,
> +	.max_sectors		= TW_MAX_SECTORS,
> +	.cmd_per_lun		= TW_MAX_CMDS_PER_LUN,
> +	.use_clustering		= ENABLE_CLUSTERING,
> +	.shost_attrs		= twa_host_attrs,
> +	.emulated		= 1
> +};
> +
> +/* This function will probe and initialize a card */
> +static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
> +{
> +	struct Scsi_Host *host = NULL;
> +	TW_Device_Extension *tw_dev;
> +	u32 mem_addr, mem_len;
> +	int retval = -ENODEV;
> +
> +	if (pci_enable_device(pdev)) {
> +		TW_PRINTK(host, TW_DRIVER, 0x32, "Failed to enable pci device");
> +		goto out;
> +	}

propagate pci_enable_device return value to caller


> +	pci_set_master(pdev);
> +
> +	if (pci_set_dma_mask(pdev, TW_DMA_MASK)) {
> +		TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask");
> +		goto out;
> +	}

propagate pci_set_dma_mask return value to caller


> +	host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
> +	if (!host) {
> +		TW_PRINTK(host, TW_DRIVER, 0x24, "Failed to allocate memory for device extension");
> +		retval = -ENOMEM;
> +		goto out;
> +	}
> +	tw_dev = (TW_Device_Extension *)host->hostdata;
> +
> +	memset(tw_dev, 0, sizeof(TW_Device_Extension));
> +
> +	/* Save values to device extension */
> +	tw_dev->host = host;
> +	tw_dev->tw_pci_dev = pdev;
> +
> +	if (twa_initialize_device_extension(tw_dev)) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x25, "Failed to initialize device extension");
> +		goto out_free_device_extension;
> +	}
> +
> +	mem_addr = pci_resource_start(pdev, 1);
> +	mem_len = pci_resource_len(pdev, 1);
> +
> +	/* Make sure that mem region isn't already taken */
> +	if (check_mem_region(mem_addr, mem_len)) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x26, "Failed to get mem region");
> +		goto out_free_device_extension;
> +	}
> +
> +	/* Reserve the mem address space */
> +	request_mem_region(mem_addr, mem_len, TW_DEVICE_NAME);

bogus.  First, check_mem_region is racy.

Moreover, you should be using pci_request_regions() and
pci_release_regions(), not request_mem_region.


> +	/* Save base address */
> +	tw_dev->base_addr = ioremap(mem_addr, PAGE_SIZE);

check for NULL return


> +	/* Disable interrupts on the card */
> +	TW_DISABLE_INTERRUPTS(tw_dev);

don't you think you enable busmastering _after_ you do this?


> +	/* Initialize the card */
> +	if (twa_reset_sequence(tw_dev, 0))
> +		goto out_release_mem_region;
> +
> +	/* Set host specific parameters */
> +	host->max_id = TW_MAX_UNITS;
> +	host->max_cmd_len = TW_MAX_CDB_LEN;
> +	host->max_lun = 0;
> +	host->max_channel = 0;

max_lun and max_channel should be greater than zero, right?


> +	/* Register the card with the kernel SCSI layer */
> +	retval = scsi_add_host(host, &pdev->dev);
> +	if (retval) {
> +		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x27, "scsi add host failed");
> +		goto out_release_mem_region;
> +	}
> +
> +	pci_set_drvdata(pdev, host);
> +
> +	printk(KERN_WARNING "3w-9xxx: scsi%d: Found a 3ware 9000 Storage Controller at 0x%x, IRQ: %d.\n", host->host_no, mem_addr, pdev->irq);
> +	printk(KERN_WARNING "3w-9xxx: scsi%d: Firmware %s, BIOS %s, Ports: %d.\n", host->host_no, (char *)twa_get_param(tw_dev, 0, TW_VERSION_TABLE, TW_PARAM_FWVER, TW_PARAM_FWVER_LENGTH), (char *)twa_get_param(tw_dev, 1, TW_VERSION_TABLE, TW_PARAM_BIOSVER, TW_PARAM_BIOSVER_LENGTH), *(int *)twa_get_param(tw_dev, 2, TW_INFORMATION_TABLE, TW_PARAM_PORTCOUNT, TW_PARAM_PORTCOUNT_LENGTH));
> +
> +	/* Now setup the interrupt handler */
> +	if (twa_setup_irq(tw_dev))
> +		goto out_remove_host;
> +
> +	twa_device_extension_list[twa_device_extension_count] = tw_dev;
> +	twa_device_extension_count++;
> +
> +	/* Re-enable interrupts on the card */
> +	TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
> +
> +	/* Finally, scan the host */
> +	scsi_scan_host(host);
> +
> +	if (twa_major == -1) {
> +		if ((twa_major = register_chrdev (0, "twa", &twa_fops)) < 0)
> +			TW_PRINTK(host, TW_DRIVER, 0x29, "Failed to register character device");
> +	}
> +	return 0;
> +
> +out_remove_host:
> +	scsi_remove_host(host);
> +out_release_mem_region:
> +	release_mem_region(mem_addr, mem_len);

pci_release_regions


> +out_free_device_extension:
> +	twa_free_device_extension(tw_dev);
> +	scsi_host_put(host);
> +out:

missing pci_disable_device, complementary to pci_enable_device


> +	return retval;
> +} /* End twa_probe() */
> +
> +/* This function is called to remove a device */
> +static void twa_remove(struct pci_dev *pdev)
> +{
> +	struct Scsi_Host *host = pci_get_drvdata(pdev);
> +	TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
> +
> +	scsi_remove_host(tw_dev->host);
> +
> +	__twa_shutdown(tw_dev);
> +
> +	/* Free up the IRQ */
> +	free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
> +
> +	/* Free up the mem region */
> +	release_mem_region(pci_resource_start(tw_dev->tw_pci_dev, 1), pci_resource_len(tw_dev->tw_pci_dev, 1));
> +
> +	/* Free up device extension resources */
> +	twa_free_device_extension(tw_dev);
> +
> +	scsi_host_put(tw_dev->host);
> +	pci_disable_device(pdev);
> +	twa_device_extension_count--;

BUG -- if you register the chrdev in PCI ->probe, you must unregister it
here.



> +} /* End twa_remove() */
> +
> +/* PCI Devices supported by this driver */
> +static struct pci_device_id twa_pci_tbl[] __devinitdata = {
> +	{ PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9000,
> +	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(pci, twa_pci_tbl);
> +
> +/* pci_driver initializer */
> +static struct pci_driver twa_driver = {
> +	.name		= "3w-9xxx",
> +	.id_table	= twa_pci_tbl,
> +	.probe		= twa_probe,
> +	.remove		= twa_remove,
> +	.driver		= {
> +		.shutdown = twa_shutdown
> +	}
> +};
> +
> +/* This function is called on driver initialization */
> +static int __init twa_init(void)
> +{
> +	printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", twa_driver_version);
> +
> +	pci_register_driver(&twa_driver);
> +
> +	return 0;
> +} /* End twa_init() */

wrong.  do

	return pci_module_init(&twa_driver)


> +/* This function is called on driver exit */
> +static void __exit twa_exit(void)
> +{
> +	/* Unregister character device */
> +	if (twa_major >= 0) {
> +		unregister_chrdev(twa_major, "twa");
> +		twa_major = -1;
> +	}

see above


> +	pci_unregister_driver(&twa_driver);
> +} /* End twa_exit() */
> +
> +module_init(twa_init);
> +module_exit(twa_exit);



             reply	other threads:[~2004-05-26 22:00 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-05-26 21:59 Adam Radford [this message]
2004-05-27 17:20 ` [PATCH/RFC 1/2] 3ware 9000 SATA-RAID driver v2.26.00.009 Jeff Garzik
2004-05-28  4:32   ` Matthew Wilcox
  -- strict thread matches above, loose matches on Subject: below --
2004-05-26 20:46 Adam Radford
2004-05-26 21:42 ` Jeff Garzik

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=HYCD4000.31K@hadar.amcc.com \
    --to=aradford@amcc.com \
    --cc=akpm@osdl.org \
    --cc=hch@lst.de \
    --cc=james.bottomley@steeleye.com \
    --cc=jgarzik@pobox.com \
    --cc=linux-scsi@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox