mirror of
https://github.com/SCST-project/scst.git
synced 2026-07-19 22:42:38 +00:00
Initial commit
git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
#
|
||||
# Common makefile for SCSI target mid-level and its drivers
|
||||
#
|
||||
# Copyright (C) 2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#
|
||||
|
||||
# Decide to use which kernel src. If not specified, is current running kernel.
|
||||
#export KDIR=/usr/src/linux-2.6
|
||||
|
||||
SCST_DIR=scst
|
||||
QLA_INI_DIR=qla2x00t
|
||||
QLA_DIR=qla2x00t/qla2x00-target
|
||||
|
||||
all:
|
||||
cd $(SCST_DIR) && $(MAKE) $@
|
||||
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
|
||||
|
||||
install:
|
||||
cd $(SCST_DIR) && $(MAKE) $@
|
||||
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
|
||||
|
||||
uninstall:
|
||||
cd $(SCST_DIR) && $(MAKE) $@
|
||||
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
|
||||
|
||||
clean:
|
||||
cd $(SCST_DIR) && $(MAKE) $@
|
||||
@if [ -d $(QLA_INI_DIR) ]; then cd $(QLA_INI_DIR) && $(MAKE) $@; fi
|
||||
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
|
||||
|
||||
extraclean:
|
||||
cd $(SCST_DIR) && $(MAKE) $@
|
||||
@if [ -d $(QLA_INI_DIR) ]; then cd $(QLA_INI_DIR) && $(MAKE) $@; fi
|
||||
@if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
|
||||
|
||||
scst:
|
||||
cd $(SCST_DIR) && $(MAKE)
|
||||
|
||||
scst_install:
|
||||
cd $(SCST_DIR) && $(MAKE) install
|
||||
|
||||
scst_uninstall:
|
||||
cd $(SCST_DIR) && $(MAKE) uninstall
|
||||
|
||||
scst_clean:
|
||||
cd $(SCST_DIR) && $(MAKE) clean
|
||||
|
||||
scst_extraclean:
|
||||
cd $(SCST_DIR) && $(MAKE) extraclean
|
||||
|
||||
qla:
|
||||
cd $(QLA_DIR) && $(MAKE)
|
||||
|
||||
qla_install:
|
||||
cd $(QLA_DIR) && $(MAKE) install
|
||||
|
||||
qla_uninstall:
|
||||
cd $(QLA_DIR) && $(MAKE) uninstall
|
||||
|
||||
qla_clean:
|
||||
cd $(QLA_INI_DIR) && $(MAKE) clean
|
||||
cd $(QLA_DIR) && $(MAKE) clean
|
||||
|
||||
qla_extraclean:
|
||||
cd $(QLA_INI_DIR)/.. && $(MAKE) extraclean
|
||||
cd $(QLA_DIR) && $(MAKE) extraclean
|
||||
|
||||
help:
|
||||
@echo " all (the default) : make all"
|
||||
@echo " clean : clean files"
|
||||
@echo " extraclean : clean + clean dependencies"
|
||||
@echo " install : install"
|
||||
@echo " uninstall : uninstall"
|
||||
@echo ""
|
||||
@echo " scst : make scst only"
|
||||
@echo " scst_clean : scst: clean "
|
||||
@echo " scst_extraclean : scst: clean + clean dependencies"
|
||||
@echo " scst_install : scst: install"
|
||||
@echo " scst_uninstall : scst: uninstall"
|
||||
@echo ""
|
||||
@echo " qla : make new qla target using 2.6.x kernel qla2xxx"
|
||||
@echo " qla_clean : 2.6 qla target: clean "
|
||||
@echo " qla_extraclean : 2.6 qla target: clean + clean dependencies"
|
||||
@echo " qla_install : 2.6 qla target: install"
|
||||
@echo " qla_uninstall : 2.6 qla target: uninstall"
|
||||
@echo " Notes :"
|
||||
@echo " - install and uninstall must be made as root"
|
||||
|
||||
.PHONY: all install uninstall clean extraclean help \
|
||||
qla qla_install qla_uninstall qla_clean qla_extraclean \
|
||||
scst scst_install scst_uninstall scst_clean scst_extraclean
|
||||
@@ -0,0 +1,82 @@
|
||||
config SCSI_QLA_FC
|
||||
tristate "QLogic QLA2XXX Fibre Channel Support"
|
||||
depends on PCI && SCSI
|
||||
select SCSI_FC_ATTRS
|
||||
select FW_LOADER
|
||||
---help---
|
||||
This qla2xxx driver supports all QLogic Fibre Channel
|
||||
PCI and PCIe host adapters.
|
||||
|
||||
By default, firmware for the ISP parts will be loaded
|
||||
via the Firmware Loader interface.
|
||||
|
||||
ISP Firmware Filename
|
||||
---------- -----------------
|
||||
21xx ql2100_fw.bin
|
||||
22xx ql2200_fw.bin
|
||||
2300, 2312, 6312 ql2300_fw.bin
|
||||
2322, 6322 ql2322_fw.bin
|
||||
24xx ql2400_fw.bin
|
||||
|
||||
Upon request, the driver caches the firmware image until
|
||||
the driver is unloaded.
|
||||
|
||||
Firmware images can be retrieved from:
|
||||
|
||||
ftp://ftp.qlogic.com/outgoing/linux/firmware/
|
||||
|
||||
NOTE: The original method of building firmware-loader
|
||||
modules has been deprecated as the firmware-images will
|
||||
be removed from the kernel sources.
|
||||
|
||||
config SCSI_QLA2XXX_TARGET
|
||||
bool "QLogic 2xxx target mode support"
|
||||
depends on SCSI_QLA_FC
|
||||
default y
|
||||
---help---
|
||||
This option enables target mode hooks used by the SCST QLA2x00tgt driver.
|
||||
Once the qla2x00tgt module is loaded, target mode can be enable via a
|
||||
sysfs interface under scsi_host, thus enabling target mode for specific
|
||||
cards.
|
||||
You will also need the SCST middle level drivers from http://scst.sf.net/.
|
||||
|
||||
config SCSI_QLA2XXX_EMBEDDED_FIRMWARE
|
||||
bool " Use firmware-loader modules (DEPRECATED)"
|
||||
depends on SCSI_QLA_FC
|
||||
help
|
||||
This option offers you the deprecated firmware-loader
|
||||
modules that have been obsoleted by the usage of the
|
||||
Firmware Loader interface in the qla2xxx driver.
|
||||
|
||||
config SCSI_QLA21XX
|
||||
tristate " Build QLogic ISP2100 firmware-module"
|
||||
depends on SCSI_QLA_FC && SCSI_QLA2XXX_EMBEDDED_FIRMWARE
|
||||
---help---
|
||||
This driver supports the QLogic 21xx (ISP2100) host adapter family.
|
||||
|
||||
config SCSI_QLA22XX
|
||||
tristate " Build QLogic ISP2200 firmware-module"
|
||||
depends on SCSI_QLA_FC && SCSI_QLA2XXX_EMBEDDED_FIRMWARE
|
||||
---help---
|
||||
This driver supports the QLogic 22xx (ISP2200) host adapter family.
|
||||
|
||||
config SCSI_QLA2300
|
||||
tristate " Build QLogic ISP2300/ISP6312 firmware-module"
|
||||
depends on SCSI_QLA_FC && SCSI_QLA2XXX_EMBEDDED_FIRMWARE
|
||||
---help---
|
||||
This driver supports the QLogic 2300 (ISP2300, ISP2312 and
|
||||
ISP6312) host adapter family.
|
||||
|
||||
config SCSI_QLA2322
|
||||
tristate " Build QLogic ISP2322/ISP6322 firmware-module"
|
||||
depends on SCSI_QLA_FC && SCSI_QLA2XXX_EMBEDDED_FIRMWARE
|
||||
---help---
|
||||
This driver supports the QLogic 2322 (ISP2322 and ISP6322) host
|
||||
adapter family.
|
||||
|
||||
config SCSI_QLA24XX
|
||||
tristate " Build QLogic ISP24xx firmware-module"
|
||||
depends on SCSI_QLA_FC && SCSI_QLA2XXX_EMBEDDED_FIRMWARE
|
||||
---help---
|
||||
This driver supports the QLogic 24xx (ISP2422 and ISP2432) host
|
||||
adapter family.
|
||||
@@ -0,0 +1,48 @@
|
||||
ifneq ($(KERNELRELEASE),)
|
||||
EXTRA_CFLAGS += -DUNIQUE_FW_NAME
|
||||
|
||||
qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
|
||||
qla_dbg.o qla_sup.o qla_rscn.o qla_attr.o
|
||||
|
||||
obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o
|
||||
|
||||
qla2100-y := ql2100.o ql2100_fw.o
|
||||
qla2200-y := ql2200.o ql2200_fw.o
|
||||
qla2300-y := ql2300.o ql2300_fw.o
|
||||
qla2322-y := ql2322.o ql2322_fw.o
|
||||
qla2400-y := ql2400.o ql2400_fw.o
|
||||
|
||||
obj-$(CONFIG_SCSI_QLA21XX) += qla2xxx.o qla2100.o
|
||||
obj-$(CONFIG_SCSI_QLA22XX) += qla2xxx.o qla2200.o
|
||||
obj-$(CONFIG_SCSI_QLA2300) += qla2xxx.o qla2300.o
|
||||
obj-$(CONFIG_SCSI_QLA2322) += qla2xxx.o qla2322.o
|
||||
obj-$(CONFIG_SCSI_QLA24XX) += qla2xxx.o qla2400.o
|
||||
|
||||
else
|
||||
ifeq ($(KVER),)
|
||||
ifeq ($(KDIR),)
|
||||
KDIR := /lib/modules/$(shell uname -r)/build
|
||||
endif
|
||||
else
|
||||
KDIR := /lib/modules/$(KVER)/build
|
||||
endif
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)
|
||||
|
||||
install: all
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \
|
||||
modules_install
|
||||
|
||||
uninstall:
|
||||
rm -f $(INSTALL_DIR)/qla2[2-3]00.ko $(INSTALL_DIR)/qla2xxx.ko
|
||||
-/sbin/depmod -a
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend *~ Modules.symvers
|
||||
rm -rf .tmp_versions
|
||||
|
||||
extraclean: clean
|
||||
|
||||
.PHONY: all install uninstall clean extraclean
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* QLogic Fibre Channel HBA Driver
|
||||
* Copyright (C) 2003 Christoph Hellwig.
|
||||
* Copyright (c) 2003-2005 QLogic Corporation
|
||||
*
|
||||
* See LICENSE.qla2xxx for copyright and licensing details.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "qla_def.h"
|
||||
|
||||
static char qla_driver_name[] = "qla2100";
|
||||
|
||||
extern unsigned char fw2100tp_version[];
|
||||
extern unsigned char fw2100tp_version_str[];
|
||||
extern unsigned short fw2100tp_addr01;
|
||||
extern unsigned short fw2100tp_code01[];
|
||||
extern unsigned short fw2100tp_length01;
|
||||
|
||||
static struct qla_fw_info qla_fw_tbl[] = {
|
||||
{
|
||||
.addressing = FW_INFO_ADDR_NORMAL,
|
||||
.fwcode = &fw2100tp_code01[0],
|
||||
.fwlen = &fw2100tp_length01,
|
||||
.fwstart = &fw2100tp_addr01,
|
||||
},
|
||||
|
||||
{ FW_INFO_ADDR_NOMORE, },
|
||||
};
|
||||
|
||||
static struct qla_board_info qla_board_tbl = {
|
||||
.drv_name = qla_driver_name,
|
||||
|
||||
.isp_name = "ISP2100",
|
||||
.fw_info = qla_fw_tbl,
|
||||
};
|
||||
|
||||
static struct pci_device_id qla2100_pci_tbl[] = {
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QLOGIC,
|
||||
.device = PCI_DEVICE_ID_QLOGIC_ISP2100,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (unsigned long)&qla_board_tbl,
|
||||
},
|
||||
|
||||
{0, 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, qla2100_pci_tbl);
|
||||
|
||||
static int __devinit
|
||||
qla2100_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
return qla2x00_probe_one(pdev,
|
||||
(struct qla_board_info *)id->driver_data);
|
||||
}
|
||||
|
||||
static void __devexit
|
||||
qla2100_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
qla2x00_remove_one(pdev);
|
||||
}
|
||||
|
||||
static struct pci_driver qla2100_pci_driver = {
|
||||
.name = "qla2100",
|
||||
.id_table = qla2100_pci_tbl,
|
||||
.probe = qla2100_probe_one,
|
||||
.remove = __devexit_p(qla2100_remove_one),
|
||||
};
|
||||
|
||||
static int __init
|
||||
qla2100_init(void)
|
||||
{
|
||||
return pci_module_init(&qla2100_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
qla2100_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&qla2100_pci_driver);
|
||||
}
|
||||
|
||||
module_init(qla2100_init);
|
||||
module_exit(qla2100_exit);
|
||||
|
||||
MODULE_AUTHOR("QLogic Corporation");
|
||||
MODULE_DESCRIPTION("QLogic ISP21xx FC-SCSI Host Bus Adapter driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(QLA2XXX_VERSION);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* QLogic Fibre Channel HBA Driver
|
||||
* Copyright (C) 2003 Christoph Hellwig.
|
||||
* Copyright (c) 2003-2005 QLogic Corporation
|
||||
*
|
||||
* See LICENSE.qla2xxx for copyright and licensing details.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "qla_def.h"
|
||||
|
||||
static char qla_driver_name[] = "qla2200";
|
||||
|
||||
extern unsigned char fw2200tp_version[];
|
||||
extern unsigned char fw2200tp_version_str[];
|
||||
extern unsigned short fw2200tp_addr01;
|
||||
extern unsigned short fw2200tp_code01[];
|
||||
extern unsigned short fw2200tp_length01;
|
||||
|
||||
static struct qla_fw_info qla_fw_tbl[] = {
|
||||
{
|
||||
.addressing = FW_INFO_ADDR_NORMAL,
|
||||
.fwcode = &fw2200tp_code01[0],
|
||||
.fwlen = &fw2200tp_length01,
|
||||
.fwstart = &fw2200tp_addr01,
|
||||
},
|
||||
|
||||
{ FW_INFO_ADDR_NOMORE, },
|
||||
};
|
||||
|
||||
static struct qla_board_info qla_board_tbl = {
|
||||
.drv_name = qla_driver_name,
|
||||
|
||||
.isp_name = "ISP2200",
|
||||
.fw_info = qla_fw_tbl,
|
||||
};
|
||||
|
||||
static struct pci_device_id qla2200_pci_tbl[] = {
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QLOGIC,
|
||||
.device = PCI_DEVICE_ID_QLOGIC_ISP2200,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (unsigned long)&qla_board_tbl,
|
||||
},
|
||||
|
||||
{0, 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, qla2200_pci_tbl);
|
||||
|
||||
static int __devinit
|
||||
qla2200_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
return qla2x00_probe_one(pdev,
|
||||
(struct qla_board_info *)id->driver_data);
|
||||
}
|
||||
|
||||
static void __devexit
|
||||
qla2200_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
qla2x00_remove_one(pdev);
|
||||
}
|
||||
|
||||
static struct pci_driver qla2200_pci_driver = {
|
||||
.name = "qla2200",
|
||||
.id_table = qla2200_pci_tbl,
|
||||
.probe = qla2200_probe_one,
|
||||
.remove = __devexit_p(qla2200_remove_one),
|
||||
};
|
||||
|
||||
static int __init
|
||||
qla2200_init(void)
|
||||
{
|
||||
return pci_module_init(&qla2200_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
qla2200_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&qla2200_pci_driver);
|
||||
}
|
||||
|
||||
module_init(qla2200_init);
|
||||
module_exit(qla2200_exit);
|
||||
|
||||
MODULE_AUTHOR("QLogic Corporation");
|
||||
MODULE_DESCRIPTION("QLogic ISP22xx FC-SCSI Host Bus Adapter driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(QLA2XXX_VERSION);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* QLogic Fibre Channel HBA Driver
|
||||
* Copyright (C) 2003 Christoph Hellwig.
|
||||
* Copyright (c) 2003-2005 QLogic Corporation
|
||||
*
|
||||
* See LICENSE.qla2xxx for copyright and licensing details.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "qla_def.h"
|
||||
|
||||
static char qla_driver_name[] = "qla2300";
|
||||
|
||||
extern unsigned char fw2300ipx_version[];
|
||||
extern unsigned char fw2300ipx_version_str[];
|
||||
extern unsigned short fw2300ipx_addr01;
|
||||
extern unsigned short fw2300ipx_code01[];
|
||||
extern unsigned short fw2300ipx_length01;
|
||||
|
||||
static struct qla_fw_info qla_fw_tbl[] = {
|
||||
{
|
||||
.addressing = FW_INFO_ADDR_NORMAL,
|
||||
.fwcode = &fw2300ipx_code01[0],
|
||||
.fwlen = &fw2300ipx_length01,
|
||||
.fwstart = &fw2300ipx_addr01,
|
||||
},
|
||||
{ FW_INFO_ADDR_NOMORE, },
|
||||
};
|
||||
|
||||
static struct qla_board_info qla_board_tbl[] = {
|
||||
{
|
||||
.drv_name = qla_driver_name,
|
||||
.isp_name = "ISP2300",
|
||||
.fw_info = qla_fw_tbl,
|
||||
},
|
||||
{
|
||||
.drv_name = qla_driver_name,
|
||||
.isp_name = "ISP2312",
|
||||
.fw_info = qla_fw_tbl,
|
||||
},
|
||||
{
|
||||
.drv_name = qla_driver_name,
|
||||
.isp_name = "ISP6312",
|
||||
.fw_info = qla_fw_tbl,
|
||||
},
|
||||
};
|
||||
|
||||
static struct pci_device_id qla2300_pci_tbl[] = {
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QLOGIC,
|
||||
.device = PCI_DEVICE_ID_QLOGIC_ISP2300,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (unsigned long)&qla_board_tbl[0],
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QLOGIC,
|
||||
.device = PCI_DEVICE_ID_QLOGIC_ISP2312,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (unsigned long)&qla_board_tbl[1],
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QLOGIC,
|
||||
.device = PCI_DEVICE_ID_QLOGIC_ISP6312,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (unsigned long)&qla_board_tbl[2],
|
||||
},
|
||||
{0, 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, qla2300_pci_tbl);
|
||||
|
||||
static int __devinit
|
||||
qla2300_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
return qla2x00_probe_one(pdev,
|
||||
(struct qla_board_info *)id->driver_data);
|
||||
}
|
||||
|
||||
static void __devexit
|
||||
qla2300_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
qla2x00_remove_one(pdev);
|
||||
}
|
||||
|
||||
static struct pci_driver qla2300_pci_driver = {
|
||||
.name = "qla2300",
|
||||
.id_table = qla2300_pci_tbl,
|
||||
.probe = qla2300_probe_one,
|
||||
.remove = __devexit_p(qla2300_remove_one),
|
||||
};
|
||||
|
||||
static int __init
|
||||
qla2300_init(void)
|
||||
{
|
||||
return pci_module_init(&qla2300_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
qla2300_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&qla2300_pci_driver);
|
||||
}
|
||||
|
||||
module_init(qla2300_init);
|
||||
module_exit(qla2300_exit);
|
||||
|
||||
MODULE_AUTHOR("QLogic Corporation");
|
||||
MODULE_DESCRIPTION("QLogic ISP23xx FC-SCSI Host Bus Adapter driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(QLA2XXX_VERSION);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* QLogic Fibre Channel HBA Driver
|
||||
* Copyright (c) 2003-2005 QLogic Corporation
|
||||
*
|
||||
* See LICENSE.qla2xxx for copyright and licensing details.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "qla_def.h"
|
||||
|
||||
static char qla_driver_name[] = "qla2322";
|
||||
|
||||
extern unsigned char fw2322ipx_version[];
|
||||
extern unsigned char fw2322ipx_version_str[];
|
||||
extern unsigned short fw2322ipx_addr01;
|
||||
extern unsigned short fw2322ipx_code01[];
|
||||
extern unsigned short fw2322ipx_length01;
|
||||
extern unsigned long rseqipx_code_addr01;
|
||||
extern unsigned short rseqipx_code01[];
|
||||
extern unsigned short rseqipx_code_length01;
|
||||
extern unsigned long xseqipx_code_addr01;
|
||||
extern unsigned short xseqipx_code01[];
|
||||
extern unsigned short xseqipx_code_length01;
|
||||
|
||||
static struct qla_fw_info qla_fw_tbl[] = {
|
||||
{
|
||||
.addressing = FW_INFO_ADDR_NORMAL,
|
||||
.fwcode = &fw2322ipx_code01[0],
|
||||
.fwlen = &fw2322ipx_length01,
|
||||
.fwstart = &fw2322ipx_addr01,
|
||||
},
|
||||
{
|
||||
.addressing = FW_INFO_ADDR_EXTENDED,
|
||||
.fwcode = &rseqipx_code01[0],
|
||||
.fwlen = &rseqipx_code_length01,
|
||||
.lfwstart = &rseqipx_code_addr01,
|
||||
},
|
||||
{
|
||||
.addressing = FW_INFO_ADDR_EXTENDED,
|
||||
.fwcode = &xseqipx_code01[0],
|
||||
.fwlen = &xseqipx_code_length01,
|
||||
.lfwstart = &xseqipx_code_addr01,
|
||||
},
|
||||
{ FW_INFO_ADDR_NOMORE, },
|
||||
};
|
||||
|
||||
static struct qla_board_info qla_board_tbl[] = {
|
||||
{
|
||||
.drv_name = qla_driver_name,
|
||||
.isp_name = "ISP2322",
|
||||
.fw_info = qla_fw_tbl,
|
||||
},
|
||||
{
|
||||
.drv_name = qla_driver_name,
|
||||
.isp_name = "ISP6322",
|
||||
.fw_info = qla_fw_tbl,
|
||||
},
|
||||
};
|
||||
|
||||
static struct pci_device_id qla2322_pci_tbl[] = {
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QLOGIC,
|
||||
.device = PCI_DEVICE_ID_QLOGIC_ISP2322,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (unsigned long)&qla_board_tbl[0],
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QLOGIC,
|
||||
.device = PCI_DEVICE_ID_QLOGIC_ISP6322,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (unsigned long)&qla_board_tbl[1],
|
||||
},
|
||||
{0, 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, qla2322_pci_tbl);
|
||||
|
||||
static int __devinit
|
||||
qla2322_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
return qla2x00_probe_one(pdev,
|
||||
(struct qla_board_info *)id->driver_data);
|
||||
}
|
||||
|
||||
static void __devexit
|
||||
qla2322_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
qla2x00_remove_one(pdev);
|
||||
}
|
||||
|
||||
static struct pci_driver qla2322_pci_driver = {
|
||||
.name = "qla2322",
|
||||
.id_table = qla2322_pci_tbl,
|
||||
.probe = qla2322_probe_one,
|
||||
.remove = __devexit_p(qla2322_remove_one),
|
||||
};
|
||||
|
||||
static int __init
|
||||
qla2322_init(void)
|
||||
{
|
||||
return pci_module_init(&qla2322_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
qla2322_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&qla2322_pci_driver);
|
||||
}
|
||||
|
||||
module_init(qla2322_init);
|
||||
module_exit(qla2322_exit);
|
||||
|
||||
MODULE_AUTHOR("QLogic Corporation");
|
||||
MODULE_DESCRIPTION("QLogic ISP2322 FC-SCSI Host Bus Adapter driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(QLA2XXX_VERSION);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* QLogic Fibre Channel HBA Driver
|
||||
* Copyright (c) 2003-2005 QLogic Corporation
|
||||
*
|
||||
* See LICENSE.qla2xxx for copyright and licensing details.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "qla_def.h"
|
||||
|
||||
static char qla_driver_name[] = "qla2400";
|
||||
|
||||
extern uint32_t fw2400_version_str[];
|
||||
extern uint32_t fw2400_addr01;
|
||||
extern uint32_t fw2400_code01[];
|
||||
extern uint32_t fw2400_length01;
|
||||
extern uint32_t fw2400_addr02;
|
||||
extern uint32_t fw2400_code02[];
|
||||
extern uint32_t fw2400_length02;
|
||||
|
||||
static struct qla_fw_info qla_fw_tbl[] = {
|
||||
{
|
||||
.addressing = FW_INFO_ADDR_EXTENDED,
|
||||
.fwcode = (unsigned short *)&fw2400_code01[0],
|
||||
.fwlen = (unsigned short *)&fw2400_length01,
|
||||
.lfwstart = (unsigned long *)&fw2400_addr01,
|
||||
},
|
||||
{
|
||||
.addressing = FW_INFO_ADDR_EXTENDED,
|
||||
.fwcode = (unsigned short *)&fw2400_code02[0],
|
||||
.fwlen = (unsigned short *)&fw2400_length02,
|
||||
.lfwstart = (unsigned long *)&fw2400_addr02,
|
||||
},
|
||||
{ FW_INFO_ADDR_NOMORE, },
|
||||
};
|
||||
|
||||
static struct qla_board_info qla_board_tbl[] = {
|
||||
{
|
||||
.drv_name = qla_driver_name,
|
||||
.isp_name = "ISP2422",
|
||||
.fw_info = qla_fw_tbl,
|
||||
.fw_fname = "ql2400_fw.bin",
|
||||
},
|
||||
{
|
||||
.drv_name = qla_driver_name,
|
||||
.isp_name = "ISP2432",
|
||||
.fw_info = qla_fw_tbl,
|
||||
.fw_fname = "ql2400_fw.bin",
|
||||
},
|
||||
{
|
||||
.drv_name = qla_driver_name,
|
||||
.isp_name = "ISP5422",
|
||||
.fw_info = qla_fw_tbl,
|
||||
.fw_fname = "ql2400_fw.bin",
|
||||
},
|
||||
{
|
||||
.drv_name = qla_driver_name,
|
||||
.isp_name = "ISP5432",
|
||||
.fw_info = qla_fw_tbl,
|
||||
.fw_fname = "ql2400_fw.bin",
|
||||
},
|
||||
};
|
||||
|
||||
static struct pci_device_id qla24xx_pci_tbl[] = {
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QLOGIC,
|
||||
.device = PCI_DEVICE_ID_QLOGIC_ISP2422,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (unsigned long)&qla_board_tbl[0],
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QLOGIC,
|
||||
.device = PCI_DEVICE_ID_QLOGIC_ISP2432,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (unsigned long)&qla_board_tbl[1],
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QLOGIC,
|
||||
.device = PCI_DEVICE_ID_QLOGIC_ISP5422,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (unsigned long)&qla_board_tbl[2],
|
||||
},
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_QLOGIC,
|
||||
.device = PCI_DEVICE_ID_QLOGIC_ISP5432,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (unsigned long)&qla_board_tbl[3],
|
||||
},
|
||||
|
||||
{0, 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, qla24xx_pci_tbl);
|
||||
|
||||
static int __devinit
|
||||
qla24xx_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
return qla2x00_probe_one(pdev,
|
||||
(struct qla_board_info *)id->driver_data);
|
||||
}
|
||||
|
||||
static void __devexit
|
||||
qla24xx_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
qla2x00_remove_one(pdev);
|
||||
}
|
||||
|
||||
static struct pci_driver qla24xx_pci_driver = {
|
||||
.name = "qla2400",
|
||||
.id_table = qla24xx_pci_tbl,
|
||||
.probe = qla24xx_probe_one,
|
||||
.remove = __devexit_p(qla24xx_remove_one),
|
||||
};
|
||||
|
||||
static int __init
|
||||
qla24xx_init(void)
|
||||
{
|
||||
return pci_module_init(&qla24xx_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
qla24xx_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&qla24xx_pci_driver);
|
||||
}
|
||||
|
||||
module_init(qla24xx_init);
|
||||
module_exit(qla24xx_exit);
|
||||
|
||||
MODULE_AUTHOR("QLogic Corporation");
|
||||
MODULE_DESCRIPTION("QLogic ISP24xx FC-SCSI Host Bus Adapter driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(QLA2XXX_VERSION);
|
||||
+12346
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
Summary of changes between versions 0.9.3.8 and 0.9.3.9
|
||||
-------------------------------------------------------
|
||||
Patch vs: qla2xxx v8.01.03-k (in kernels 2.6.15.x+, including 2.6.16-prex)
|
||||
|
||||
- Fixed problem when "out-of-order frames rassembly" bit was wrongly set,
|
||||
which lead to only 1Gbps speed of 23xx cards.
|
||||
|
||||
- Removed all code that deals with "use_sg == 0" data, since staring
|
||||
from SCST 0.9.5 commands data are always in SG-vector.
|
||||
|
||||
- Fixed hungup on the module unload.
|
||||
|
||||
- Minor cleanups and fixes
|
||||
|
||||
Summary of changes between versions 0.9.3.7 and 0.9.3.8
|
||||
-------------------------------------------------------
|
||||
Patch vs: qla2xxx v8.01.03-k (in kernels 2.6.15.x+, including 2.6.16-prex)
|
||||
|
||||
- Minor cleanups
|
||||
|
||||
Summary of changes between versions 0.9.3.6 and 0.9.3.7
|
||||
-------------------------------------------------------
|
||||
Patch vs: qla2xxx v8.01.03-k (in kernels 2.6.15.x+, including 2.6.16-prex)
|
||||
|
||||
- Fix null pointer dereference in do_ctio_complete (thanks to Calvin Morrow)
|
||||
|
||||
Summary of changes between versions 0.9.3.5 and 0.9.3.6
|
||||
-------------------------------------------------------
|
||||
Patch vs: qla2xxx v8.01.03-k (in kernels 2.6.15.x+, including 2.6.16-prex)
|
||||
|
||||
- Include MEMSET, MEMCPY definitions
|
||||
|
||||
- Patched to compile on 64bit cleanly (thanks to Ming Zhang <mingz@ele.uri.edu>
|
||||
for patch)
|
||||
|
||||
- Fixed possible race during initial enabling of Target Mode (thanks to
|
||||
Mark Buechler <mark.buechler@gmail.com> for finding)
|
||||
|
||||
Summary of changes between versions 0.9.3.4 and 0.9.3.5
|
||||
-------------------------------------------------------
|
||||
Patch vs: qla2xxx v8.01.03-k (in kernels 2.6.15.x)
|
||||
|
||||
- Reset chip when switching from initiator to initator/target and back
|
||||
implemented
|
||||
|
||||
- Use 2K loop_id's for 23xx chips and thus change how sessions are
|
||||
tracked.
|
||||
|
||||
- Added Module Info
|
||||
|
||||
- Fixed module counting with initiator module preventing it from going
|
||||
away w/out telling us (pointed out by Vladislav Bolkhovitin
|
||||
<vst@vlnb.net>)
|
||||
|
||||
- Some minor debugging changes
|
||||
|
||||
Summary of changes between versions 0.9.3.2 of old driver and 0.9.3.4
|
||||
---------------------------------------------------------------------
|
||||
|
||||
- Support for the latest in-tree Qlogic initiator added by Nathaniel
|
||||
Clark <nate@misrule.us>
|
||||
|
||||
- Only one module is build and it interacts with a patched kernel
|
||||
qla2xxx driver. The single module is called qla2x00tgt.ko instead of
|
||||
having one for each driver type.
|
||||
|
||||
- Target mode can be enabled via a sysfs interface on a per card basis.
|
||||
Under the appropriate scsi_host there is now an entry
|
||||
target_mode_enabled, which can read and written.
|
||||
|
||||
NOTES:
|
||||
|
||||
The initiator's req_pkt function has never updated the request queue pointers.
|
||||
So the implementation for continuation packets, has never worked correctly.
|
||||
@@ -0,0 +1,84 @@
|
||||
#
|
||||
# Qlogic 2x00 SCSI target driver makefile
|
||||
#
|
||||
# Copyright (C) 2004 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
# and Leonid Stoljar
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#
|
||||
# Main targets:
|
||||
# all (the default) : make all
|
||||
# clean : clean files
|
||||
# extraclean : clean + clean dependencies
|
||||
# install : install
|
||||
# uninstall : uninstall
|
||||
#
|
||||
# Notes :
|
||||
# - install and uninstall must be made as root
|
||||
#
|
||||
|
||||
#SCST_INC_DIR := /usr/local/include/scst
|
||||
SCST_INC_DIR := $(SUBDIRS)/../../scst/include
|
||||
SCST_DIR := $(shell pwd)/../../scst/src
|
||||
EXTRA_CFLAGS += -I$(SCST_INC_DIR) -DFC_TARGET_SUPPORT
|
||||
EXTRA_CFLAGS += -DFC_SCST_WWN_AUTH
|
||||
EXTRA_CFLAGS += -DEXTRACHECKS
|
||||
|
||||
INSTALL_DIR := /lib/modules/$(shell uname -r)/extra
|
||||
|
||||
#EXTRA_CFLAGS += -DTRACING
|
||||
EXTRA_CFLAGS += -DDEBUG_TGT -DDEBUG_WORK_IN_THREAD
|
||||
#EXTRA_CFLAGS += -DDEBUG_WORK_IN_THREAD
|
||||
|
||||
ifeq ($(KVER),)
|
||||
ifeq ($(KDIR),)
|
||||
KDIR := /lib/modules/$(shell uname -r)/build
|
||||
endif
|
||||
else
|
||||
KDIR := /lib/modules/$(KVER)/build
|
||||
endif
|
||||
|
||||
QLA2XXX_INC_DIR := $(KDIR)/drivers/scsi/qla2xxx
|
||||
EXTRA_CFLAGS += -I$(QLA2XXX_INC_DIR)
|
||||
|
||||
ifneq ($(KERNELRELEASE),)
|
||||
obj-m := qla2x00tgt.o
|
||||
qla2x00tgt-objs := qla2x00t.o
|
||||
|
||||
else
|
||||
|
||||
all: Modules.symvers
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m
|
||||
|
||||
tgt: Modules.symvers
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=n
|
||||
|
||||
install: all
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_INI=m \
|
||||
modules_install
|
||||
-depmod -a
|
||||
|
||||
Modules.symvers: $(SCST_DIR)/Modules.symvers
|
||||
-cp $(SCST_DIR)/Modules.symvers .
|
||||
|
||||
uninstall:
|
||||
rm -f $(INSTALL_DIR)/qla2[23x]00tgt.ko
|
||||
-/sbin/depmod -a
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend *~ Modules.symvers
|
||||
rm -rf .tmp_versions
|
||||
|
||||
extraclean: clean
|
||||
|
||||
.PHONY: all tgt install uninstall clean extraclean
|
||||
@@ -0,0 +1,99 @@
|
||||
Target driver for Qlogic 2200/2300 Fibre Channel cards
|
||||
======================================================
|
||||
|
||||
Version 0.9.5-pre1, XX XXX 2006
|
||||
-------------------------------
|
||||
|
||||
This driver has all required features and looks to be quite stable (for
|
||||
beta) and useful. It is designed to work in conjunction with the
|
||||
initiator driver from Linux kernel version 2.6, which is intended to
|
||||
perform all the initialization and shutdown tasks. This driver needs to
|
||||
be patched to enable the target mode and provide all necessary
|
||||
callbacks, but it's still capable to work as initiator only. Mode, when
|
||||
a host acts as the initiator and the target simultaneously, is supported
|
||||
as well.
|
||||
|
||||
This version is compatible with SCST version 0.9.5 and higher.
|
||||
|
||||
Tested on stable kernels from http://www.kernel.org
|
||||
|
||||
NOTE: WWN-based authentification on 2200 cards doesn't always work from
|
||||
---- the first "connect", but works on all subsequent attempts.
|
||||
On this error message "Unable to find wwn login for loop
|
||||
id XX, using loop id instead" is logged. So, just try again, eg
|
||||
unload/load FC card driver on the initiator host. This is the
|
||||
current limitation. Everyone who fix it or found a usable
|
||||
workaround is welcome.
|
||||
|
||||
See also "ToDo" file for list of known issues and unimplemented
|
||||
features.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
At first, make sure that the link "/lib/modules/`you_kernel_version`/build"
|
||||
points to the source code for your currently running kernel.
|
||||
|
||||
Then, edit Makefile and set SCST_INC_DIR variable to point to the
|
||||
directory, where SCST's public include files are located. If you install
|
||||
QLA2x00 target driver's source code in the SCST's directory, then
|
||||
SCST_INC_DIR will be set correctly for you ("../include").
|
||||
|
||||
The driver consists of two parts: the patch for initiator driver
|
||||
"linux-2.6-qla2xxx-target.patch" and target driver in the
|
||||
"qla2x00-target" directory.
|
||||
|
||||
Patch the kernel tree with "linux-2.6-qla2xxx-target.patch" patch.
|
||||
|
||||
To compile, type 'make'. It will build qla2x00tgt.ko module.
|
||||
|
||||
To install, type 'make install'. The target driver will be installed in
|
||||
/lib/modules/`you_kernel_version`/kernel/drivers/scsi/scsi_tgt.
|
||||
|
||||
To uninstall, type 'make uninstall'.
|
||||
|
||||
The initiator driver must be loaded before the target one.
|
||||
|
||||
Then the target mode should be enabled via a sysfs interface on a per
|
||||
card basis. Under the appropriate scsi_host there is an entry
|
||||
target_mode_enabled, where you should write "1", like:
|
||||
|
||||
echo "1" >/sys/class/scsi_host/host0/target_mode_enabled
|
||||
|
||||
Compilation options
|
||||
-------------------
|
||||
|
||||
There are the following compilation options, that could be commented
|
||||
in/out in Makefile:
|
||||
|
||||
- FC_SCST_WWN_AUTH - turns on using remote initiator's WWN as
|
||||
authentification name for scst_register_session_ex(). See note above
|
||||
about limitations of this mode. If it is off, LOOP ID used instead of
|
||||
WWN.
|
||||
|
||||
- DEBUG_TGT - turns on some debugging code, including some logging. Makes
|
||||
the driver considerably bigger and slower, producing large amount of
|
||||
log data.
|
||||
|
||||
- TRACING - turns on ability to log events. Makes the driver considerably
|
||||
bigger and lead to some performance loss.
|
||||
|
||||
- DEBUG_WORK_IN_THREAD - makes SCST process incoming commands and
|
||||
call the driver's callbacks in SCST thread context instead of tasklet
|
||||
one. Useful for debugging and lead to some performance loss.
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
Thanks to
|
||||
|
||||
* Nathaniel Clark <nate@misrule.us> for porting to new 2.6 kernel
|
||||
initiator driver.
|
||||
|
||||
* Mark Buechler <mark.buechler@gmail.com> for the original
|
||||
WWN-based authentification, a lot of useful suggestions, bug reports and
|
||||
help in debugging.
|
||||
|
||||
* Ming Zhang <mingz@ele.uri.edu> for his fixes.
|
||||
|
||||
Vladislav Bolkhovitin <vst@vlnb.net>, http://scst.sourceforge.net
|
||||
@@ -0,0 +1,29 @@
|
||||
Known issues and unimplemented features
|
||||
---------------------------------------
|
||||
|
||||
- If a Linux initiator asks for devices using INQUIRY command too soon
|
||||
before the controller on the target is fully initialized in the
|
||||
target mode, the initiator could receive garbage devices and the
|
||||
messages "scsi: unknown device type 31" will be printed in the kernel
|
||||
log on the initiator. After rescan on the initiator (eg by
|
||||
rmmod/modprobe the driver) the correct devices will be added instead
|
||||
of the garbage ones. This is actually Linux SCSI mid-level's problem
|
||||
(although also could be the Qlogic firmware's one): type 31 is
|
||||
"unknown or no device type", but the SCSI mid-level treats such
|
||||
devices as real and adds them to the system. To avoid it, try to
|
||||
leave the target enough time for initialization (some 10th seconds).
|
||||
|
||||
- Minor "ToDo"'s spread in the code.
|
||||
|
||||
- SNS support.
|
||||
|
||||
- Support for sense with length > 26 (is it possible at all?)
|
||||
|
||||
- Queue types (simple, ordered, etc.) support (needs appropriate support
|
||||
from SCST and Linux kernel)
|
||||
|
||||
- On 2300 if on a tape with block size 0 we write block with size X
|
||||
and then read it with bs <X the tape skips all blocks with size X
|
||||
until the next correct block or filemark found, instead of returning
|
||||
ILI with negative counter. Looks like the initiator retries the
|
||||
command quetly. 2200 works correctly.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* qla2x00t.h
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* Leonid Stoljar
|
||||
* Nathaniel Clark <nate@misrule.us>
|
||||
*
|
||||
* Significant modification 2006 by Nathaniel Clark <nate@misrule.us>
|
||||
*
|
||||
* Qlogic 2x00 SCSI target driver.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __QLA2X00T_H
|
||||
#define __QLA2X00T_H
|
||||
|
||||
/* Sneaky hack to skip redefinitions from qla_dbg.h */
|
||||
#define __QLA_DBG_H
|
||||
#include <qla_def.h>
|
||||
#include "qla2x_tgt_def.h"
|
||||
|
||||
/* Version numbers, the same as for the kernel */
|
||||
#define Q2T_VERSION(a,b,c,d) (((a) << 030) + ((b) << 020) + (c) << 010 + (d))
|
||||
#define Q2T_VERSION_CODE Q2T_VERSION(0,9,5,0)
|
||||
#define Q2T_VERSION_STRING "0.9.5-pre1"
|
||||
|
||||
#define Q2T_MAX_CDB_LEN 16
|
||||
#define Q2T_TIMEOUT 10 /* in seconds */
|
||||
|
||||
/* Immediate notify status constants */
|
||||
#define IMM_NTFY_LIP_RESET 0x000E
|
||||
#define IMM_NTFY_IOCB_OVERFLOW 0x0016
|
||||
#define IMM_NTFY_ABORT_TASK 0x0020
|
||||
#define IMM_NTFY_PORT_LOGOUT 0x0029
|
||||
#define IMM_NTFY_PORT_CONFIG 0x002A
|
||||
#define IMM_NTFY_GLBL_TPRLO 0x002D
|
||||
#define IMM_NTFY_GLBL_LOGO 0x002E
|
||||
#define IMM_NTFY_RESOURCE 0x0034
|
||||
#define IMM_NTFY_MSG_RX 0x0036
|
||||
|
||||
/* Immediate notify task flags */
|
||||
#define IMM_NTFY_CLEAR_ACA 0x4000
|
||||
#define IMM_NTFY_TARGET_RESET 0x2000
|
||||
#define IMM_NTFY_LUN_RESET 0x1000
|
||||
#define IMM_NTFY_CLEAR_TS 0x0400
|
||||
#define IMM_NTFY_ABORT_TS 0x0200
|
||||
|
||||
/* Notify Acknowledge flags */
|
||||
#define NOTIFY_ACK_RES_COUNT BIT_8
|
||||
#define NOTIFY_ACK_CLEAR_LIP_RESET BIT_5
|
||||
#define NOTIFY_ACK_TM_RESP_CODE_VALID BIT_4
|
||||
|
||||
/* Command's states */
|
||||
#define Q2T_STATE_NEW 0 /* New command and SCST processes it */
|
||||
#define Q2T_STATE_PROCESSED 1 /* SCST done processing */
|
||||
#define Q2T_STATE_NEED_DATA 2 /* SCST needs data to process */
|
||||
#define Q2T_STATE_DATA_IN 3 /* Data arrived and SCST processes it */
|
||||
#define Q2T_STATE_ABORTED 4 /* Command aborted */
|
||||
|
||||
/* Misc */
|
||||
#define Q2T_NULL_HANDLE 0
|
||||
#define Q2T_SKIP_HANDLE (0xFFFFFFFE & ~CTIO_COMPLETION_HANDLE_MARK)
|
||||
#define Q2T_BUSY_HANDLE (0xFFFFFFFF & ~CTIO_COMPLETION_HANDLE_MARK)
|
||||
#define Q2T_DISABLE_LUN_STATUS_NOT_SET -1
|
||||
|
||||
/* ATIO task_codes fields */
|
||||
#define ATIO_SIMPLE_QUEUE 0
|
||||
#define ATIO_HEAD_OF_QUEUE 1
|
||||
#define ATIO_ORDERED_QUEUE 2
|
||||
#define ATIO_ACA_QUEUE 4
|
||||
#define ATIO_UNTAGGED 5
|
||||
|
||||
/* CTIO additional flag ToDo: move to the appropriate place */
|
||||
#define OF_TERM_EXCH BIT_14 /* Terminate exchange */
|
||||
|
||||
/* ToDo: move to the appropriate place */
|
||||
#define CTIO_INVALID_RX_ID 0x08
|
||||
#define CTIO_TIMEOUT 0x0B
|
||||
#define CTIO_LIP_RESET 0x0E
|
||||
#define CTIO_TARGET_RESET 0x17
|
||||
#define CTIO_PORT_UNAVAILABLE 0x28
|
||||
#define CTIO_PORT_LOGGED_OUT 0x29
|
||||
|
||||
/* TM failed response code, see FCP */
|
||||
#define FC_TM_FAILED 0x5
|
||||
|
||||
#if (BITS_PER_LONG > 32) || defined(CONFIG_HIGHMEM64G)
|
||||
#define pci_dma_lo32(a) (a & 0xffffffff)
|
||||
#define pci_dma_hi32(a) ((((a) >> 16)>>16) & 0xffffffff)
|
||||
#else
|
||||
#define pci_dma_lo32(a) (a & 0xffffffff)
|
||||
#define pci_dma_hi32(a) 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Equivilant to IT Nexus (Initiator-Target)
|
||||
*/
|
||||
struct q2t_sess
|
||||
{
|
||||
struct list_head list;
|
||||
struct scst_session *scst_sess;
|
||||
struct q2t_tgt *tgt;
|
||||
int loop_id;
|
||||
};
|
||||
|
||||
struct q2t_cmd
|
||||
{
|
||||
struct q2t_sess *sess;
|
||||
struct scst_cmd *scst_cmd;
|
||||
int state;
|
||||
atio_entry_t atio;
|
||||
dma_addr_t dma_handle;
|
||||
uint32_t iocb_cnt;
|
||||
};
|
||||
|
||||
struct q2t_tgt
|
||||
{
|
||||
struct scst_tgt *scst_tgt;
|
||||
uint32_t handle;
|
||||
scsi_qla_host_t *ha;
|
||||
int datasegs_per_cmd, datasegs_per_cont;
|
||||
/* Target's flags, serialized by ha->hardware_lock */
|
||||
unsigned int tgt_shutdown:1; /* The driver is being released */
|
||||
unsigned int tgt_enable_64bit_addr:1; /* 64-bits PCI addressing anables */
|
||||
wait_queue_head_t waitQ;
|
||||
int notify_ack_expected;
|
||||
volatile int modify_lun_expected;
|
||||
volatile int disable_lun_status;
|
||||
atomic_t sess_count; /* count sessions refing q2t_tgt */
|
||||
struct list_head sess_list;
|
||||
};
|
||||
|
||||
struct q2t_mgmt_cmd
|
||||
{
|
||||
struct q2t_sess *sess;
|
||||
notify_entry_t notify_entry;
|
||||
};
|
||||
|
||||
struct q2t_prm
|
||||
{
|
||||
struct q2t_tgt *tgt;
|
||||
uint16_t req_cnt;
|
||||
uint16_t seg_cnt;
|
||||
int sg_cnt;
|
||||
struct scatterlist *sg;
|
||||
int bufflen;
|
||||
scst_data_direction data_direction;
|
||||
uint16_t rq_result;
|
||||
uint16_t scsi_status;
|
||||
unsigned char *sense_buffer;
|
||||
unsigned int sense_buffer_len;
|
||||
int residual;
|
||||
struct q2t_cmd *cmd;
|
||||
ctio_common_entry_t *pkt;
|
||||
};
|
||||
|
||||
/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */
|
||||
static inline struct q2t_sess *q2t_find_sess_by_lid(struct q2t_tgt *tgt,
|
||||
uint16_t lid)
|
||||
{
|
||||
struct q2t_sess *sess, *sess_tmp;
|
||||
BUG_ON(tgt == NULL);
|
||||
list_for_each_entry_safe(sess, sess_tmp, &tgt->sess_list, list) {
|
||||
if (lid == (sess->loop_id))
|
||||
return sess;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* __QLA2X00T_H */
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* qla2x_tgt.h
|
||||
*
|
||||
* Copyright (C) 2004-2005 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* Additional file for the target driver support.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* This should be included only from within qla2xxx module.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __QLA2X_TGT_H
|
||||
#define __QLA2X_TGT_H
|
||||
|
||||
#ifndef FC_TARGET_SUPPORT
|
||||
#error __FILE__ " included without FC_TARGET_SUPPORT"
|
||||
#endif
|
||||
|
||||
#include "qla2x_tgt_def.h"
|
||||
|
||||
extern struct qla2x_tgt_initiator qla_target;
|
||||
|
||||
/* declare here because it doesn't appear in any .h file just in qla_iocb.c */
|
||||
extern request_t *qla2x00_req_pkt(scsi_qla_host_t *ha);
|
||||
|
||||
/********************************************************************\
|
||||
* ISP Queue types left out of new QLogic driver (from old version)
|
||||
\********************************************************************/
|
||||
|
||||
/*
|
||||
* qla2x00_do_en_dis_lun
|
||||
* Issue enable or disable LUN entry IOCB.
|
||||
* Also set enable_target_mode in ha
|
||||
*
|
||||
* Input:
|
||||
* ha = adapter block pointer.
|
||||
*/
|
||||
/* Caller MUST have hardware lock held */
|
||||
static inline void
|
||||
__qla2x00_en_dis_lun(scsi_qla_host_t *ha, int enable)
|
||||
{
|
||||
elun_entry_t *pkt;
|
||||
|
||||
if ((pkt = (elun_entry_t *)qla2x00_req_pkt(ha)) != NULL) {
|
||||
pkt->entry_type = ENABLE_LUN_TYPE;
|
||||
if (enable) {
|
||||
pkt->command_count = QLA2X00_COMMAND_COUNT_INIT;
|
||||
pkt->immed_notify_count = QLA2X00_IMMED_NOTIFY_COUNT_INIT;
|
||||
pkt->timeout = 0xffff;
|
||||
}
|
||||
else
|
||||
{
|
||||
pkt->command_count = 0;
|
||||
pkt->immed_notify_count = 0;
|
||||
pkt->timeout = 0;
|
||||
}
|
||||
DEBUG2(printk(KERN_DEBUG
|
||||
"scsi%lu:ENABLE_LUN IOCB imm %u cmd %u timeout %u\n",
|
||||
ha->host_no, pkt->immed_notify_count,
|
||||
pkt->command_count, pkt->timeout));
|
||||
|
||||
/* Issue command to ISP */
|
||||
qla2x00_isp_cmd(ha);
|
||||
ha->flags.enable_target_mode = enable;
|
||||
}
|
||||
#if defined(QL_DEBUG_LEVEL_2) || defined(QL_DEBUG_LEVEL_3)
|
||||
if (!pkt)
|
||||
printk("qla2100_en_dis_lun: **** FAILED ****\n");
|
||||
#endif
|
||||
}
|
||||
/* get-lock version */
|
||||
static inline void
|
||||
qla2x00_en_dis_lun(scsi_qla_host_t *ha, int enable)
|
||||
{
|
||||
unsigned long flags = 0;
|
||||
|
||||
spin_lock_irqsave(&ha->hardware_lock, flags);
|
||||
__qla2x00_en_dis_lun(ha, enable);
|
||||
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* qla2x00_enable_lun
|
||||
* Issue enable LUN entry IOCB.
|
||||
*
|
||||
* Input:
|
||||
* ha = adapter block pointer.
|
||||
*/
|
||||
static inline void
|
||||
qla2x00_enable_lun(scsi_qla_host_t *ha)
|
||||
{
|
||||
qla2x00_en_dis_lun(ha, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* qla2x00_disable_lun
|
||||
* Issue enable LUN entry IOCB (command_count = 0).
|
||||
*
|
||||
* Input:
|
||||
* ha = adapter block pointer.
|
||||
*/
|
||||
static inline void
|
||||
qla2x00_disable_lun(scsi_qla_host_t *ha)
|
||||
{
|
||||
qla2x00_en_dis_lun(ha, 0);
|
||||
}
|
||||
|
||||
#endif /* __QLA2X_TGT_H */
|
||||
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* qla2x_tgt_def.h
|
||||
*
|
||||
* Copyright (C) 2004-2005 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* Additional file for the target driver support. Intended to define
|
||||
* for 2200 and 2300 thier own exported symbols with unique names.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* This is the global def file that is useful for including from the
|
||||
* target portion.
|
||||
*/
|
||||
|
||||
#ifndef __QLA2X_TGT_DEF_H
|
||||
#define __QLA2X_TGT_DEF_H
|
||||
|
||||
#include "qla_def.h"
|
||||
|
||||
#ifndef FC_TARGET_SUPPORT
|
||||
#error __FILE__ " included without FC_TARGET_SUPPORT"
|
||||
#endif
|
||||
|
||||
#ifndef ENTER
|
||||
#define ENTER(a)
|
||||
#endif
|
||||
|
||||
#ifndef LEAVE
|
||||
#define LEAVE(a)
|
||||
#endif
|
||||
|
||||
#define QLA2X_TARGET_MAGIC 153
|
||||
#define QLA2X_INITIATOR_MAGIC 54203
|
||||
|
||||
#define QLA2X00_COMMAND_COUNT_INIT 250
|
||||
#define QLA2X00_IMMED_NOTIFY_COUNT_INIT 250
|
||||
|
||||
#define QLA_EXTENDED_LUN 1
|
||||
|
||||
/*
|
||||
* Used to mark which completion handles (for RIO Status's) are for CTIO's
|
||||
* vs. regular (non-target) info.
|
||||
*/
|
||||
#define CTIO_COMPLETION_HANDLE_MARK BIT_15
|
||||
#if (CTIO_COMPLETION_HANDLE_MARK <= MAX_OUTSTANDING_COMMANDS)
|
||||
#error "Hackish CTIO_COMPLETION_HANDLE_MARK no longer larger than MAX_OUTSTANDING_COMMANDS"
|
||||
#endif
|
||||
#define HANDLE_IS_CTIO_COMP(h) (h & CTIO_COMPLETION_HANDLE_MARK)
|
||||
|
||||
#ifndef OF_SS_MODE_0
|
||||
/*
|
||||
* ISP target entries - Flags bit definitions.
|
||||
*/
|
||||
#define OF_SS_MODE_0 0
|
||||
#define OF_SS_MODE_1 1
|
||||
#define OF_SS_MODE_2 2
|
||||
#define OF_SS_MODE_3 3
|
||||
|
||||
#define OF_RESET BIT_5 /* Reset LIP flag */
|
||||
#define OF_DATA_IN BIT_6 /* Data in to initiator */
|
||||
/* (data from target to initiator) */
|
||||
#define OF_DATA_OUT BIT_7 /* Data out from initiator */
|
||||
/* (data from initiator to target) */
|
||||
#define OF_NO_DATA (BIT_7 | BIT_6)
|
||||
#define OF_INC_RC BIT_8 /* Increment command resource count */
|
||||
#define OF_FAST_POST BIT_9 /* Enable mailbox fast posting. */
|
||||
#define OF_SSTS BIT_15 /* Send SCSI status */
|
||||
#endif
|
||||
|
||||
#ifndef DATASEGS_PER_COMMAND32
|
||||
#define DATASEGS_PER_COMMAND32 3
|
||||
#define DATASEGS_PER_CONT32 7
|
||||
#define QLA_MAX_SG32(ql) \
|
||||
(DATASEGS_PER_COMMAND32 + (((ql) > 0) ? DATASEGS_PER_CONT32*((ql) - 1) : 0))
|
||||
|
||||
#define DATASEGS_PER_COMMAND64 2
|
||||
#define DATASEGS_PER_CONT64 5
|
||||
#define QLA_MAX_SG64(ql) \
|
||||
(DATASEGS_PER_COMMAND64 + (((ql) > 0) ? DATASEGS_PER_CONT64*((ql) - 1) : 0))
|
||||
#endif
|
||||
|
||||
/********************************************************************\
|
||||
* ISP Queue types left out of new QLogic driver (from old version)
|
||||
\********************************************************************/
|
||||
|
||||
#ifndef ENABLE_LUN_TYPE
|
||||
#define ENABLE_LUN_TYPE 0x0B /* Enable LUN entry. */
|
||||
/*!
|
||||
* ISP queue - enable LUN entry structure definition.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
uint8_t entry_count; /* Entry count. */
|
||||
uint8_t sys_define; /* System defined. */
|
||||
uint8_t entry_status; /* Entry Status. */
|
||||
uint32_t sys_define_2; /* System defined. */
|
||||
uint8_t reserved_8;
|
||||
uint8_t reserved_1;
|
||||
uint16_t reserved_2;
|
||||
uint32_t reserved_3;
|
||||
uint8_t status;
|
||||
uint8_t reserved_4;
|
||||
uint8_t command_count; /* Number of ATIOs allocated. */
|
||||
uint8_t immed_notify_count; /* Number of Immediate Notify entries allocated. */
|
||||
uint16_t reserved_5;
|
||||
uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
|
||||
uint16_t reserved_6[20];
|
||||
} elun_entry_t;
|
||||
#define ENABLE_LUN_SUCCESS 0x01
|
||||
#define ENABLE_LUN_RC_NONZERO 0x04
|
||||
#define ENABLE_LUN_INVALID_REQUEST 0x06
|
||||
#define ENABLE_LUN_ALREADY_ENABLED 0x3E
|
||||
#endif
|
||||
|
||||
#ifndef MODIFY_LUN_TYPE
|
||||
#define MODIFY_LUN_TYPE 0x0C //!< Modify LUN entry.
|
||||
/*
|
||||
* ISP queue - modify LUN entry structure definition.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
uint8_t entry_count; /* Entry count. */
|
||||
uint8_t sys_define; /* System defined. */
|
||||
uint8_t entry_status; /* Entry Status. */
|
||||
uint32_t sys_define_2; /* System defined. */
|
||||
uint8_t reserved_8;
|
||||
uint8_t reserved_1;
|
||||
uint8_t operators;
|
||||
uint8_t reserved_2;
|
||||
uint32_t reserved_3;
|
||||
uint8_t status;
|
||||
uint8_t reserved_4;
|
||||
uint8_t command_count; /* Number of ATIOs allocated. */
|
||||
uint8_t immed_notify_count; /* Number of Immediate Notify */
|
||||
/* entries allocated. */
|
||||
uint16_t reserved_5;
|
||||
uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
|
||||
uint16_t reserved_7[20];
|
||||
}modify_lun_entry_t;
|
||||
#define MODIFY_LUN_SUCCESS 0x01
|
||||
#define MODIFY_LUN_CMD_ADD BIT_0
|
||||
#define MODIFY_LUN_CMD_SUB BIT_1
|
||||
#define MODIFY_LUN_IMM_ADD BIT_2
|
||||
#define MODIFY_LUN_IMM_SUB BIT_3
|
||||
#endif
|
||||
|
||||
|
||||
#define GET_TARGET_ID(ha, iocb) ((HAS_EXTENDED_IDS(ha)) \
|
||||
? le16_to_cpu((iocb)->target.extended) \
|
||||
: (uint16_t)(iocb)->target.id.standard)
|
||||
|
||||
#ifndef IMMED_NOTIFY_TYPE
|
||||
#define IMMED_NOTIFY_TYPE 0x0D/* Immediate notify entry. */
|
||||
/*
|
||||
* ISP queue - immediate notify entry structure definition.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
uint8_t entry_count; /* Entry count. */
|
||||
uint8_t sys_define; /* System defined. */
|
||||
uint8_t entry_status; /* Entry Status. */
|
||||
uint32_t sys_define_2; /* System defined. */
|
||||
target_id_t target;
|
||||
uint16_t lun;
|
||||
uint32_t reserved_2;
|
||||
uint16_t status;
|
||||
uint16_t task_flags;
|
||||
uint16_t seq_id;
|
||||
uint16_t reserved_5[11];
|
||||
uint16_t scsi_status;
|
||||
uint8_t sense_data[18];
|
||||
}notify_entry_t;
|
||||
#endif
|
||||
|
||||
#ifndef NOTIFY_ACK_TYPE
|
||||
#define NOTIFY_ACK_TYPE 0x0E /* Notify acknowledge entry. */
|
||||
/*
|
||||
* ISP queue - notify acknowledge entry structure definition.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
uint8_t entry_count; /* Entry count. */
|
||||
uint8_t sys_define; /* System defined. */
|
||||
uint8_t entry_status; /* Entry Status. */
|
||||
uint32_t sys_define_2; /* System defined. */
|
||||
target_id_t target;
|
||||
uint8_t reserved_1;
|
||||
uint8_t target_id;
|
||||
uint16_t flags;
|
||||
uint16_t resp_code;
|
||||
uint16_t status;
|
||||
uint16_t task_flags;
|
||||
uint16_t seq_id;
|
||||
uint16_t reserved_3[21];
|
||||
}nack_entry_t;
|
||||
#define NOTIFY_ACK_SUCCESS 0x01
|
||||
#endif
|
||||
|
||||
#ifndef ACCEPT_TGT_IO_TYPE
|
||||
#define ACCEPT_TGT_IO_TYPE 0x16 /* Accept target I/O entry. */
|
||||
/*
|
||||
* ISP queue - Accept Target I/O (ATIO) entry structure definition.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
uint8_t entry_count; /* Entry count. */
|
||||
uint8_t sys_define; /* System defined. */
|
||||
uint8_t entry_status; /* Entry Status. */
|
||||
uint32_t sys_define_2; /* System defined. */
|
||||
target_id_t target;
|
||||
uint16_t exchange_id;
|
||||
uint16_t flags;
|
||||
uint16_t status;
|
||||
uint8_t command_ref;
|
||||
uint8_t task_codes;
|
||||
uint8_t task_flags;
|
||||
uint8_t execution_codes;
|
||||
uint8_t cdb[MAX_CMDSZ];
|
||||
uint32_t data_length;
|
||||
uint16_t lun;
|
||||
uint8_t initiator_port_name[WWN_SIZE]; /* on qla23xx */
|
||||
uint16_t reserved_32[6];
|
||||
uint16_t ox_id;
|
||||
}atio_entry_t;
|
||||
#endif
|
||||
|
||||
#ifndef CONTINUE_TGT_IO_TYPE
|
||||
#define CONTINUE_TGT_IO_TYPE 0x17
|
||||
/*
|
||||
* ISP queue - Continue Target I/O (CTIO) entry for status mode 0
|
||||
* structure definition.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
uint8_t entry_count; /* Entry count. */
|
||||
uint8_t sys_define; /* System defined. */
|
||||
uint8_t entry_status; /* Entry Status. */
|
||||
uint32_t handle; /* System defined handle */
|
||||
target_id_t target;
|
||||
uint16_t exchange_id;
|
||||
uint16_t flags;
|
||||
uint16_t status;
|
||||
uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
|
||||
uint16_t dseg_count; /* Data segment count. */
|
||||
uint32_t relative_offset;
|
||||
uint32_t residual;
|
||||
uint16_t reserved_1[3];
|
||||
uint16_t scsi_status;
|
||||
uint32_t transfer_length;
|
||||
uint32_t dseg_0_address[0];
|
||||
}ctio_common_entry_t;
|
||||
#define ATIO_PATH_INVALID 0x07
|
||||
#define ATIO_CANT_PROV_CAP 0x16
|
||||
#define ATIO_CDB_VALID 0x3D
|
||||
|
||||
#define ATIO_EXEC_READ BIT_1
|
||||
#define ATIO_EXEC_WRITE BIT_0
|
||||
#endif
|
||||
|
||||
#ifndef CTIO_A64_TYPE
|
||||
#define CTIO_A64_TYPE 0x1F
|
||||
typedef struct
|
||||
{
|
||||
ctio_common_entry_t common;
|
||||
uint32_t dseg_0_address; /* Data segment 0 address. */
|
||||
uint32_t dseg_0_length; /* Data segment 0 length. */
|
||||
uint32_t dseg_1_address; /* Data segment 1 address. */
|
||||
uint32_t dseg_1_length; /* Data segment 1 length. */
|
||||
uint32_t dseg_2_address; /* Data segment 2 address. */
|
||||
uint32_t dseg_2_length; /* Data segment 2 length. */
|
||||
}ctio_entry_t;
|
||||
#define CTIO_SUCCESS 0x01
|
||||
#define CTIO_ABORTED 0x02
|
||||
#define CTIO_ERROR_INVALID_RX_ID 0x08
|
||||
#define CTIO_ERROR_PORT_LOGGED_OUT 0x29
|
||||
#endif
|
||||
|
||||
#ifndef CTIO_RET_TYPE
|
||||
#define CTIO_RET_TYPE 0x17 /* CTIO return entry */
|
||||
/*
|
||||
* ISP queue - CTIO returned entry structure definition.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
uint8_t entry_count; /* Entry count. */
|
||||
uint8_t sys_define; /* System defined. */
|
||||
uint8_t entry_status; /* Entry Status. */
|
||||
uint32_t handle; /* System defined handle. */
|
||||
target_id_t target;
|
||||
uint16_t exchange_id;
|
||||
uint16_t flags;
|
||||
uint16_t status;
|
||||
uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
|
||||
uint16_t dseg_count; /* Data segment count. */
|
||||
uint32_t relative_offset;
|
||||
uint32_t residual;
|
||||
uint16_t reserved_1[2];
|
||||
uint16_t sense_length;
|
||||
uint16_t scsi_status;
|
||||
uint16_t response_length;
|
||||
uint8_t sense_data[26];
|
||||
}ctio_ret_entry_t;
|
||||
#endif
|
||||
|
||||
/********************************************************************\
|
||||
* Type Definitions used by initiator & target halves
|
||||
\********************************************************************/
|
||||
|
||||
typedef enum {
|
||||
DISABLE_TARGET_MODE = 0,
|
||||
ENABLE_TARGET_MODE = 1
|
||||
} qla2x_tgt_host_action_t;
|
||||
|
||||
struct qla2x_tgt_initiator
|
||||
{
|
||||
int magic;
|
||||
|
||||
/* Callbacks */
|
||||
void (*tgt_response_pkt)(scsi_qla_host_t *ha, sts_entry_t *pkt);
|
||||
void (*tgt_ctio_completion)(scsi_qla_host_t *ha, uint32_t handle);
|
||||
void (*tgt_async_event)(uint16_t code, scsi_qla_host_t *ha, uint16_t *mailbox);
|
||||
void (*tgt_host_action)(scsi_qla_host_t *ha, qla2x_tgt_host_action_t action);
|
||||
};
|
||||
|
||||
struct qla2x_tgt_target
|
||||
{
|
||||
int magic;
|
||||
|
||||
/* Callbacks - H/W lock MUST be held while calling any*/
|
||||
request_t *(*req_pkt)(scsi_qla_host_t *ha);
|
||||
void (*isp_cmd)(scsi_qla_host_t *ha);
|
||||
void (*enable_lun)(scsi_qla_host_t *ha);
|
||||
void (*disable_lun)(scsi_qla_host_t *ha);
|
||||
int (*issue_marker)(scsi_qla_host_t *ha);
|
||||
cont_entry_t *(*req_cont_pkt)(scsi_qla_host_t *ha);
|
||||
int (*get_counts)(scsi_qla_host_t *ha, uint8_t *cmd, uint8_t *imm);
|
||||
};
|
||||
|
||||
int qla2xxx_tgt_register_driver(/* IN */ struct qla2x_tgt_initiator *tgt,
|
||||
/* OUT */ struct qla2x_tgt_target *init);
|
||||
|
||||
void qla2xxx_tgt_unregister_driver(void);
|
||||
|
||||
|
||||
/* from qla_os.c for __qla2x00_host_reset in qla_init.c*/
|
||||
int
|
||||
qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha);
|
||||
int
|
||||
qla2x00_wait_for_hba_online(scsi_qla_host_t *ha);
|
||||
int
|
||||
qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha);
|
||||
|
||||
#endif
|
||||
+1183
File diff suppressed because it is too large
Load Diff
+2113
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* QLogic Fibre Channel HBA Driver
|
||||
* Copyright (c) 2003-2005 QLogic Corporation
|
||||
*
|
||||
* See LICENSE.qla2xxx for copyright and licensing details.
|
||||
*/
|
||||
/*
|
||||
* Driver debug definitions.
|
||||
*/
|
||||
/* #define QL_DEBUG_LEVEL_1 */ /* Output register accesses to COM1 */
|
||||
/* #define QL_DEBUG_LEVEL_2 */ /* Output error msgs to COM1 */
|
||||
/* #define QL_DEBUG_LEVEL_3 */ /* Output function trace msgs to COM1 */
|
||||
/* #define QL_DEBUG_LEVEL_4 */ /* Output NVRAM trace msgs to COM1 */
|
||||
/* #define QL_DEBUG_LEVEL_5 */ /* Output ring trace msgs to COM1 */
|
||||
/* #define QL_DEBUG_LEVEL_6 */ /* Output WATCHDOG timer trace to COM1 */
|
||||
/* #define QL_DEBUG_LEVEL_7 */ /* Output RISC load trace msgs to COM1 */
|
||||
/* #define QL_DEBUG_LEVEL_8 */ /* Output ring saturation msgs to COM1 */
|
||||
/* #define QL_DEBUG_LEVEL_9 */ /* Output IOCTL trace msgs */
|
||||
/* #define QL_DEBUG_LEVEL_10 */ /* Output IOCTL error msgs */
|
||||
/* #define QL_DEBUG_LEVEL_11 */ /* Output Mbx Cmd trace msgs */
|
||||
/* #define QL_DEBUG_LEVEL_12 */ /* Output IP trace msgs */
|
||||
/* #define QL_DEBUG_LEVEL_13 */ /* Output fdmi function trace msgs */
|
||||
/* #define QL_DEBUG_LEVEL_14 */ /* Output RSCN trace msgs */
|
||||
/*
|
||||
* Local Macro Definitions.
|
||||
*/
|
||||
#if defined(QL_DEBUG_LEVEL_1) || defined(QL_DEBUG_LEVEL_2) || \
|
||||
defined(QL_DEBUG_LEVEL_3) || defined(QL_DEBUG_LEVEL_4) || \
|
||||
defined(QL_DEBUG_LEVEL_5) || defined(QL_DEBUG_LEVEL_6) || \
|
||||
defined(QL_DEBUG_LEVEL_7) || defined(QL_DEBUG_LEVEL_8) || \
|
||||
defined(QL_DEBUG_LEVEL_9) || defined(QL_DEBUG_LEVEL_10) || \
|
||||
defined(QL_DEBUG_LEVEL_11) || defined(QL_DEBUG_LEVEL_12) || \
|
||||
defined(QL_DEBUG_LEVEL_13) || defined(QL_DEBUG_LEVEL_14)
|
||||
#define QL_DEBUG_ROUTINES
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Macros use for debugging the driver.
|
||||
*/
|
||||
#undef ENTER_TRACE
|
||||
#if defined(ENTER_TRACE)
|
||||
#define ENTER(x) do { printk("qla2100 : Entering %s()\n", x); } while (0)
|
||||
#define LEAVE(x) do { printk("qla2100 : Leaving %s()\n", x); } while (0)
|
||||
#define ENTER_INTR(x) do { printk("qla2100 : Entering %s()\n", x); } while (0)
|
||||
#define LEAVE_INTR(x) do { printk("qla2100 : Leaving %s()\n", x); } while (0)
|
||||
#else
|
||||
#define ENTER(x) do {} while (0)
|
||||
#define LEAVE(x) do {} while (0)
|
||||
#define ENTER_INTR(x) do {} while (0)
|
||||
#define LEAVE_INTR(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
#if DEBUG_QLA2100
|
||||
#define DEBUG(x) do {x;} while (0);
|
||||
#else
|
||||
#define DEBUG(x) do {} while (0);
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_1)
|
||||
#define DEBUG1(x) do {x;} while (0);
|
||||
#else
|
||||
#define DEBUG1(x) do {} while (0);
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_2)
|
||||
#define DEBUG2(x) do {x;} while (0);
|
||||
#define DEBUG2_3(x) do {x;} while (0);
|
||||
#define DEBUG2_3_11(x) do {x;} while (0);
|
||||
#define DEBUG2_9_10(x) do {x;} while (0);
|
||||
#define DEBUG2_11(x) do {x;} while (0);
|
||||
#define DEBUG2_13(x) do {x;} while (0);
|
||||
#else
|
||||
#define DEBUG2(x) do {} while (0);
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_3)
|
||||
#define DEBUG3(x) do {x;} while (0);
|
||||
#define DEBUG2_3(x) do {x;} while (0);
|
||||
#define DEBUG2_3_11(x) do {x;} while (0);
|
||||
#define DEBUG3_11(x) do {x;} while (0);
|
||||
#else
|
||||
#define DEBUG3(x) do {} while (0);
|
||||
#if !defined(QL_DEBUG_LEVEL_2)
|
||||
#define DEBUG2_3(x) do {} while (0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_4)
|
||||
#define DEBUG4(x) do {x;} while (0);
|
||||
#else
|
||||
#define DEBUG4(x) do {} while (0);
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_5)
|
||||
#define DEBUG5(x) do {x;} while (0);
|
||||
#else
|
||||
#define DEBUG5(x) do {} while (0);
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_7)
|
||||
#define DEBUG7(x) do {x;} while (0);
|
||||
#else
|
||||
#define DEBUG7(x) do {} while (0);
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_9)
|
||||
#define DEBUG9(x) do {x;} while (0);
|
||||
#define DEBUG9_10(x) do {x;} while (0);
|
||||
#define DEBUG2_9_10(x) do {x;} while (0);
|
||||
#else
|
||||
#define DEBUG9(x) do {} while (0);
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_10)
|
||||
#define DEBUG10(x) do {x;} while (0);
|
||||
#define DEBUG2_9_10(x) do {x;} while (0);
|
||||
#define DEBUG9_10(x) do {x;} while (0);
|
||||
#else
|
||||
#define DEBUG10(x) do {} while (0);
|
||||
#if !defined(DEBUG2_9_10)
|
||||
#define DEBUG2_9_10(x) do {} while (0);
|
||||
#endif
|
||||
#if !defined(DEBUG9_10)
|
||||
#define DEBUG9_10(x) do {} while (0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_11)
|
||||
#define DEBUG11(x) do{x;} while(0);
|
||||
#if !defined(DEBUG2_11)
|
||||
#define DEBUG2_11(x) do{x;} while(0);
|
||||
#endif
|
||||
#if !defined(DEBUG2_3_11)
|
||||
#define DEBUG2_3_11(x) do{x;} while(0);
|
||||
#endif
|
||||
#if !defined(DEBUG3_11)
|
||||
#define DEBUG3_11(x) do{x;} while(0);
|
||||
#endif
|
||||
#else
|
||||
#define DEBUG11(x) do{} while(0);
|
||||
#if !defined(QL_DEBUG_LEVEL_2)
|
||||
#define DEBUG2_11(x) do{} while(0);
|
||||
#if !defined(QL_DEBUG_LEVEL_3)
|
||||
#define DEBUG2_3_11(x) do{} while(0);
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(QL_DEBUG_LEVEL_3)
|
||||
#define DEBUG3_11(x) do{} while(0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_12)
|
||||
#define DEBUG12(x) do {x;} while (0);
|
||||
#else
|
||||
#define DEBUG12(x) do {} while (0);
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_13)
|
||||
#define DEBUG13(x) do {x;} while (0)
|
||||
#if !defined(DEBUG2_13)
|
||||
#define DEBUG2_13(x) do {x;} while(0)
|
||||
#endif
|
||||
#else
|
||||
#define DEBUG13(x) do {} while (0)
|
||||
#if !defined(QL_DEBUG_LEVEL_2)
|
||||
#define DEBUG2_13(x) do {} while(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(QL_DEBUG_LEVEL_14)
|
||||
#define DEBUG14(x) do {x;} while (0)
|
||||
#else
|
||||
#define DEBUG14(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Firmware Dump structure definition
|
||||
*/
|
||||
#define FW_DUMP_SIZE_128K 0xBC000
|
||||
#define FW_DUMP_SIZE_512K 0x2FC000
|
||||
#define FW_DUMP_SIZE_1M 0x5FC000
|
||||
|
||||
struct qla2300_fw_dump {
|
||||
uint16_t hccr;
|
||||
uint16_t pbiu_reg[8];
|
||||
uint16_t risc_host_reg[8];
|
||||
uint16_t mailbox_reg[32];
|
||||
uint16_t resp_dma_reg[32];
|
||||
uint16_t dma_reg[48];
|
||||
uint16_t risc_hdw_reg[16];
|
||||
uint16_t risc_gp0_reg[16];
|
||||
uint16_t risc_gp1_reg[16];
|
||||
uint16_t risc_gp2_reg[16];
|
||||
uint16_t risc_gp3_reg[16];
|
||||
uint16_t risc_gp4_reg[16];
|
||||
uint16_t risc_gp5_reg[16];
|
||||
uint16_t risc_gp6_reg[16];
|
||||
uint16_t risc_gp7_reg[16];
|
||||
uint16_t frame_buf_hdw_reg[64];
|
||||
uint16_t fpm_b0_reg[64];
|
||||
uint16_t fpm_b1_reg[64];
|
||||
uint16_t risc_ram[0xf800];
|
||||
uint16_t stack_ram[0x1000];
|
||||
uint16_t data_ram[1];
|
||||
};
|
||||
|
||||
struct qla2100_fw_dump {
|
||||
uint16_t hccr;
|
||||
uint16_t pbiu_reg[8];
|
||||
uint16_t mailbox_reg[32];
|
||||
uint16_t dma_reg[48];
|
||||
uint16_t risc_hdw_reg[16];
|
||||
uint16_t risc_gp0_reg[16];
|
||||
uint16_t risc_gp1_reg[16];
|
||||
uint16_t risc_gp2_reg[16];
|
||||
uint16_t risc_gp3_reg[16];
|
||||
uint16_t risc_gp4_reg[16];
|
||||
uint16_t risc_gp5_reg[16];
|
||||
uint16_t risc_gp6_reg[16];
|
||||
uint16_t risc_gp7_reg[16];
|
||||
uint16_t frame_buf_hdw_reg[16];
|
||||
uint16_t fpm_b0_reg[64];
|
||||
uint16_t fpm_b1_reg[64];
|
||||
uint16_t risc_ram[0xf000];
|
||||
};
|
||||
|
||||
#define FW_DUMP_SIZE_24XX 0x2B0000
|
||||
|
||||
struct qla24xx_fw_dump {
|
||||
uint32_t host_status;
|
||||
uint32_t host_reg[32];
|
||||
uint32_t shadow_reg[7];
|
||||
uint16_t mailbox_reg[32];
|
||||
uint32_t xseq_gp_reg[128];
|
||||
uint32_t xseq_0_reg[16];
|
||||
uint32_t xseq_1_reg[16];
|
||||
uint32_t rseq_gp_reg[128];
|
||||
uint32_t rseq_0_reg[16];
|
||||
uint32_t rseq_1_reg[16];
|
||||
uint32_t rseq_2_reg[16];
|
||||
uint32_t cmd_dma_reg[16];
|
||||
uint32_t req0_dma_reg[15];
|
||||
uint32_t resp0_dma_reg[15];
|
||||
uint32_t req1_dma_reg[15];
|
||||
uint32_t xmt0_dma_reg[32];
|
||||
uint32_t xmt1_dma_reg[32];
|
||||
uint32_t xmt2_dma_reg[32];
|
||||
uint32_t xmt3_dma_reg[32];
|
||||
uint32_t xmt4_dma_reg[32];
|
||||
uint32_t xmt_data_dma_reg[16];
|
||||
uint32_t rcvt0_data_dma_reg[32];
|
||||
uint32_t rcvt1_data_dma_reg[32];
|
||||
uint32_t risc_gp_reg[128];
|
||||
uint32_t lmc_reg[112];
|
||||
uint32_t fpm_hdw_reg[192];
|
||||
uint32_t fb_hdw_reg[176];
|
||||
uint32_t code_ram[0x2000];
|
||||
uint32_t ext_mem[1];
|
||||
};
|
||||
+2598
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,81 @@
|
||||
#define QLA_MODEL_NAMES 0x4A
|
||||
|
||||
/*
|
||||
* Adapter model names and descriptions.
|
||||
*/
|
||||
static char *qla2x00_model_name[QLA_MODEL_NAMES*2] = {
|
||||
"QLA2340", "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x100 */
|
||||
"QLA2342", "133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x101 */
|
||||
"QLA2344", "133MHz PCI-X to 2Gb FC, Quad Channel", /* 0x102 */
|
||||
"QCP2342", "cPCI to 2Gb FC, Dual Channel", /* 0x103 */
|
||||
"QSB2340", "SBUS to 2Gb FC, Single Channel", /* 0x104 */
|
||||
"QSB2342", "SBUS to 2Gb FC, Dual Channel", /* 0x105 */
|
||||
"QLA2310", "Sun 66MHz PCI-X to 2Gb FC, Single Channel", /* 0x106 */
|
||||
"QLA2332", "Sun 66MHz PCI-X to 2Gb FC, Single Channel", /* 0x107 */
|
||||
"QCP2332", "Sun cPCI to 2Gb FC, Dual Channel", /* 0x108 */
|
||||
"QCP2340", "cPCI to 2Gb FC, Single Channel", /* 0x109 */
|
||||
"QLA2342", "Sun 133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x10a */
|
||||
"QCP2342", "Sun - cPCI to 2Gb FC, Dual Channel", /* 0x10b */
|
||||
"QLA2350", "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x10c */
|
||||
"QLA2352", "133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x10d */
|
||||
"QLA2352", "Sun 133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x10e */
|
||||
" ", " ", /* 0x10f */
|
||||
" ", " ", /* 0x110 */
|
||||
" ", " ", /* 0x111 */
|
||||
" ", " ", /* 0x112 */
|
||||
" ", " ", /* 0x113 */
|
||||
" ", " ", /* 0x114 */
|
||||
"QLA2360", "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x115 */
|
||||
"QLA2362", "133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x116 */
|
||||
"QLE2360", "PCI-Express to 2Gb FC, Single Channel", /* 0x117 */
|
||||
"QLE2362", "PCI-Express to 2Gb FC, Dual Channel", /* 0x118 */
|
||||
"QLA200", "133MHz PCI-X to 2Gb FC Optical", /* 0x119 */
|
||||
" ", " ", /* 0x11a */
|
||||
" ", " ", /* 0x11b */
|
||||
"QLA200P", "133MHz PCI-X to 2Gb FC SFP", /* 0x11c */
|
||||
" ", " ", /* 0x11d */
|
||||
" ", " ", /* 0x11e */
|
||||
" ", " ", /* 0x11f */
|
||||
" ", " ", /* 0x120 */
|
||||
" ", " ", /* 0x121 */
|
||||
" ", " ", /* 0x122 */
|
||||
" ", " ", /* 0x123 */
|
||||
" ", " ", /* 0x124 */
|
||||
" ", " ", /* 0x125 */
|
||||
" ", " ", /* 0x126 */
|
||||
" ", " ", /* 0x127 */
|
||||
" ", " ", /* 0x128 */
|
||||
" ", " ", /* 0x129 */
|
||||
" ", " ", /* 0x12a */
|
||||
" ", " ", /* 0x12b */
|
||||
" ", " ", /* 0x12c */
|
||||
" ", " ", /* 0x12d */
|
||||
" ", " ", /* 0x12e */
|
||||
"QLA210", "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x12f */
|
||||
"EMC 250", "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x130 */
|
||||
"HP A7538A", "HP 1p2g PCI-X to 2Gb FC, Single Channel", /* 0x131 */
|
||||
"QLA210", "Sun 133MHz PCI-X to 2Gb FC, Single Channel", /* 0x132 */
|
||||
"QLA2460", "PCI-X 2.0 to 4Gb FC, Single Channel", /* 0x133 */
|
||||
"QLA2462", "PCI-X 2.0 to 4Gb FC, Dual Channel", /* 0x134 */
|
||||
"QMC2462", "IBM eServer BC 4Gb FC Expansion Card", /* 0x135 */
|
||||
"QMC2462S", "IBM eServer BC 4Gb FC Expansion Card SFF", /* 0x136 */
|
||||
"QLE2460", "PCI-Express to 4Gb FC, Single Channel", /* 0x137 */
|
||||
"QLE2462", "PCI-Express to 4Gb FC, Dual Channel", /* 0x138 */
|
||||
"QME2462", "Dell BS PCI-Express to 4Gb FC, Dual Channel", /* 0x139 */
|
||||
" ", " ", /* 0x13a */
|
||||
" ", " ", /* 0x13b */
|
||||
" ", " ", /* 0x13c */
|
||||
"QEM2462", "Sun Server I/O Module 4Gb FC, Dual Channel", /* 0x13d */
|
||||
"QLE210", "PCI-Express to 2Gb FC, Single Channel", /* 0x13e */
|
||||
"QLE220", "PCI-Express to 4Gb FC, Single Channel", /* 0x13f */
|
||||
"QLA2460", "Sun PCI-X 2.0 to 4Gb FC, Single Channel", /* 0x140 */
|
||||
"QLA2462", "Sun PCI-X 2.0 to 4Gb FC, Dual Channel", /* 0x141 */
|
||||
"QLE2460", "Sun PCI-Express to 2Gb FC, Single Channel", /* 0x142 */
|
||||
"QLE2462", "Sun PCI-Express to 4Gb FC, Single Channel", /* 0x143 */
|
||||
"QEM2462" "Server I/O Module 4Gb FC, Dual Channel", /* 0x144 */
|
||||
"QLE2440", "PCI-Express to 4Gb FC, Single Channel", /* 0x145 */
|
||||
"QLE2464", "PCI-Express to 4Gb FC, Quad Channel", /* 0x146 */
|
||||
"QLA2440", "PCI-X 2.0 to 4Gb FC, Single Channel", /* 0x147 */
|
||||
" ", " ", /* 0x148 */
|
||||
"QLA2340", "Sun 133MHz PCI-X to 2Gb FC, Single Channel", /* 0x149 */
|
||||
};
|
||||
+1059
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* QLogic Fibre Channel HBA Driver
|
||||
* Copyright (c) 2003-2005 QLogic Corporation
|
||||
*
|
||||
* See LICENSE.qla2xxx for copyright and licensing details.
|
||||
*/
|
||||
#ifndef __QLA_GBL_H
|
||||
#define __QLA_GBL_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
extern void qla2x00_remove_one(struct pci_dev *);
|
||||
extern int qla2x00_probe_one(struct pci_dev *, struct qla_board_info *);
|
||||
|
||||
/*
|
||||
* Global Function Prototypes in qla_init.c source file.
|
||||
*/
|
||||
extern int qla2x00_initialize_adapter(scsi_qla_host_t *);
|
||||
|
||||
extern int qla2100_pci_config(struct scsi_qla_host *);
|
||||
extern int qla2300_pci_config(struct scsi_qla_host *);
|
||||
extern int qla24xx_pci_config(scsi_qla_host_t *);
|
||||
extern void qla2x00_reset_chip(struct scsi_qla_host *);
|
||||
extern void qla24xx_reset_chip(struct scsi_qla_host *);
|
||||
extern int qla2x00_chip_diag(struct scsi_qla_host *);
|
||||
extern int qla24xx_chip_diag(struct scsi_qla_host *);
|
||||
extern void qla2x00_config_rings(struct scsi_qla_host *);
|
||||
extern void qla24xx_config_rings(struct scsi_qla_host *);
|
||||
extern void qla2x00_reset_adapter(struct scsi_qla_host *);
|
||||
extern void qla24xx_reset_adapter(struct scsi_qla_host *);
|
||||
extern int qla2x00_nvram_config(struct scsi_qla_host *);
|
||||
extern int qla24xx_nvram_config(struct scsi_qla_host *);
|
||||
extern void qla2x00_update_fw_options(struct scsi_qla_host *);
|
||||
extern void qla24xx_update_fw_options(scsi_qla_host_t *);
|
||||
extern int qla2x00_load_risc(struct scsi_qla_host *, uint32_t *);
|
||||
extern int qla24xx_load_risc(scsi_qla_host_t *, uint32_t *);
|
||||
extern int qla24xx_load_risc_flash(scsi_qla_host_t *, uint32_t *);
|
||||
|
||||
extern fc_port_t *qla2x00_alloc_fcport(scsi_qla_host_t *, gfp_t);
|
||||
|
||||
extern int qla2x00_loop_resync(scsi_qla_host_t *);
|
||||
|
||||
extern int qla2x00_find_new_loop_id(scsi_qla_host_t *, fc_port_t *);
|
||||
extern int qla2x00_fabric_login(scsi_qla_host_t *, fc_port_t *, uint16_t *);
|
||||
extern int qla2x00_local_device_login(scsi_qla_host_t *, fc_port_t *);
|
||||
|
||||
extern void qla2x00_restart_queues(scsi_qla_host_t *, uint8_t);
|
||||
|
||||
extern void qla2x00_rescan_fcports(scsi_qla_host_t *);
|
||||
extern void qla2x00_update_fcports(scsi_qla_host_t *);
|
||||
|
||||
extern int qla2x00_abort_isp(scsi_qla_host_t *);
|
||||
|
||||
extern void qla2x00_update_fcport(scsi_qla_host_t *, fc_port_t *);
|
||||
extern void qla2x00_reg_remote_port(scsi_qla_host_t *, fc_port_t *);
|
||||
|
||||
/*
|
||||
* Global Data in qla_os.c source file.
|
||||
*/
|
||||
extern char qla2x00_version_str[];
|
||||
|
||||
extern int ql2xlogintimeout;
|
||||
extern int qlport_down_retry;
|
||||
extern int ql2xplogiabsentdevice;
|
||||
extern int ql2xloginretrycount;
|
||||
extern int ql2xfdmienable;
|
||||
extern int ql2xprocessrscn;
|
||||
|
||||
extern void qla2x00_sp_compl(scsi_qla_host_t *, srb_t *);
|
||||
|
||||
extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *);
|
||||
|
||||
extern void qla2x00_cmd_timeout(srb_t *);
|
||||
|
||||
extern void qla2x00_mark_device_lost(scsi_qla_host_t *, fc_port_t *, int, int);
|
||||
extern void qla2x00_mark_all_devices_lost(scsi_qla_host_t *, int);
|
||||
|
||||
extern int qla2x00_down_timeout(struct semaphore *, unsigned long);
|
||||
|
||||
extern struct fw_blob *qla2x00_request_firmware(scsi_qla_host_t *);
|
||||
|
||||
extern int qla2x00_wait_for_hba_online(scsi_qla_host_t *);
|
||||
|
||||
extern void qla2xxx_wake_dpc(scsi_qla_host_t *);
|
||||
|
||||
/*
|
||||
* Global Function Prototypes in qla_iocb.c source file.
|
||||
*/
|
||||
extern void qla2x00_isp_cmd(scsi_qla_host_t *);
|
||||
|
||||
extern uint16_t qla2x00_calc_iocbs_32(uint16_t);
|
||||
extern uint16_t qla2x00_calc_iocbs_64(uint16_t);
|
||||
extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
|
||||
extern void qla2x00_build_scsi_iocbs_64(srb_t *, cmd_entry_t *, uint16_t);
|
||||
extern int qla2x00_start_scsi(srb_t *sp);
|
||||
extern int qla24xx_start_scsi(srb_t *sp);
|
||||
int qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
|
||||
int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t);
|
||||
|
||||
/*
|
||||
* Global Function Prototypes in qla_mbx.c source file.
|
||||
*/
|
||||
extern int
|
||||
qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t);
|
||||
|
||||
extern int
|
||||
qla2x00_execute_fw(scsi_qla_host_t *, uint32_t);
|
||||
|
||||
extern void
|
||||
qla2x00_get_fw_version(scsi_qla_host_t *, uint16_t *,
|
||||
uint16_t *, uint16_t *, uint16_t *, uint32_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_get_fw_options(scsi_qla_host_t *, uint16_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_set_fw_options(scsi_qla_host_t *, uint16_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_mbx_reg_test(scsi_qla_host_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_verify_checksum(scsi_qla_host_t *, uint32_t);
|
||||
|
||||
extern int
|
||||
qla2x00_issue_iocb(scsi_qla_host_t *, void *, dma_addr_t, size_t);
|
||||
|
||||
extern int
|
||||
qla2x00_abort_command(scsi_qla_host_t *, srb_t *);
|
||||
|
||||
#if USE_ABORT_TGT
|
||||
extern int
|
||||
qla2x00_abort_target(fc_port_t *);
|
||||
#endif
|
||||
|
||||
extern int
|
||||
qla2x00_get_adapter_id(scsi_qla_host_t *, uint16_t *, uint8_t *, uint8_t *,
|
||||
uint8_t *, uint16_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_init_firmware(scsi_qla_host_t *, uint16_t);
|
||||
|
||||
extern int
|
||||
qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t);
|
||||
|
||||
extern int
|
||||
qla2x00_get_firmware_state(scsi_qla_host_t *, uint16_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_get_port_name(scsi_qla_host_t *, uint16_t, uint8_t *, uint8_t);
|
||||
|
||||
extern int
|
||||
qla2x00_lip_reset(scsi_qla_host_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_send_sns(scsi_qla_host_t *, dma_addr_t, uint16_t, size_t);
|
||||
|
||||
extern int
|
||||
qla2x00_login_fabric(scsi_qla_host_t *, uint16_t, uint8_t, uint8_t, uint8_t,
|
||||
uint16_t *, uint8_t);
|
||||
extern int
|
||||
qla24xx_login_fabric(scsi_qla_host_t *, uint16_t, uint8_t, uint8_t, uint8_t,
|
||||
uint16_t *, uint8_t);
|
||||
|
||||
extern int
|
||||
qla2x00_login_local_device(scsi_qla_host_t *, fc_port_t *, uint16_t *,
|
||||
uint8_t);
|
||||
|
||||
extern int
|
||||
qla2x00_fabric_logout(scsi_qla_host_t *, uint16_t, uint8_t, uint8_t, uint8_t);
|
||||
|
||||
extern int
|
||||
qla24xx_fabric_logout(scsi_qla_host_t *, uint16_t, uint8_t, uint8_t, uint8_t);
|
||||
|
||||
extern int
|
||||
qla2x00_full_login_lip(scsi_qla_host_t *ha);
|
||||
|
||||
extern int
|
||||
qla2x00_get_id_list(scsi_qla_host_t *, void *, dma_addr_t, uint16_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_get_resource_cnts(scsi_qla_host_t *, uint16_t *, uint16_t *, uint16_t *,
|
||||
uint16_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map);
|
||||
|
||||
extern int
|
||||
qla2x00_get_link_status(scsi_qla_host_t *, uint16_t, link_stat_t *,
|
||||
uint16_t *);
|
||||
|
||||
extern int
|
||||
qla24xx_get_isp_stats(scsi_qla_host_t *, uint32_t *, uint32_t, uint16_t *);
|
||||
|
||||
extern int qla24xx_abort_command(scsi_qla_host_t *, srb_t *);
|
||||
extern int qla24xx_abort_target(fc_port_t *);
|
||||
|
||||
extern int qla2x00_system_error(scsi_qla_host_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_get_serdes_params(scsi_qla_host_t *, uint16_t *, uint16_t *,
|
||||
uint16_t *);
|
||||
|
||||
extern int
|
||||
qla2x00_set_serdes_params(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t);
|
||||
|
||||
extern int
|
||||
qla2x00_stop_firmware(scsi_qla_host_t *);
|
||||
|
||||
/*
|
||||
* Global Function Prototypes in qla_isr.c source file.
|
||||
*/
|
||||
extern irqreturn_t qla2100_intr_handler(int, void *, struct pt_regs *);
|
||||
extern irqreturn_t qla2300_intr_handler(int, void *, struct pt_regs *);
|
||||
extern irqreturn_t qla24xx_intr_handler(int, void *, struct pt_regs *);
|
||||
extern void qla2x00_process_response_queue(struct scsi_qla_host *);
|
||||
extern void qla24xx_process_response_queue(struct scsi_qla_host *);
|
||||
|
||||
/*
|
||||
* Global Function Prototypes in qla_sup.c source file.
|
||||
*/
|
||||
extern void qla2x00_lock_nvram_access(scsi_qla_host_t *);
|
||||
extern void qla2x00_unlock_nvram_access(scsi_qla_host_t *);
|
||||
extern void qla2x00_release_nvram_protection(scsi_qla_host_t *);
|
||||
extern uint16_t qla2x00_get_nvram_word(scsi_qla_host_t *, uint32_t);
|
||||
extern void qla2x00_write_nvram_word(scsi_qla_host_t *, uint32_t, uint16_t);
|
||||
extern uint32_t *qla24xx_read_flash_data(scsi_qla_host_t *, uint32_t *,
|
||||
uint32_t, uint32_t);
|
||||
extern uint8_t *qla2x00_read_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
|
||||
uint32_t);
|
||||
extern uint8_t *qla24xx_read_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
|
||||
uint32_t);
|
||||
extern int qla2x00_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
|
||||
uint32_t);
|
||||
extern int qla24xx_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
|
||||
uint32_t);
|
||||
|
||||
extern int qla2x00_beacon_on(struct scsi_qla_host *);
|
||||
extern int qla2x00_beacon_off(struct scsi_qla_host *);
|
||||
extern void qla2x00_beacon_blink(struct scsi_qla_host *);
|
||||
extern int qla24xx_beacon_on(struct scsi_qla_host *);
|
||||
extern int qla24xx_beacon_off(struct scsi_qla_host *);
|
||||
extern void qla24xx_beacon_blink(struct scsi_qla_host *);
|
||||
|
||||
extern uint8_t *qla2x00_read_optrom_data(struct scsi_qla_host *, uint8_t *,
|
||||
uint32_t, uint32_t);
|
||||
extern int qla2x00_write_optrom_data(struct scsi_qla_host *, uint8_t *,
|
||||
uint32_t, uint32_t);
|
||||
extern uint8_t *qla24xx_read_optrom_data(struct scsi_qla_host *, uint8_t *,
|
||||
uint32_t, uint32_t);
|
||||
extern int qla24xx_write_optrom_data(struct scsi_qla_host *, uint8_t *,
|
||||
uint32_t, uint32_t);
|
||||
|
||||
/*
|
||||
* Global Function Prototypes in qla_dbg.c source file.
|
||||
*/
|
||||
extern void qla2100_fw_dump(scsi_qla_host_t *, int);
|
||||
extern void qla2300_fw_dump(scsi_qla_host_t *, int);
|
||||
extern void qla24xx_fw_dump(scsi_qla_host_t *, int);
|
||||
extern void qla2100_ascii_fw_dump(scsi_qla_host_t *);
|
||||
extern void qla2300_ascii_fw_dump(scsi_qla_host_t *);
|
||||
extern void qla24xx_ascii_fw_dump(scsi_qla_host_t *);
|
||||
extern void qla2x00_dump_regs(scsi_qla_host_t *);
|
||||
extern void qla2x00_dump_buffer(uint8_t *, uint32_t);
|
||||
extern void qla2x00_print_scsi_cmd(struct scsi_cmnd *);
|
||||
extern void qla2x00_dump_pkt(void *);
|
||||
|
||||
/*
|
||||
* Global Function Prototypes in qla_gs.c source file.
|
||||
*/
|
||||
extern void *qla2x00_prep_ms_iocb(scsi_qla_host_t *, uint32_t, uint32_t);
|
||||
extern void *qla24xx_prep_ms_iocb(scsi_qla_host_t *, uint32_t, uint32_t);
|
||||
extern int qla2x00_ga_nxt(scsi_qla_host_t *, fc_port_t *);
|
||||
extern int qla2x00_gid_pt(scsi_qla_host_t *, sw_info_t *);
|
||||
extern int qla2x00_gpn_id(scsi_qla_host_t *, sw_info_t *);
|
||||
extern int qla2x00_gnn_id(scsi_qla_host_t *, sw_info_t *);
|
||||
extern int qla2x00_rft_id(scsi_qla_host_t *);
|
||||
extern int qla2x00_rff_id(scsi_qla_host_t *);
|
||||
extern int qla2x00_rnn_id(scsi_qla_host_t *);
|
||||
extern int qla2x00_rsnn_nn(scsi_qla_host_t *);
|
||||
extern void *qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *, uint32_t, uint32_t);
|
||||
extern void *qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *, uint32_t, uint32_t);
|
||||
extern int qla2x00_fdmi_register(scsi_qla_host_t *);
|
||||
|
||||
/*
|
||||
* Global Function Prototypes in qla_rscn.c source file.
|
||||
*/
|
||||
extern fc_port_t *qla2x00_alloc_rscn_fcport(scsi_qla_host_t *, gfp_t);
|
||||
extern int qla2x00_handle_port_rscn(scsi_qla_host_t *, uint32_t, fc_port_t *,
|
||||
int);
|
||||
extern void qla2x00_process_iodesc(scsi_qla_host_t *, struct mbx_entry *);
|
||||
extern void qla2x00_cancel_io_descriptors(scsi_qla_host_t *);
|
||||
|
||||
/*
|
||||
* Global Function Prototypes in qla_xioctl.c source file.
|
||||
*/
|
||||
#define qla2x00_enqueue_aen(ha, cmd, mode) do { } while (0)
|
||||
#define qla2x00_alloc_ioctl_mem(ha) (0)
|
||||
#define qla2x00_free_ioctl_mem(ha) do { } while (0)
|
||||
|
||||
/*
|
||||
* Global Function Prototypes in qla_attr.c source file.
|
||||
*/
|
||||
struct class_device_attribute;
|
||||
extern struct class_device_attribute *qla2x00_host_attrs[];
|
||||
struct fc_function_template;
|
||||
extern struct fc_function_template qla2xxx_transport_functions;
|
||||
extern void qla2x00_alloc_sysfs_attr(scsi_qla_host_t *);
|
||||
extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *);
|
||||
extern void qla2x00_init_host_attr(scsi_qla_host_t *);
|
||||
extern void qla2x00_alloc_sysfs_attr(scsi_qla_host_t *);
|
||||
extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *);
|
||||
#endif /* _QLA_GBL_H */
|
||||
+1649
File diff suppressed because it is too large
Load Diff
+4142
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* QLogic Fibre Channel HBA Driver
|
||||
* Copyright (c) 2003-2005 QLogic Corporation
|
||||
*
|
||||
* See LICENSE.qla2xxx for copyright and licensing details.
|
||||
*/
|
||||
|
||||
static __inline__ uint16_t qla2x00_debounce_register(volatile uint16_t __iomem *);
|
||||
/*
|
||||
* qla2x00_debounce_register
|
||||
* Debounce register.
|
||||
*
|
||||
* Input:
|
||||
* port = register address.
|
||||
*
|
||||
* Returns:
|
||||
* register value.
|
||||
*/
|
||||
static __inline__ uint16_t
|
||||
qla2x00_debounce_register(volatile uint16_t __iomem *addr)
|
||||
{
|
||||
volatile uint16_t first;
|
||||
volatile uint16_t second;
|
||||
|
||||
do {
|
||||
first = RD_REG_WORD(addr);
|
||||
barrier();
|
||||
cpu_relax();
|
||||
second = RD_REG_WORD(addr);
|
||||
} while (first != second);
|
||||
|
||||
return (first);
|
||||
}
|
||||
|
||||
static __inline__ int qla2x00_normalize_dma_addr(
|
||||
dma_addr_t *e_addr, uint32_t *e_len,
|
||||
dma_addr_t *ne_addr, uint32_t *ne_len);
|
||||
|
||||
/**
|
||||
* qla2x00_normalize_dma_addr() - Normalize an DMA address.
|
||||
* @e_addr: Raw DMA address
|
||||
* @e_len: Raw DMA length
|
||||
* @ne_addr: Normalized second DMA address
|
||||
* @ne_len: Normalized second DMA length
|
||||
*
|
||||
* If the address does not span a 4GB page boundary, the contents of @ne_addr
|
||||
* and @ne_len are undefined. @e_len is updated to reflect a normalization.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ffffabc0ffffeeee (e_addr) start of DMA address
|
||||
* 0000000020000000 (e_len) length of DMA transfer
|
||||
* ffffabc11fffeeed end of DMA transfer
|
||||
*
|
||||
* Is the 4GB boundary crossed?
|
||||
*
|
||||
* ffffabc0ffffeeee (e_addr)
|
||||
* ffffabc11fffeeed (e_addr + e_len - 1)
|
||||
* 00000001e0000003 ((e_addr ^ (e_addr + e_len - 1))
|
||||
* 0000000100000000 ((e_addr ^ (e_addr + e_len - 1)) & ~(0xffffffff)
|
||||
*
|
||||
* Compute start of second DMA segment:
|
||||
*
|
||||
* ffffabc0ffffeeee (e_addr)
|
||||
* ffffabc1ffffeeee (0x100000000 + e_addr)
|
||||
* ffffabc100000000 (0x100000000 + e_addr) & ~(0xffffffff)
|
||||
* ffffabc100000000 (ne_addr)
|
||||
*
|
||||
* Compute length of second DMA segment:
|
||||
*
|
||||
* 00000000ffffeeee (e_addr & 0xffffffff)
|
||||
* 0000000000001112 (0x100000000 - (e_addr & 0xffffffff))
|
||||
* 000000001fffeeee (e_len - (0x100000000 - (e_addr & 0xffffffff))
|
||||
* 000000001fffeeee (ne_len)
|
||||
*
|
||||
* Adjust length of first DMA segment
|
||||
*
|
||||
* 0000000020000000 (e_len)
|
||||
* 0000000000001112 (e_len - ne_len)
|
||||
* 0000000000001112 (e_len)
|
||||
*
|
||||
* Returns non-zero if the specified address was normalized, else zero.
|
||||
*/
|
||||
static __inline__ int
|
||||
qla2x00_normalize_dma_addr(
|
||||
dma_addr_t *e_addr, uint32_t *e_len,
|
||||
dma_addr_t *ne_addr, uint32_t *ne_len)
|
||||
{
|
||||
int normalized;
|
||||
|
||||
normalized = 0;
|
||||
if ((*e_addr ^ (*e_addr + *e_len - 1)) & ~(0xFFFFFFFFULL)) {
|
||||
/* Compute normalized crossed address and len */
|
||||
*ne_addr = (0x100000000ULL + *e_addr) & ~(0xFFFFFFFFULL);
|
||||
*ne_len = *e_len - (0x100000000ULL - (*e_addr & 0xFFFFFFFFULL));
|
||||
*e_len -= *ne_len;
|
||||
|
||||
normalized++;
|
||||
}
|
||||
return (normalized);
|
||||
}
|
||||
|
||||
static __inline__ void qla2x00_poll(scsi_qla_host_t *);
|
||||
static inline void
|
||||
qla2x00_poll(scsi_qla_host_t *ha)
|
||||
{
|
||||
ha->isp_ops.intr_handler(0, ha, NULL);
|
||||
}
|
||||
|
||||
static __inline__ void qla2x00_check_fabric_devices(scsi_qla_host_t *);
|
||||
/*
|
||||
* This routine will wait for fabric devices for
|
||||
* the reset delay.
|
||||
*/
|
||||
static __inline__ void qla2x00_check_fabric_devices(scsi_qla_host_t *ha)
|
||||
{
|
||||
uint16_t fw_state;
|
||||
|
||||
qla2x00_get_firmware_state(ha, &fw_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_issue_marker() - Issue a Marker IOCB if necessary.
|
||||
* @ha: HA context
|
||||
* @ha_locked: is function called with the hardware lock
|
||||
*
|
||||
* Returns non-zero if a failure occured, else zero.
|
||||
*/
|
||||
static inline int
|
||||
qla2x00_issue_marker(scsi_qla_host_t *ha, int ha_locked)
|
||||
{
|
||||
/* Send marker if required */
|
||||
if (ha->marker_needed != 0) {
|
||||
if (ha_locked) {
|
||||
if (__qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) !=
|
||||
QLA_SUCCESS)
|
||||
return (QLA_FUNCTION_FAILED);
|
||||
} else {
|
||||
if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) !=
|
||||
QLA_SUCCESS)
|
||||
return (QLA_FUNCTION_FAILED);
|
||||
}
|
||||
ha->marker_needed = 0;
|
||||
}
|
||||
return (QLA_SUCCESS);
|
||||
}
|
||||
|
||||
static inline uint8_t *host_to_fcp_swap(uint8_t *, uint32_t);
|
||||
static inline uint8_t *
|
||||
host_to_fcp_swap(uint8_t *fcp, uint32_t bsize)
|
||||
{
|
||||
uint32_t *ifcp = (uint32_t *) fcp;
|
||||
uint32_t *ofcp = (uint32_t *) fcp;
|
||||
uint32_t iter = bsize >> 2;
|
||||
|
||||
for (; iter ; iter--)
|
||||
*ofcp++ = swab32(*ifcp++);
|
||||
|
||||
return fcp;
|
||||
}
|
||||
|
||||
static inline int qla2x00_is_reserved_id(scsi_qla_host_t *, uint16_t);
|
||||
static inline int
|
||||
qla2x00_is_reserved_id(scsi_qla_host_t *ha, uint16_t loop_id)
|
||||
{
|
||||
if (IS_QLA24XX(ha) || IS_QLA54XX(ha))
|
||||
return (loop_id > NPH_LAST_HANDLE);
|
||||
|
||||
return ((loop_id > ha->last_loop_id && loop_id < SNS_FIRST_LOOP_ID) ||
|
||||
loop_id == MANAGEMENT_SERVER || loop_id == BROADCAST);
|
||||
};
|
||||
@@ -0,0 +1,875 @@
|
||||
/*
|
||||
* QLogic Fibre Channel HBA Driver
|
||||
* Copyright (c) 2003-2005 QLogic Corporation
|
||||
*
|
||||
* See LICENSE.qla2xxx for copyright and licensing details.
|
||||
*/
|
||||
#include "qla_def.h"
|
||||
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <scsi/scsi_tcq.h>
|
||||
|
||||
static inline uint16_t qla2x00_get_cmd_direction(struct scsi_cmnd *cmd);
|
||||
static inline cont_entry_t *qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *);
|
||||
static inline cont_a64_entry_t *qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *);
|
||||
request_t *qla2x00_req_pkt(scsi_qla_host_t *ha);
|
||||
|
||||
/**
|
||||
* qla2x00_get_cmd_direction() - Determine control_flag data direction.
|
||||
* @cmd: SCSI command
|
||||
*
|
||||
* Returns the proper CF_* direction based on CDB.
|
||||
*/
|
||||
static inline uint16_t
|
||||
qla2x00_get_cmd_direction(struct scsi_cmnd *cmd)
|
||||
{
|
||||
uint16_t cflags;
|
||||
|
||||
cflags = 0;
|
||||
|
||||
/* Set transfer direction */
|
||||
if (cmd->sc_data_direction == DMA_TO_DEVICE)
|
||||
cflags = CF_WRITE;
|
||||
else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
|
||||
cflags = CF_READ;
|
||||
return (cflags);
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_calc_iocbs_32() - Determine number of Command Type 2 and
|
||||
* Continuation Type 0 IOCBs to allocate.
|
||||
*
|
||||
* @dsds: number of data segment decriptors needed
|
||||
*
|
||||
* Returns the number of IOCB entries needed to store @dsds.
|
||||
*/
|
||||
uint16_t
|
||||
qla2x00_calc_iocbs_32(uint16_t dsds)
|
||||
{
|
||||
uint16_t iocbs;
|
||||
|
||||
iocbs = 1;
|
||||
if (dsds > 3) {
|
||||
iocbs += (dsds - 3) / 7;
|
||||
if ((dsds - 3) % 7)
|
||||
iocbs++;
|
||||
}
|
||||
return (iocbs);
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_calc_iocbs_64() - Determine number of Command Type 3 and
|
||||
* Continuation Type 1 IOCBs to allocate.
|
||||
*
|
||||
* @dsds: number of data segment decriptors needed
|
||||
*
|
||||
* Returns the number of IOCB entries needed to store @dsds.
|
||||
*/
|
||||
uint16_t
|
||||
qla2x00_calc_iocbs_64(uint16_t dsds)
|
||||
{
|
||||
uint16_t iocbs;
|
||||
|
||||
iocbs = 1;
|
||||
if (dsds > 2) {
|
||||
iocbs += (dsds - 2) / 5;
|
||||
if ((dsds - 2) % 5)
|
||||
iocbs++;
|
||||
}
|
||||
return (iocbs);
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_prep_cont_type0_iocb() - Initialize a Continuation Type 0 IOCB.
|
||||
* @ha: HA context
|
||||
*
|
||||
* Returns a pointer to the Continuation Type 0 IOCB packet.
|
||||
*/
|
||||
static inline cont_entry_t *
|
||||
qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *ha)
|
||||
{
|
||||
cont_entry_t *cont_pkt;
|
||||
|
||||
/* Adjust ring index. */
|
||||
ha->req_ring_index++;
|
||||
if (ha->req_ring_index == ha->request_q_length) {
|
||||
ha->req_ring_index = 0;
|
||||
ha->request_ring_ptr = ha->request_ring;
|
||||
} else {
|
||||
ha->request_ring_ptr++;
|
||||
}
|
||||
|
||||
cont_pkt = (cont_entry_t *)ha->request_ring_ptr;
|
||||
|
||||
/* Load packet defaults. */
|
||||
*((uint32_t *)(&cont_pkt->entry_type)) =
|
||||
__constant_cpu_to_le32(CONTINUE_TYPE);
|
||||
|
||||
return (cont_pkt);
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_prep_cont_type1_iocb() - Initialize a Continuation Type 1 IOCB.
|
||||
* @ha: HA context
|
||||
*
|
||||
* Returns a pointer to the continuation type 1 IOCB packet.
|
||||
*/
|
||||
static inline cont_a64_entry_t *
|
||||
qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *ha)
|
||||
{
|
||||
cont_a64_entry_t *cont_pkt;
|
||||
|
||||
/* Adjust ring index. */
|
||||
ha->req_ring_index++;
|
||||
if (ha->req_ring_index == ha->request_q_length) {
|
||||
ha->req_ring_index = 0;
|
||||
ha->request_ring_ptr = ha->request_ring;
|
||||
} else {
|
||||
ha->request_ring_ptr++;
|
||||
}
|
||||
|
||||
cont_pkt = (cont_a64_entry_t *)ha->request_ring_ptr;
|
||||
|
||||
/* Load packet defaults. */
|
||||
*((uint32_t *)(&cont_pkt->entry_type)) =
|
||||
__constant_cpu_to_le32(CONTINUE_A64_TYPE);
|
||||
|
||||
return (cont_pkt);
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_build_scsi_iocbs_32() - Build IOCB command utilizing 32bit
|
||||
* capable IOCB types.
|
||||
*
|
||||
* @sp: SRB command to process
|
||||
* @cmd_pkt: Command type 2 IOCB
|
||||
* @tot_dsds: Total number of segments to transfer
|
||||
*/
|
||||
void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
|
||||
uint16_t tot_dsds)
|
||||
{
|
||||
uint16_t avail_dsds;
|
||||
uint32_t *cur_dsd;
|
||||
scsi_qla_host_t *ha;
|
||||
struct scsi_cmnd *cmd;
|
||||
|
||||
cmd = sp->cmd;
|
||||
|
||||
/* Update entry type to indicate Command Type 2 IOCB */
|
||||
*((uint32_t *)(&cmd_pkt->entry_type)) =
|
||||
__constant_cpu_to_le32(COMMAND_TYPE);
|
||||
|
||||
/* No data transfer */
|
||||
if (cmd->request_bufflen == 0 || cmd->sc_data_direction == DMA_NONE) {
|
||||
cmd_pkt->byte_count = __constant_cpu_to_le32(0);
|
||||
return;
|
||||
}
|
||||
|
||||
ha = sp->ha;
|
||||
|
||||
cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(cmd));
|
||||
|
||||
/* Three DSDs are available in the Command Type 2 IOCB */
|
||||
avail_dsds = 3;
|
||||
cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address;
|
||||
|
||||
/* Load data segments */
|
||||
if (cmd->use_sg != 0) {
|
||||
struct scatterlist *cur_seg;
|
||||
struct scatterlist *end_seg;
|
||||
|
||||
cur_seg = (struct scatterlist *)cmd->request_buffer;
|
||||
end_seg = cur_seg + tot_dsds;
|
||||
while (cur_seg < end_seg) {
|
||||
cont_entry_t *cont_pkt;
|
||||
|
||||
/* Allocate additional continuation packets? */
|
||||
if (avail_dsds == 0) {
|
||||
/*
|
||||
* Seven DSDs are available in the Continuation
|
||||
* Type 0 IOCB.
|
||||
*/
|
||||
cont_pkt = qla2x00_prep_cont_type0_iocb(ha);
|
||||
cur_dsd = (uint32_t *)&cont_pkt->dseg_0_address;
|
||||
avail_dsds = 7;
|
||||
}
|
||||
|
||||
*cur_dsd++ = cpu_to_le32(sg_dma_address(cur_seg));
|
||||
*cur_dsd++ = cpu_to_le32(sg_dma_len(cur_seg));
|
||||
avail_dsds--;
|
||||
|
||||
cur_seg++;
|
||||
}
|
||||
} else {
|
||||
*cur_dsd++ = cpu_to_le32(sp->dma_handle);
|
||||
*cur_dsd++ = cpu_to_le32(cmd->request_bufflen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_build_scsi_iocbs_64() - Build IOCB command utilizing 64bit
|
||||
* capable IOCB types.
|
||||
*
|
||||
* @sp: SRB command to process
|
||||
* @cmd_pkt: Command type 3 IOCB
|
||||
* @tot_dsds: Total number of segments to transfer
|
||||
*/
|
||||
void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
|
||||
uint16_t tot_dsds)
|
||||
{
|
||||
uint16_t avail_dsds;
|
||||
uint32_t *cur_dsd;
|
||||
scsi_qla_host_t *ha;
|
||||
struct scsi_cmnd *cmd;
|
||||
|
||||
cmd = sp->cmd;
|
||||
|
||||
/* Update entry type to indicate Command Type 3 IOCB */
|
||||
*((uint32_t *)(&cmd_pkt->entry_type)) =
|
||||
__constant_cpu_to_le32(COMMAND_A64_TYPE);
|
||||
|
||||
/* No data transfer */
|
||||
if (cmd->request_bufflen == 0 || cmd->sc_data_direction == DMA_NONE) {
|
||||
cmd_pkt->byte_count = __constant_cpu_to_le32(0);
|
||||
return;
|
||||
}
|
||||
|
||||
ha = sp->ha;
|
||||
|
||||
cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(cmd));
|
||||
|
||||
/* Two DSDs are available in the Command Type 3 IOCB */
|
||||
avail_dsds = 2;
|
||||
cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address;
|
||||
|
||||
/* Load data segments */
|
||||
if (cmd->use_sg != 0) {
|
||||
struct scatterlist *cur_seg;
|
||||
struct scatterlist *end_seg;
|
||||
|
||||
cur_seg = (struct scatterlist *)cmd->request_buffer;
|
||||
end_seg = cur_seg + tot_dsds;
|
||||
while (cur_seg < end_seg) {
|
||||
dma_addr_t sle_dma;
|
||||
cont_a64_entry_t *cont_pkt;
|
||||
|
||||
/* Allocate additional continuation packets? */
|
||||
if (avail_dsds == 0) {
|
||||
/*
|
||||
* Five DSDs are available in the Continuation
|
||||
* Type 1 IOCB.
|
||||
*/
|
||||
cont_pkt = qla2x00_prep_cont_type1_iocb(ha);
|
||||
cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
|
||||
avail_dsds = 5;
|
||||
}
|
||||
|
||||
sle_dma = sg_dma_address(cur_seg);
|
||||
*cur_dsd++ = cpu_to_le32(LSD(sle_dma));
|
||||
*cur_dsd++ = cpu_to_le32(MSD(sle_dma));
|
||||
*cur_dsd++ = cpu_to_le32(sg_dma_len(cur_seg));
|
||||
avail_dsds--;
|
||||
|
||||
cur_seg++;
|
||||
}
|
||||
} else {
|
||||
*cur_dsd++ = cpu_to_le32(LSD(sp->dma_handle));
|
||||
*cur_dsd++ = cpu_to_le32(MSD(sp->dma_handle));
|
||||
*cur_dsd++ = cpu_to_le32(cmd->request_bufflen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_start_scsi() - Send a SCSI command to the ISP
|
||||
* @sp: command to send to the ISP
|
||||
*
|
||||
* Returns non-zero if a failure occured, else zero.
|
||||
*/
|
||||
int
|
||||
qla2x00_start_scsi(srb_t *sp)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
scsi_qla_host_t *ha;
|
||||
struct scsi_cmnd *cmd;
|
||||
uint32_t *clr_ptr;
|
||||
uint32_t index;
|
||||
uint32_t handle;
|
||||
cmd_entry_t *cmd_pkt;
|
||||
struct scatterlist *sg;
|
||||
uint16_t cnt;
|
||||
uint16_t req_cnt;
|
||||
uint16_t tot_dsds;
|
||||
struct device_reg_2xxx __iomem *reg;
|
||||
|
||||
/* Setup device pointers. */
|
||||
ret = 0;
|
||||
ha = sp->ha;
|
||||
reg = &ha->iobase->isp;
|
||||
cmd = sp->cmd;
|
||||
/* So we know we haven't pci_map'ed anything yet */
|
||||
tot_dsds = 0;
|
||||
|
||||
/* Send marker if required */
|
||||
if (ha->marker_needed != 0) {
|
||||
if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
|
||||
return (QLA_FUNCTION_FAILED);
|
||||
}
|
||||
ha->marker_needed = 0;
|
||||
}
|
||||
|
||||
/* Acquire ring specific lock */
|
||||
spin_lock_irqsave(&ha->hardware_lock, flags);
|
||||
|
||||
/* Check for room in outstanding command list. */
|
||||
handle = ha->current_outstanding_cmd;
|
||||
for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
|
||||
handle++;
|
||||
if (handle == MAX_OUTSTANDING_COMMANDS)
|
||||
handle = 1;
|
||||
if (ha->outstanding_cmds[handle] == 0)
|
||||
break;
|
||||
}
|
||||
if (index == MAX_OUTSTANDING_COMMANDS)
|
||||
goto queuing_error;
|
||||
|
||||
/* Map the sg table so we have an accurate count of sg entries needed */
|
||||
if (cmd->use_sg) {
|
||||
sg = (struct scatterlist *) cmd->request_buffer;
|
||||
tot_dsds = pci_map_sg(ha->pdev, sg, cmd->use_sg,
|
||||
cmd->sc_data_direction);
|
||||
if (tot_dsds == 0)
|
||||
goto queuing_error;
|
||||
} else if (cmd->request_bufflen) {
|
||||
dma_addr_t req_dma;
|
||||
|
||||
req_dma = pci_map_single(ha->pdev, cmd->request_buffer,
|
||||
cmd->request_bufflen, cmd->sc_data_direction);
|
||||
if (dma_mapping_error(req_dma))
|
||||
goto queuing_error;
|
||||
|
||||
sp->dma_handle = req_dma;
|
||||
tot_dsds = 1;
|
||||
}
|
||||
|
||||
/* Calculate the number of request entries needed. */
|
||||
req_cnt = ha->isp_ops.calc_req_entries(tot_dsds);
|
||||
if (ha->req_q_cnt < (req_cnt + 2)) {
|
||||
cnt = RD_REG_WORD_RELAXED(ISP_REQ_Q_OUT(ha, reg));
|
||||
if (ha->req_ring_index < cnt)
|
||||
ha->req_q_cnt = cnt - ha->req_ring_index;
|
||||
else
|
||||
ha->req_q_cnt = ha->request_q_length -
|
||||
(ha->req_ring_index - cnt);
|
||||
}
|
||||
if (ha->req_q_cnt < (req_cnt + 2))
|
||||
goto queuing_error;
|
||||
|
||||
/* Build command packet */
|
||||
ha->current_outstanding_cmd = handle;
|
||||
ha->outstanding_cmds[handle] = sp;
|
||||
sp->ha = ha;
|
||||
sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
|
||||
ha->req_q_cnt -= req_cnt;
|
||||
|
||||
cmd_pkt = (cmd_entry_t *)ha->request_ring_ptr;
|
||||
cmd_pkt->handle = handle;
|
||||
/* Zero out remaining portion of packet. */
|
||||
clr_ptr = (uint32_t *)cmd_pkt + 2;
|
||||
memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
|
||||
cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
|
||||
|
||||
/* Set target ID and LUN number*/
|
||||
SET_TARGET_ID(ha, cmd_pkt->target, sp->fcport->loop_id);
|
||||
cmd_pkt->lun = cpu_to_le16(sp->cmd->device->lun);
|
||||
|
||||
/* Update tagged queuing modifier */
|
||||
cmd_pkt->control_flags = __constant_cpu_to_le16(CF_SIMPLE_TAG);
|
||||
|
||||
/* Load SCSI command packet. */
|
||||
memcpy(cmd_pkt->scsi_cdb, cmd->cmnd, cmd->cmd_len);
|
||||
cmd_pkt->byte_count = cpu_to_le32((uint32_t)cmd->request_bufflen);
|
||||
|
||||
/* Build IOCB segments */
|
||||
ha->isp_ops.build_iocbs(sp, cmd_pkt, tot_dsds);
|
||||
|
||||
/* Set total data segment count. */
|
||||
cmd_pkt->entry_count = (uint8_t)req_cnt;
|
||||
wmb();
|
||||
|
||||
DEBUG5(printk("%s(): IOCB data:\n", __func__));
|
||||
DEBUG5(qla2x00_dump_buffer(
|
||||
(uint8_t *)ha->request_ring_ptr, REQUEST_ENTRY_SIZE));
|
||||
|
||||
/* Adjust ring index. */
|
||||
ha->req_ring_index++;
|
||||
if (ha->req_ring_index == ha->request_q_length) {
|
||||
ha->req_ring_index = 0;
|
||||
ha->request_ring_ptr = ha->request_ring;
|
||||
} else
|
||||
ha->request_ring_ptr++;
|
||||
|
||||
sp->flags |= SRB_DMA_VALID;
|
||||
sp->state = SRB_ACTIVE_STATE;
|
||||
|
||||
/* Set chip new ring index. */
|
||||
WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), ha->req_ring_index);
|
||||
RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, reg)); /* PCI Posting. */
|
||||
|
||||
/* Manage unprocessed RIO/ZIO commands in response queue. */
|
||||
if (ha->flags.process_response_queue &&
|
||||
ha->response_ring_ptr->signature != RESPONSE_PROCESSED)
|
||||
qla2x00_process_response_queue(ha);
|
||||
|
||||
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
||||
return (QLA_SUCCESS);
|
||||
|
||||
queuing_error:
|
||||
if (cmd->use_sg && tot_dsds) {
|
||||
sg = (struct scatterlist *) cmd->request_buffer;
|
||||
pci_unmap_sg(ha->pdev, sg, cmd->use_sg,
|
||||
cmd->sc_data_direction);
|
||||
} else if (tot_dsds) {
|
||||
pci_unmap_single(ha->pdev, sp->dma_handle,
|
||||
cmd->request_bufflen, cmd->sc_data_direction);
|
||||
}
|
||||
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
||||
|
||||
return (QLA_FUNCTION_FAILED);
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_marker() - Send a marker IOCB to the firmware.
|
||||
* @ha: HA context
|
||||
* @loop_id: loop ID
|
||||
* @lun: LUN
|
||||
* @type: marker modifier
|
||||
*
|
||||
* Can be called from both normal and interrupt context.
|
||||
*
|
||||
* Returns non-zero if a failure occured, else zero.
|
||||
*/
|
||||
int
|
||||
__qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
|
||||
uint8_t type)
|
||||
{
|
||||
mrk_entry_t *mrk;
|
||||
struct mrk_entry_24xx *mrk24;
|
||||
|
||||
mrk24 = NULL;
|
||||
mrk = (mrk_entry_t *)qla2x00_req_pkt(ha);
|
||||
if (mrk == NULL) {
|
||||
DEBUG2_3(printk("%s(%ld): failed to allocate Marker IOCB.\n",
|
||||
__func__, ha->host_no));
|
||||
|
||||
return (QLA_FUNCTION_FAILED);
|
||||
}
|
||||
|
||||
mrk->entry_type = MARKER_TYPE;
|
||||
mrk->modifier = type;
|
||||
if (type != MK_SYNC_ALL) {
|
||||
if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) {
|
||||
mrk24 = (struct mrk_entry_24xx *) mrk;
|
||||
mrk24->nport_handle = cpu_to_le16(loop_id);
|
||||
mrk24->lun[1] = LSB(lun);
|
||||
mrk24->lun[2] = MSB(lun);
|
||||
} else {
|
||||
SET_TARGET_ID(ha, mrk->target, loop_id);
|
||||
mrk->lun = cpu_to_le16(lun);
|
||||
}
|
||||
}
|
||||
wmb();
|
||||
|
||||
qla2x00_isp_cmd(ha);
|
||||
|
||||
return (QLA_SUCCESS);
|
||||
}
|
||||
|
||||
int
|
||||
qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun,
|
||||
uint8_t type)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags = 0;
|
||||
|
||||
spin_lock_irqsave(&ha->hardware_lock, flags);
|
||||
ret = __qla2x00_marker(ha, loop_id, lun, type);
|
||||
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_req_pkt() - Retrieve a request packet from the request ring.
|
||||
* @ha: HA context
|
||||
*
|
||||
* Note: The caller must hold the hardware lock before calling this routine.
|
||||
*
|
||||
* Returns NULL if function failed, else, a pointer to the request packet.
|
||||
*/
|
||||
request_t *
|
||||
qla2x00_req_pkt(scsi_qla_host_t *ha)
|
||||
{
|
||||
device_reg_t __iomem *reg = ha->iobase;
|
||||
request_t *pkt = NULL;
|
||||
uint16_t cnt;
|
||||
uint32_t *dword_ptr;
|
||||
uint32_t timer;
|
||||
uint16_t req_cnt = 1;
|
||||
|
||||
/* Wait 1 second for slot. */
|
||||
for (timer = HZ; timer; timer--) {
|
||||
if ((req_cnt + 2) >= ha->req_q_cnt) {
|
||||
/* Calculate number of free request entries. */
|
||||
if (IS_QLA24XX(ha) || IS_QLA54XX(ha))
|
||||
cnt = (uint16_t)RD_REG_DWORD(
|
||||
®->isp24.req_q_out);
|
||||
else
|
||||
cnt = qla2x00_debounce_register(
|
||||
ISP_REQ_Q_OUT(ha, ®->isp));
|
||||
if (ha->req_ring_index < cnt)
|
||||
ha->req_q_cnt = cnt - ha->req_ring_index;
|
||||
else
|
||||
ha->req_q_cnt = ha->request_q_length -
|
||||
(ha->req_ring_index - cnt);
|
||||
}
|
||||
/* If room for request in request ring. */
|
||||
if ((req_cnt + 2) < ha->req_q_cnt) {
|
||||
ha->req_q_cnt--;
|
||||
pkt = ha->request_ring_ptr;
|
||||
|
||||
/* Zero out packet. */
|
||||
dword_ptr = (uint32_t *)pkt;
|
||||
for (cnt = 0; cnt < REQUEST_ENTRY_SIZE / 4; cnt++)
|
||||
*dword_ptr++ = 0;
|
||||
|
||||
/* Set system defined field. */
|
||||
pkt->sys_define = (uint8_t)ha->req_ring_index;
|
||||
|
||||
/* Set entry count. */
|
||||
pkt->entry_count = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Release ring specific lock */
|
||||
spin_unlock(&ha->hardware_lock);
|
||||
|
||||
udelay(2); /* 2 us */
|
||||
|
||||
/* Check for pending interrupts. */
|
||||
/* During init we issue marker directly */
|
||||
if (!ha->marker_needed)
|
||||
qla2x00_poll(ha);
|
||||
|
||||
spin_lock_irq(&ha->hardware_lock);
|
||||
}
|
||||
if (!pkt) {
|
||||
DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__));
|
||||
}
|
||||
|
||||
return (pkt);
|
||||
}
|
||||
|
||||
/**
|
||||
* qla2x00_isp_cmd() - Modify the request ring pointer.
|
||||
* @ha: HA context
|
||||
*
|
||||
* Note: The caller must hold the hardware lock before calling this routine.
|
||||
*/
|
||||
void
|
||||
qla2x00_isp_cmd(scsi_qla_host_t *ha)
|
||||
{
|
||||
device_reg_t __iomem *reg = ha->iobase;
|
||||
|
||||
DEBUG5(printk("%s(): IOCB data:\n", __func__));
|
||||
DEBUG5(qla2x00_dump_buffer(
|
||||
(uint8_t *)ha->request_ring_ptr, REQUEST_ENTRY_SIZE));
|
||||
|
||||
/* Adjust ring index. */
|
||||
ha->req_ring_index++;
|
||||
if (ha->req_ring_index == ha->request_q_length) {
|
||||
ha->req_ring_index = 0;
|
||||
ha->request_ring_ptr = ha->request_ring;
|
||||
} else
|
||||
ha->request_ring_ptr++;
|
||||
|
||||
/* Set chip new ring index. */
|
||||
if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) {
|
||||
WRT_REG_DWORD(®->isp24.req_q_in, ha->req_ring_index);
|
||||
RD_REG_DWORD_RELAXED(®->isp24.req_q_in);
|
||||
} else {
|
||||
WRT_REG_WORD(ISP_REQ_Q_IN(ha, ®->isp), ha->req_ring_index);
|
||||
RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, ®->isp));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* qla24xx_calc_iocbs() - Determine number of Command Type 3 and
|
||||
* Continuation Type 1 IOCBs to allocate.
|
||||
*
|
||||
* @dsds: number of data segment decriptors needed
|
||||
*
|
||||
* Returns the number of IOCB entries needed to store @dsds.
|
||||
*/
|
||||
static inline uint16_t
|
||||
qla24xx_calc_iocbs(uint16_t dsds)
|
||||
{
|
||||
uint16_t iocbs;
|
||||
|
||||
iocbs = 1;
|
||||
if (dsds > 1) {
|
||||
iocbs += (dsds - 1) / 5;
|
||||
if ((dsds - 1) % 5)
|
||||
iocbs++;
|
||||
}
|
||||
return iocbs;
|
||||
}
|
||||
|
||||
/**
|
||||
* qla24xx_build_scsi_iocbs() - Build IOCB command utilizing Command Type 7
|
||||
* IOCB types.
|
||||
*
|
||||
* @sp: SRB command to process
|
||||
* @cmd_pkt: Command type 3 IOCB
|
||||
* @tot_dsds: Total number of segments to transfer
|
||||
*/
|
||||
static inline void
|
||||
qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
|
||||
uint16_t tot_dsds)
|
||||
{
|
||||
uint16_t avail_dsds;
|
||||
uint32_t *cur_dsd;
|
||||
scsi_qla_host_t *ha;
|
||||
struct scsi_cmnd *cmd;
|
||||
|
||||
cmd = sp->cmd;
|
||||
|
||||
/* Update entry type to indicate Command Type 3 IOCB */
|
||||
*((uint32_t *)(&cmd_pkt->entry_type)) =
|
||||
__constant_cpu_to_le32(COMMAND_TYPE_7);
|
||||
|
||||
/* No data transfer */
|
||||
if (cmd->request_bufflen == 0 || cmd->sc_data_direction == DMA_NONE) {
|
||||
cmd_pkt->byte_count = __constant_cpu_to_le32(0);
|
||||
return;
|
||||
}
|
||||
|
||||
ha = sp->ha;
|
||||
|
||||
/* Set transfer direction */
|
||||
if (cmd->sc_data_direction == DMA_TO_DEVICE)
|
||||
cmd_pkt->task_mgmt_flags =
|
||||
__constant_cpu_to_le16(TMF_WRITE_DATA);
|
||||
else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
|
||||
cmd_pkt->task_mgmt_flags =
|
||||
__constant_cpu_to_le16(TMF_READ_DATA);
|
||||
|
||||
/* One DSD is available in the Command Type 3 IOCB */
|
||||
avail_dsds = 1;
|
||||
cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address;
|
||||
|
||||
/* Load data segments */
|
||||
if (cmd->use_sg != 0) {
|
||||
struct scatterlist *cur_seg;
|
||||
struct scatterlist *end_seg;
|
||||
|
||||
cur_seg = (struct scatterlist *)cmd->request_buffer;
|
||||
end_seg = cur_seg + tot_dsds;
|
||||
while (cur_seg < end_seg) {
|
||||
dma_addr_t sle_dma;
|
||||
cont_a64_entry_t *cont_pkt;
|
||||
|
||||
/* Allocate additional continuation packets? */
|
||||
if (avail_dsds == 0) {
|
||||
/*
|
||||
* Five DSDs are available in the Continuation
|
||||
* Type 1 IOCB.
|
||||
*/
|
||||
cont_pkt = qla2x00_prep_cont_type1_iocb(ha);
|
||||
cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
|
||||
avail_dsds = 5;
|
||||
}
|
||||
|
||||
sle_dma = sg_dma_address(cur_seg);
|
||||
*cur_dsd++ = cpu_to_le32(LSD(sle_dma));
|
||||
*cur_dsd++ = cpu_to_le32(MSD(sle_dma));
|
||||
*cur_dsd++ = cpu_to_le32(sg_dma_len(cur_seg));
|
||||
avail_dsds--;
|
||||
|
||||
cur_seg++;
|
||||
}
|
||||
} else {
|
||||
*cur_dsd++ = cpu_to_le32(LSD(sp->dma_handle));
|
||||
*cur_dsd++ = cpu_to_le32(MSD(sp->dma_handle));
|
||||
*cur_dsd++ = cpu_to_le32(cmd->request_bufflen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* qla24xx_start_scsi() - Send a SCSI command to the ISP
|
||||
* @sp: command to send to the ISP
|
||||
*
|
||||
* Returns non-zero if a failure occured, else zero.
|
||||
*/
|
||||
int
|
||||
qla24xx_start_scsi(srb_t *sp)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
scsi_qla_host_t *ha;
|
||||
struct scsi_cmnd *cmd;
|
||||
uint32_t *clr_ptr;
|
||||
uint32_t index;
|
||||
uint32_t handle;
|
||||
struct cmd_type_7 *cmd_pkt;
|
||||
struct scatterlist *sg;
|
||||
uint16_t cnt;
|
||||
uint16_t req_cnt;
|
||||
uint16_t tot_dsds;
|
||||
struct device_reg_24xx __iomem *reg;
|
||||
|
||||
/* Setup device pointers. */
|
||||
ret = 0;
|
||||
ha = sp->ha;
|
||||
reg = &ha->iobase->isp24;
|
||||
cmd = sp->cmd;
|
||||
/* So we know we haven't pci_map'ed anything yet */
|
||||
tot_dsds = 0;
|
||||
|
||||
/* Send marker if required */
|
||||
if (ha->marker_needed != 0) {
|
||||
if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
|
||||
return QLA_FUNCTION_FAILED;
|
||||
}
|
||||
ha->marker_needed = 0;
|
||||
}
|
||||
|
||||
/* Acquire ring specific lock */
|
||||
spin_lock_irqsave(&ha->hardware_lock, flags);
|
||||
|
||||
/* Check for room in outstanding command list. */
|
||||
handle = ha->current_outstanding_cmd;
|
||||
for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
|
||||
handle++;
|
||||
if (handle == MAX_OUTSTANDING_COMMANDS)
|
||||
handle = 1;
|
||||
if (ha->outstanding_cmds[handle] == 0)
|
||||
break;
|
||||
}
|
||||
if (index == MAX_OUTSTANDING_COMMANDS)
|
||||
goto queuing_error;
|
||||
|
||||
/* Map the sg table so we have an accurate count of sg entries needed */
|
||||
if (cmd->use_sg) {
|
||||
sg = (struct scatterlist *) cmd->request_buffer;
|
||||
tot_dsds = pci_map_sg(ha->pdev, sg, cmd->use_sg,
|
||||
cmd->sc_data_direction);
|
||||
if (tot_dsds == 0)
|
||||
goto queuing_error;
|
||||
} else if (cmd->request_bufflen) {
|
||||
dma_addr_t req_dma;
|
||||
|
||||
req_dma = pci_map_single(ha->pdev, cmd->request_buffer,
|
||||
cmd->request_bufflen, cmd->sc_data_direction);
|
||||
if (dma_mapping_error(req_dma))
|
||||
goto queuing_error;
|
||||
|
||||
sp->dma_handle = req_dma;
|
||||
tot_dsds = 1;
|
||||
}
|
||||
|
||||
req_cnt = qla24xx_calc_iocbs(tot_dsds);
|
||||
if (ha->req_q_cnt < (req_cnt + 2)) {
|
||||
cnt = (uint16_t)RD_REG_DWORD_RELAXED(®->req_q_out);
|
||||
if (ha->req_ring_index < cnt)
|
||||
ha->req_q_cnt = cnt - ha->req_ring_index;
|
||||
else
|
||||
ha->req_q_cnt = ha->request_q_length -
|
||||
(ha->req_ring_index - cnt);
|
||||
}
|
||||
if (ha->req_q_cnt < (req_cnt + 2))
|
||||
goto queuing_error;
|
||||
|
||||
/* Build command packet. */
|
||||
ha->current_outstanding_cmd = handle;
|
||||
ha->outstanding_cmds[handle] = sp;
|
||||
sp->ha = ha;
|
||||
sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
|
||||
ha->req_q_cnt -= req_cnt;
|
||||
|
||||
cmd_pkt = (struct cmd_type_7 *)ha->request_ring_ptr;
|
||||
cmd_pkt->handle = handle;
|
||||
|
||||
/* Zero out remaining portion of packet. */
|
||||
/* tagged queuing modifier -- default is TSK_SIMPLE (0). */
|
||||
clr_ptr = (uint32_t *)cmd_pkt + 2;
|
||||
memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
|
||||
cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
|
||||
|
||||
/* Set NPORT-ID and LUN number*/
|
||||
cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
|
||||
cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
|
||||
cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
|
||||
cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
|
||||
|
||||
int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun);
|
||||
host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
|
||||
|
||||
/* Load SCSI command packet. */
|
||||
memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len);
|
||||
host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb));
|
||||
|
||||
cmd_pkt->byte_count = cpu_to_le32((uint32_t)cmd->request_bufflen);
|
||||
|
||||
/* Build IOCB segments */
|
||||
qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds);
|
||||
|
||||
/* Set total data segment count. */
|
||||
cmd_pkt->entry_count = (uint8_t)req_cnt;
|
||||
wmb();
|
||||
|
||||
DEBUG5(printk("%s(): IOCB data:\n", __func__));
|
||||
DEBUG5(qla2x00_dump_buffer(
|
||||
(uint8_t *)ha->request_ring_ptr, REQUEST_ENTRY_SIZE));
|
||||
|
||||
/* Adjust ring index. */
|
||||
ha->req_ring_index++;
|
||||
if (ha->req_ring_index == ha->request_q_length) {
|
||||
ha->req_ring_index = 0;
|
||||
ha->request_ring_ptr = ha->request_ring;
|
||||
} else
|
||||
ha->request_ring_ptr++;
|
||||
|
||||
sp->flags |= SRB_DMA_VALID;
|
||||
sp->state = SRB_ACTIVE_STATE;
|
||||
|
||||
/* Set chip new ring index. */
|
||||
WRT_REG_DWORD(®->req_q_in, ha->req_ring_index);
|
||||
RD_REG_DWORD_RELAXED(®->req_q_in); /* PCI Posting. */
|
||||
|
||||
/* Manage unprocessed RIO/ZIO commands in response queue. */
|
||||
if (ha->flags.process_response_queue &&
|
||||
ha->response_ring_ptr->signature != RESPONSE_PROCESSED)
|
||||
qla24xx_process_response_queue(ha);
|
||||
|
||||
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
||||
return QLA_SUCCESS;
|
||||
|
||||
queuing_error:
|
||||
if (cmd->use_sg && tot_dsds) {
|
||||
sg = (struct scatterlist *) cmd->request_buffer;
|
||||
pci_unmap_sg(ha->pdev, sg, cmd->use_sg,
|
||||
cmd->sc_data_direction);
|
||||
} else if (tot_dsds) {
|
||||
pci_unmap_single(ha->pdev, sp->dma_handle,
|
||||
cmd->request_bufflen, cmd->sc_data_direction);
|
||||
}
|
||||
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
||||
|
||||
return QLA_FUNCTION_FAILED;
|
||||
}
|
||||
+1696
File diff suppressed because it is too large
Load Diff
+2466
File diff suppressed because it is too large
Load Diff
+2809
File diff suppressed because it is too large
Load Diff
+1426
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* QLogic Fibre Channel HBA Driver
|
||||
* Copyright (c) 2003-2005 QLogic Corporation
|
||||
*
|
||||
* See LICENSE.qla2xxx for copyright and licensing details.
|
||||
*/
|
||||
/*
|
||||
* Compile time Options:
|
||||
* 0 - Disable and 1 - Enable
|
||||
*/
|
||||
#define DEBUG_QLA2100 0 /* For Debug of qla2x00 */
|
||||
|
||||
#define USE_ABORT_TGT 1 /* Use Abort Target mbx cmd */
|
||||
|
||||
#define MAX_RETRIES_OF_ISP_ABORT 5
|
||||
|
||||
/* Max time to wait for the loop to be in LOOP_READY state */
|
||||
#define MAX_LOOP_TIMEOUT (60 * 5)
|
||||
#define EH_ACTIVE 1 /* Error handler active */
|
||||
|
||||
/*
|
||||
* Some vendor subsystems do not recover properly after a device reset. Define
|
||||
* the following to force a logout after a successful device reset.
|
||||
*/
|
||||
#undef LOGOUT_AFTER_DEVICE_RESET
|
||||
|
||||
#include "qla_version.h"
|
||||
+1655
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* QLogic Fibre Channel HBA Driver
|
||||
* Copyright (c) 2003-2005 QLogic Corporation
|
||||
*
|
||||
* See LICENSE.qla2xxx for copyright and licensing details.
|
||||
*/
|
||||
/*
|
||||
* Driver version
|
||||
*/
|
||||
#define QLA2XXX_VERSION "8.01.04-k"
|
||||
|
||||
#define QLA_DRIVER_MAJOR_VER 8
|
||||
#define QLA_DRIVER_MINOR_VER 1
|
||||
#define QLA_DRIVER_PATCH_VER 4
|
||||
#define QLA_DRIVER_BETA_VER 0
|
||||
+340
@@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
Summary of changes between versions 0.9.4 and 0.9.5
|
||||
---------------------------------------------------
|
||||
|
||||
- Fixed many found task management related problems, especially in the
|
||||
RESETs area. DEBUG_TM compilation option added (see README).
|
||||
|
||||
- Timer-based retries for targets after SCST_TGT_RES_QUEUE_FULL status
|
||||
implemented.
|
||||
|
||||
- Fixed broken CDROM FILEIO. Before that it always reported
|
||||
"No medium found"
|
||||
|
||||
- Fixed READ(6)/WRITE(6) CDB decoding for block devices.
|
||||
This bug prevented FreeBSD initiator from working.
|
||||
|
||||
- Implemented sgv_pool. It is mempool-like interface, which caches
|
||||
built SG-vectors in order not to rebuild them again for every
|
||||
subsequent command, so saves performance price of building
|
||||
SG-vectors, including pages allocation, and of additional context
|
||||
switches.
|
||||
|
||||
- For ABORTED commands xmit_response() now called. A target driver
|
||||
should recognized such commands using new function
|
||||
scst_cmd_aborted(). Also there are some other cleanups, improvements
|
||||
and interfaces changes that affect target drivers and dev handlers.
|
||||
You can find their full list by diff'ing scsi_tgt.h between versions
|
||||
0.9.4 and 0.9.5.
|
||||
|
||||
- Function alloc_data_buf() added to struct scst_tgt_template as well
|
||||
as data_buf_tgt_alloc added to struct scst_cmd to allow a target
|
||||
driver implement own memory management handler (by Arne Redlich
|
||||
<arne.redlich@xiranet.com>).
|
||||
|
||||
- Exported symbols are now not GPL'ed
|
||||
|
||||
- Various cleanups and bug fixes.
|
||||
|
||||
Summary of changes between versions 0.9.3 and 0.9.4
|
||||
---------------------------------------------------
|
||||
|
||||
- Support for 2.4 kernels has been removed
|
||||
|
||||
- Added on_free_cmd() callback for dev handlers
|
||||
|
||||
- Fixed BUG() on task aborts.
|
||||
|
||||
- Fixed WCE (Write Cache Enabled) handling in FILEIO
|
||||
(by Ming Zhang <mingz@ele.uri.edu>)
|
||||
|
||||
- Minor cleanups and bug fixes.
|
||||
|
||||
Summary of changes between versions 0.9.3-pre4 and 0.9.3
|
||||
--------------------------------------------------------
|
||||
|
||||
- Fixed IO errors on initiators (return code 20008) under considerable
|
||||
load, because of returned BUSY status. Now QUEUE FULL status returned
|
||||
instead.
|
||||
|
||||
- Fixed "deadlock" on sessions creation.
|
||||
|
||||
- Fixed support for > 2TB storage device in FILEIO handler
|
||||
(by Ming Zhang <mingz@ele.uri.edu>)
|
||||
|
||||
- NULLIO added to FILEIO handler (by Ming Zhang <mingz@ele.uri.edu>)
|
||||
|
||||
- Commands serialization now per-(session, device), i.e. tgt_dev.
|
||||
|
||||
- Minor cleanups and bug fixes
|
||||
|
||||
Summary of changes between versions 0.9.3-pre2 and 0.9.3-pre4
|
||||
-------------------------------------------------------------
|
||||
|
||||
- Sessions registration and unregistrations reimplemented. The
|
||||
interface with target drivers changed (made much more simple, but
|
||||
incompatible).
|
||||
|
||||
- Some kmalloc()'s converted to SLAB caches (by Nathaniel Clark
|
||||
<nate@misrule.us>)
|
||||
|
||||
- Too low timeouts for management operations in scst_disk were
|
||||
increased
|
||||
|
||||
- Cleanups and bug fixes, including several race-based crashes
|
||||
|
||||
Summary of changes between versions 0.9.3-pre1 and 0.9.3-pre2
|
||||
-------------------------------------------------------------
|
||||
|
||||
- Task management was considerably redone. Particularly, now the result
|
||||
of ABORT TASK is returned to remote initiator immediately, except if
|
||||
the result of the aborted command is already started to be sent.
|
||||
|
||||
- Per-session commands serialization implemented as part of "device
|
||||
blocking" cleanup.
|
||||
|
||||
- UA generating after MODE SELECT and LOG SELECT implemented
|
||||
|
||||
- STRICT_SERIALIZING compile-time option added for those who need most
|
||||
robust task management and willing to pay some performance cost for
|
||||
that (see README)
|
||||
|
||||
- IRQ spinlocks were replaced by BH ones, where appropriate.
|
||||
|
||||
- Ability to change ISO image on the fly in CDROM FILEIO module added
|
||||
|
||||
- Cleanups and bug fixes
|
||||
|
||||
Summary of changes between versions 0.9.2 and 0.9.3-pre1
|
||||
--------------------------------------------------------
|
||||
|
||||
- Sophisticated per-initiator access and devices visibility management added
|
||||
(different initiators now could see different set of devices with
|
||||
different permissions)
|
||||
|
||||
- FILEIO dev handler was reimplemented in most areas, including:
|
||||
|
||||
* Management interface via /proc added
|
||||
|
||||
* Various defects using devices/files larger 2Gb were fixed. Thanks
|
||||
to Mark Buechler <mark.buechler@gmail.com>
|
||||
|
||||
* New CDROM FILEIO handler added.
|
||||
|
||||
* Now per-session threads created to improve concurrent performance.
|
||||
|
||||
* New flags added that allow using virtual devices as read only,
|
||||
with write through caching or in O_DIRECT mode.
|
||||
|
||||
* Task management support added.
|
||||
|
||||
- RAID controller (type 0xC) dev handler added.
|
||||
|
||||
- SCSI processor (type 3) dev handler added.
|
||||
|
||||
- Sessions unregistration contexts cleanup.
|
||||
|
||||
- Dev handler's on_free_cmd() was dropped. Target driver's flag
|
||||
"thread_per_session" was dropped as well.
|
||||
|
||||
- Internal locking partially redone.
|
||||
|
||||
- A lot of stability/bug fixes, cleanups and performance improvements.
|
||||
|
||||
Summary of changes between versions 0.9.1 and 0.9.2
|
||||
---------------------------------------------------
|
||||
|
||||
- 2.6 support, developed and tested on 2.6.7.
|
||||
|
||||
- FILEIO/BLKDEV virtual disk device handler added. See main README for
|
||||
details.
|
||||
|
||||
- /proc updates, "trace_log_level" entry added to change traced
|
||||
events on the fly.
|
||||
|
||||
- Some internal interfaces changed, see the documentation.
|
||||
|
||||
- Device handlers' source code layout changed, all device handlers
|
||||
live in one subdirectory now.
|
||||
|
||||
- Crash on memory allocation failure while building SG data vector
|
||||
fixed.
|
||||
|
||||
- Other minor fixes
|
||||
|
||||
Summary of changes between versions 0.9.0a and 0.9.1
|
||||
----------------------------------------------------
|
||||
|
||||
- scst_debug.* files are also installed together with scsi_tgt.h
|
||||
(required by QLA2x00 target and could be useful for other target
|
||||
drivers)
|
||||
|
||||
- CDB length for unknown commands (e.g. vendor ones) is now determined
|
||||
from CDB
|
||||
|
||||
- Race with the commands serialization on one drive fixed
|
||||
|
||||
- Cleanup
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# Common makefile for SCSI target mid-level and its drivers
|
||||
#
|
||||
# Copyright (C) 2004 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
# and Leonid Stoljar
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#
|
||||
|
||||
# Decide to use which kernel src. If not specified, is current running kernel.
|
||||
#export KDIR=/usr/src/linux-2.6
|
||||
|
||||
SCST_DIR=src
|
||||
|
||||
all:
|
||||
cd $(SCST_DIR) && $(MAKE) $@
|
||||
|
||||
install:
|
||||
cd $(SCST_DIR) && $(MAKE) $@
|
||||
|
||||
uninstall:
|
||||
cd $(SCST_DIR) && $(MAKE) $@
|
||||
|
||||
clean:
|
||||
cd $(SCST_DIR) && $(MAKE) $@
|
||||
|
||||
extraclean:
|
||||
cd $(SCST_DIR) && $(MAKE) $@
|
||||
|
||||
help:
|
||||
@echo " all (the default) : make all"
|
||||
@echo " clean : clean files"
|
||||
@echo " extraclean : clean + clean dependencies"
|
||||
@echo " install : install"
|
||||
@echo " uninstall : uninstall"
|
||||
@echo " Notes :"
|
||||
@echo " - install and uninstall must be made as root"
|
||||
|
||||
.PHONY: all install uninstall clean extraclean help
|
||||
+408
@@ -0,0 +1,408 @@
|
||||
Generic SCSI target mid-level for Linux (SCST)
|
||||
==============================================
|
||||
|
||||
Version 0.9.5, XX XXX 2006
|
||||
--------------------------
|
||||
|
||||
SCST is designed to provide unified, consistent interface between SCSI
|
||||
target drivers and Linux kernel and simplify target drivers development
|
||||
as much as possible. Detail description of SCST's features and internals
|
||||
could be found in "Generic SCSI Target Middle Level for Linux" document
|
||||
SCST's Internet page http://scst.sourceforge.net.
|
||||
|
||||
SCST looks to be quite stable (for beta) and useful. It supports disks
|
||||
(SCSI type 0), tapes (type 1), processor (type 3), CDROM's (type 5), MO
|
||||
disks (type 7), medium changers (type 8) and RAID controller (type 0xC).
|
||||
There are also FILEIO and "performance" device handlers. In addition,
|
||||
starting from version 0.9.3 advanced per-initiator access and devices
|
||||
visibility management is added, so different initiators could see
|
||||
different set of devices with different access permissions. See below
|
||||
for details.
|
||||
|
||||
This is more or less stable (but still beta) version.
|
||||
|
||||
Tested mostly on "vanilla" 2.6.17.8 kernel from kernel.org.
|
||||
|
||||
Device handlers
|
||||
---------------
|
||||
|
||||
Device specific drivers (device handlers) are plugins for SCST, which
|
||||
help SCST to analyze incoming requests and determine parameters,
|
||||
specific to various types of devices. If an appropriate device handler
|
||||
for a SCSI device type isn't loaded, SCST doesn't know how to handle
|
||||
devices of this type, so they will be invisible for remote initiators
|
||||
(more precisely, "LUN not supported" sense code will be returned).
|
||||
|
||||
In addition to device handlers for real devices, there are FILEIO and
|
||||
"performance" ones.
|
||||
|
||||
FILEIO device handler works over files on file systems and makes from
|
||||
them virtual remotely available SCSI disks or CDROM's. In addition, it
|
||||
allows to work directly over a block device, e.g. local IDE or SCSI disk
|
||||
or ever disk partition, where there is no file systems overhead. Using
|
||||
block devices comparing to sending SCSI commands directly to SCSI
|
||||
mid-level via scsi_do_req() has advantage that data are transfered via
|
||||
system cache, so it is possible to fully benefit from caching and read
|
||||
ahead performed by Linux's VM subsystem. The only disadvantage here that
|
||||
there is superfluous data copying between the cache and SCST's buffers.
|
||||
This issue is going to be addressed in the next release. Virtual CDROM's
|
||||
are useful for remote installation. See below for details how to setup
|
||||
and use FILEIO device handler.
|
||||
|
||||
"Performance" device handlers for disks, MO disks and tapes in their
|
||||
exec() method skip (pretend to execute) all READ and WRITE operations
|
||||
and thus provide a way for direct link performance measurements without
|
||||
overhead of actual data transferring from/to underlying SCSI device.
|
||||
Starting from 0.9.3 these handlers are incorporated inside of
|
||||
corresponding device handler for real device and could be assigned on
|
||||
run-time via "assign" command in "/proc/scsi_tgt/scsi_tgt" (see below).
|
||||
|
||||
NOTE: Since "perf" device handlers on READ operations don't touch the
|
||||
==== commands' data buffer, it is returned to remote initiators as it
|
||||
was allocated, without even being zeroed. Thus, "perf" device
|
||||
handlers impose some security risk, so use them with caution.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
At first, make sure that the link "/lib/modules/`you_kernel_version`/build"
|
||||
points to the source code for your currently running kernel.
|
||||
|
||||
Then, if you are going to work on 2.6 kernels, since in those kernels
|
||||
scsi_do_req() works in LIFO order, instead of expected and required
|
||||
FIFO, SCST needs a new function scsi_do_req_fifo() to be added in the
|
||||
kernel. Patch 26_scst.patch (or 26_scst-2.6.14-.patch for early kernels)
|
||||
from "kernel" directory does that. If it doesn't apply to your kernel
|
||||
version, apply it manually, it only adds that function and nothing more.
|
||||
You may not patch the kernel if STRICT_SERIALIZING is defined during the
|
||||
compilation (see its description below).
|
||||
|
||||
To compile SCST go to 'src' directory and type 'make' on 2.6 kernels and
|
||||
'make -f Makefile-24' on 2.4 ones. It will build SCST itself and its
|
||||
device handlers. To install them type 'make install'. The driver modules
|
||||
will be installed in
|
||||
'/lib/modules/`you_kernel_version`/kernel/drivers/scsi/scsi_tgt' on 2.4
|
||||
kernels and in '/lib/modules/`you_kernel_version`/extra' on 2.6 ones. In
|
||||
addition, scsi_tgt.h, scst_debug.h and scst_debug.c will be copied to
|
||||
'/usr/local/include/scst'. The first file contains all SCST's public
|
||||
data definition, which are used by target drivers. The other ones
|
||||
support debug messages logging.
|
||||
|
||||
Then you can load any module by typing 'modprobe drive_name'. The names are:
|
||||
|
||||
- scsi_tgt - SCST itself
|
||||
- scst_disk - device handler for disks (type 0)
|
||||
- scst_tape - device handler for tapes (type 1)
|
||||
- scst_processor - device handler for processors (type 3)
|
||||
- scst_cdrom - device handler for CDROMs (type 5)
|
||||
- scst_modisk - device handler for MO disks (type 7)
|
||||
- scst_changer - device handler for medium changers (type 8)
|
||||
- scst_raid - device handler for storage array controller (e.g. raid) (type C)
|
||||
- scst_fileio - device handler for FILE IO (disk or ISO CD image).
|
||||
|
||||
Then, to see your devices remotely, you need to add them to at least
|
||||
"Default" security group (see below how). By default, no local devices
|
||||
are seen remotely. There must be LUN 0 in each security group, i.e. LUs
|
||||
numeration must not start from, e.g., 1.
|
||||
|
||||
Module "scst_target" supports parameter "scst_threads", which allows to
|
||||
set count of SCST's threads (CPU count by default).
|
||||
|
||||
IMPORTANT: without loading appropriate device handler, corresponding devices
|
||||
========= will be invisible for remote initiators, which could lead to holes
|
||||
in the LUN addressing, so automatic device scanning by remote SCSI
|
||||
mid-level could not notice the devices. Therefore you will have
|
||||
to add them manually via
|
||||
'echo "scsi add-single-device A 0 0 B" >/proc/scsi/scsi',
|
||||
where A - is the host number, B - LUN.
|
||||
|
||||
IMPORTANT 1: In the current version simultaneous access to local SCSI
|
||||
=========== devices via standard high-level SCSI drivers (sd, st, sg,
|
||||
etc.) and SCST's target drivers is unsupported. Especially
|
||||
it is important for execution via sg and st commands that
|
||||
change the state of devices and their parameters, because
|
||||
that could lead to data corruption. If any such command
|
||||
is done, at least related device handler driver(s) must be
|
||||
restarted. For block devices READ/WRITE commands using direct
|
||||
disk handler look to be safe.
|
||||
|
||||
To uninstall, type 'make uninstall'. It is not implemented for 2.6
|
||||
kernels.
|
||||
|
||||
If you install QLA2x00 target driver's source code in this directory,
|
||||
then you can build, install or uninstall it by typing 'make qla', 'make
|
||||
qla_install' or 'make qla_uninstall' correspondingly. Or 'make qla26',
|
||||
'make qla26_install' or 'make qla26_uninstall' for new 2.6 driver. For
|
||||
more details about QLA2x00 target drivers see their README files.
|
||||
|
||||
Compilation options
|
||||
-------------------
|
||||
|
||||
There are the following compilation options, that could be commented
|
||||
in/out in Makefile:
|
||||
|
||||
- DEBUG - turns on some debugging code, including some logging. Makes
|
||||
the driver considerably bigger and slower, producing large amount of
|
||||
log data.
|
||||
|
||||
- TRACING - turns on ability to log events. Makes the driver considerably
|
||||
bigger and lead to some performance loss.
|
||||
|
||||
- EXTRACHECKS - adds extra validity checks in the various places.
|
||||
|
||||
- DEBUG_TM - turns on task management functions debugging, when on
|
||||
LUN 0 in the "Default" group some of the commands will be delayed for
|
||||
about 60 sec., so making the remote initiator send TM functions, eg
|
||||
ABORT TASK and TARGET RESET. Also set TM_DBG_GO_OFFLINE symbol in the
|
||||
Makefile to 1 if you want that the device eventually become
|
||||
completely unresponsive, or to 0 otherwise to circle around ABORTs
|
||||
and RESETs code. Needs DEBUG turned on.
|
||||
|
||||
- STRICT_SERIALIZING - makes SCST send all commands to underlying SCSI
|
||||
device synchronously, one after one. This makes task management more
|
||||
reliable, with cost of some performance penalty. This is mostly
|
||||
actual for stateful SCSI devices like tapes, where the result of
|
||||
command's execution depends from device's settings set by previous
|
||||
commands. Disk and RAID devices are stateless in the most cases. The
|
||||
current SCSI core in Linux doesn't allow to abort all commands
|
||||
reliably if they sent asynchronously to a stateful device. Turned off
|
||||
by default, turn it on if you use stateful device(s) and need as much
|
||||
error recovery reliability as possible. As a side effect, no kernel
|
||||
patching is necessary.
|
||||
|
||||
- SCST_HIGHMEM - if defined on HIGHMEM systems with 2.6 kernels, it
|
||||
allows SCST to use HIGHMEM. This is very experimental feature and it
|
||||
is unclear, if it brings something valuable, except some performance
|
||||
hit, so in the current version it is disabled. Note, that
|
||||
SCST_HIGHMEM isn't required for HIGHMEM systems and SCST will work
|
||||
fine on them with SCST_HIGHMEM off. Untested.
|
||||
|
||||
- SCST_STRICT_SECURITY - if defined, makes SCST zero allocated data
|
||||
buffers. Undefining it (default) considerably improves performance
|
||||
and eases CPU load, but could create a security hole (information
|
||||
leakage), so enable it, if you have strict security requirements.
|
||||
|
||||
SCST "/proc" commands
|
||||
---------------------
|
||||
|
||||
For communications with user space programs SCST provides proc-based
|
||||
interface in "/proc/scsi_tgt" directory. It contains the following
|
||||
entries:
|
||||
|
||||
- "help" file, which provides online help for SCST commands
|
||||
|
||||
- "scsi_tgt" file, which on read provides information of serving by SCST
|
||||
devices and their dev handlers. On write it supports the following
|
||||
command:
|
||||
|
||||
* "assign H:C:I:L HANDLER_NAME" assigns dev handler "HANDLER_NAME"
|
||||
on device with host:channel:id:lun
|
||||
|
||||
- "sessions" file, which lists currently connected initiators (open sessions)
|
||||
|
||||
- "threads" file, which allows to read and set number of SCST's threads
|
||||
|
||||
- "version" file, which shows version of SCST
|
||||
|
||||
- "trace_level" file, which allows to read and set trace (logging) level
|
||||
for SCST. See "help" file for list of trace levels.
|
||||
|
||||
Each dev handler has own subdirectory. Most dev handler have only two
|
||||
files in this subdirectory: "trace_level" and "type". The first one is
|
||||
similar to main SCST "trace_level" file, the latter one shows SCSI type
|
||||
number of this handler as well as some text description.
|
||||
|
||||
For example, "echo "assign 1:0:1:0 dev_disk" >/proc/scsi_tgt/scsi_tgt"
|
||||
will assign device handler "dev_disk" to real device sitting on host 1,
|
||||
channel 0, ID 1, LUN 0.
|
||||
|
||||
Access and devices visibility management
|
||||
----------------------------------------
|
||||
|
||||
Access and devices visibility management allows for an initiator or
|
||||
group of initiators to have different limited set of LUs/LUNs (security
|
||||
group) each with appropriate access permissions. Initiator is
|
||||
represented as a SCST session. Session is binded to security group on
|
||||
its registration time by character "name" parameter of the registration
|
||||
function, which provided by target driver, based on its internal
|
||||
authentication. For example, for FC "name" could be WWN or just loop
|
||||
ID. For iSCSI this could be iSCSI login credentials or iSCSI initiator
|
||||
name. Each security group has set of names assigned to it by system
|
||||
administrator. Session is binded to security group with provided name.
|
||||
If no such groups found, the session binded to "Default" group.
|
||||
|
||||
In /proc/scsi_tgt each group represented as "groups/GROUP_NAME/"
|
||||
subdirectory. In it there are files "devices" and "users". File
|
||||
"devices" lists all devices and their LUNs in the group, file "users"
|
||||
lists all names that should be binded to this group.
|
||||
|
||||
To configure access and devices visibility management SCST provides the
|
||||
following files and directories under /proc/scsi_tgt:
|
||||
|
||||
- "add_group GROUP" to /proc/scsi_tgt/scsi_tgt adds group "GROUP"
|
||||
|
||||
- "del_group GROUP" to /proc/scsi_tgt/scsi_tgt deletes group "GROUP"
|
||||
|
||||
- "add H:C:I:L lun [RO]" to /proc/scsi_tgt/groups/GROUP/devices adds
|
||||
device with host:channel:id:lun as LUN "lun" in group "GROUP". Optionally,
|
||||
the device could be marked as read only.
|
||||
|
||||
- "del H:C:I:L" to /proc/scsi_tgt/groups/GROUP/devices deletes device with
|
||||
host:channel:id:lun from group "GROUP"
|
||||
|
||||
- "add V_NAME lun [RO]" to /proc/scsi_tgt/groups/GROUP/devices adds device with
|
||||
virtual name "V_NAME" as LUN "lun" in group "GROUP". Optionally, the device
|
||||
could be marked as read only.
|
||||
|
||||
- "del V_NAME" to /proc/scsi_tgt/groups/GROUP/devices deletes device with
|
||||
virtual name "V_NAME" from group "GROUP"
|
||||
|
||||
- "clear" to /proc/scsi_tgt/groups/GROUP/devices clears the list of devices
|
||||
for group "GROUP"
|
||||
|
||||
- "add NAME" to /proc/scsi_tgt/groups/GROUP/names adds name "NAME" to group
|
||||
"GROUP"
|
||||
|
||||
- "del NAME" to /proc/scsi_tgt/groups/GROUP/names deletes name "NAME" from group
|
||||
"GROUP"
|
||||
|
||||
- "clear" to /proc/scsi_tgt/groups/GROUP/names clears the list of names
|
||||
for group "GROUP"
|
||||
|
||||
Examples:
|
||||
|
||||
- "echo "add 1:0:1:0 0" >/proc/scsi_tgt/groups/Default/devices" will
|
||||
add real device sitting on host 1, channel 0, ID 1, LUN 0 to "Default"
|
||||
group with LUN 0.
|
||||
|
||||
- "echo "add disk1 1" >/proc/scsi_tgt/groups/Default/devices" will
|
||||
add virtual FILEIO device with name "disk1" to "Default" group
|
||||
with LUN 1.
|
||||
|
||||
FILEIO device handler
|
||||
---------------------
|
||||
|
||||
After loading FILEIO device handler creates in "/proc/scsi_tgt/"
|
||||
subdirectories "disk_fileio" and "cdrom_fileio". They have similar layout:
|
||||
|
||||
- "trace_level" and "type" files as described for other dev handlers
|
||||
|
||||
- "help" file, which provides online help for FILEIO commands
|
||||
|
||||
- "disk_fileio"/"cdrom_fileio" files, which on read provides
|
||||
information of currently open device files. On write it supports the
|
||||
following command:
|
||||
|
||||
* "open NAME PATH [FLAGS]" - opens file "PATH" as device "NAME" with
|
||||
flags "FLAGS. Possible flags:
|
||||
|
||||
- WRITE_THROUGH - write back caching disabled
|
||||
|
||||
- READ_ONLY - read only
|
||||
|
||||
- O_DIRECT - both read and write caching disabled (doesn't work
|
||||
currently).
|
||||
|
||||
- NULLIO - in this mode no real IO will be done, but success will be
|
||||
returned. Intended to be used for performance measurements at the same
|
||||
way as "*_perf" handlers.
|
||||
|
||||
* "close NAME" - closes device "NAME".
|
||||
|
||||
For example, "echo "open disk1 /vdisks/disk1" >/proc/scsi_tgt/disk_fileio/disk_fileio"
|
||||
will open file /vdisks/disk1 as virtual FILEIO disk with name "disk1".
|
||||
|
||||
IMPORTANT: by default for performance reasons FILEIO devices use write back
|
||||
========= caching policy, so if you care about the consistence of file systems,
|
||||
laying over them, and your data you must supply your target
|
||||
server with some king of UPS or disable write back caching
|
||||
via WRITE_THROUGH flag. The FS joutnaling over write back
|
||||
caching enabled devices doesn't protect from power failures
|
||||
on the target side, therefore even after successful journal
|
||||
rollback you very much risk to loose your data.
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
||||
Before doing any performance measurements note that:
|
||||
|
||||
I. Maximum performance is possible only with real SCSI devices or
|
||||
performance handlers. FILEIO handler isn't optimized for performance
|
||||
yet, although, if you have enough CPU power, it could provide very
|
||||
acceptable results, when aggregate throughput is close to aggregate
|
||||
throuput locally on the target on the same disks.
|
||||
|
||||
II. In order to get the maximum performance you should:
|
||||
|
||||
1. For SCST:
|
||||
|
||||
- Disable in Makefile STRICT_SERIALIZING, EXTRACHECKS, TRACING, DEBUG,
|
||||
SCST_STRICT_SECURITY, SCST_HIGHMEM
|
||||
|
||||
2. For Qlogic target driver:
|
||||
|
||||
- Disable in Makefile EXTRACHECKS, TRACING, DEBUG_TGT, DEBUG_WORK_IN_THREAD
|
||||
|
||||
3. For device handlers, including FILEIO:
|
||||
|
||||
- Disable in Makefile TRACING, DEBUG
|
||||
|
||||
IMPORTANT: Some of those options enabled by default, i.e. SCST is optimized
|
||||
========= currently rather for development, not for performance.
|
||||
|
||||
4. For kernel:
|
||||
|
||||
- Don't enable debug/hacking features, i.e. use them as they are by
|
||||
default.
|
||||
|
||||
- The default kernel read-ahead and queuing settings are optimized
|
||||
for locally attached disks, therefore they are not optimal if they
|
||||
attached remotly (our case), which sometimes could lead to unexpectedly
|
||||
low throughput. You should increase read-ahead size
|
||||
(/sys/block/device/queue/read_ahead_kb) for at least 256Kb or even
|
||||
more on all initiators and the target. Also experiment with other
|
||||
parameters in /sys/block/device directory, they also affect the
|
||||
performance. If you find the best values, please share them with us.
|
||||
|
||||
5. For hardware.
|
||||
|
||||
- Make sure that your target hardware (e.g. target FC card) and underlaying
|
||||
SCSI hardware (e.g. SCSI card to which your disks connected) stay on
|
||||
different PCI buses. They will have to work in parallel, so it
|
||||
will be better if they don't race for the bus. The problem is not
|
||||
only in the bandwidth, which they have to share, but also in the
|
||||
interaction between the cards during that competition. We have told
|
||||
that in some cases it could lead to 5-10 times less performance, than
|
||||
expected.
|
||||
|
||||
Just for reference: we had with 0.9.2 and "old" Qlogic driver on 2.4.2x
|
||||
kernel, where we did carefull performance study, aggregate throuhput
|
||||
about 390 Mb/sec from 2 qla2300 cards sitting on different 64-bit PCI
|
||||
buses and working simultaneously for two different initiators with
|
||||
several simultaneously working load programs on each. From one card -
|
||||
about 190 Mb/sec. We used tape_perf handler, so there was no influence
|
||||
from underlying SCSI hardware, i.e. we measured only SCST/FC overhead.
|
||||
The target computer configuration was not very modern for the moment:
|
||||
something like 2x1GHz Intel P3 Xeon CPUs. You can estimate the
|
||||
memory/PCI speed from that. CPU load was ~5%, there were ~30K IRQ/sec
|
||||
and no additional SCST related context switches. Version 0.9.3 at the
|
||||
same setup will usually have 1 CS/cmd for buffer allocation, so the will
|
||||
be about 5-10K CS/sec. This will be fixed in the next version, when
|
||||
sgv_pool is integrated.
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
Thanks to:
|
||||
|
||||
* Mark Buechler <mark.buechler@gmail.com> for a lot of useful
|
||||
suggestions, bug reports and help in debugging.
|
||||
|
||||
* Ming Zhang <mingz@ele.uri.edu> for fixes and comments.
|
||||
|
||||
* Nathaniel Clark <nate@misrule.us> for fixes and comments.
|
||||
|
||||
* Calvin Morrow <calvin.morrow@comcast.net> for testing and usful
|
||||
suggestions.
|
||||
|
||||
Vladislav Bolkhovitin <vst@vlnb.net>, http://scst.sourceforge.net
|
||||
@@ -0,0 +1,55 @@
|
||||
To be done
|
||||
----------
|
||||
|
||||
- Kernel build integration (patch).
|
||||
|
||||
- Reimplement FILEIO handler with usage of async. read/write operations
|
||||
(in order to avoid unnecessary context switches) and direct access to
|
||||
the page cache (in order to avoid data copy between it and internal
|
||||
buffers). Requires modifications of the kernel.
|
||||
|
||||
- O_DIRECT mode doesn't work (oops'es somewhere in the kernel)
|
||||
|
||||
- Close integration with Linux initiator SCSI mil-level, including
|
||||
queue types (simple, ordered, etc.) and local initiators (sd, st, sg,
|
||||
etc.) support. Requires modifications of the kernel.
|
||||
|
||||
- Improve task management and Unit Attention conditions handling
|
||||
using ACA in order to make them always reliable. Honoring NACA, QErr,
|
||||
TST, UA_INTLCK_CTRL bits. Requires deep modifications of the kernel.
|
||||
|
||||
- Move linear searches to hash-table based.
|
||||
|
||||
- Create dev handler for block devices, which would insert commands as
|
||||
block requests in the device's block queue, so they would processed
|
||||
by IO-scheduler then.
|
||||
|
||||
- Redone some semaphores with completion interface.
|
||||
|
||||
- HIGHMEM cleanup. Looks like HIGHMEM usage doesn't worth the effort and
|
||||
performance hit, at least until FILEIO handler doesn't use the page
|
||||
cache directly, so disable it for now, although the code looks ready.
|
||||
To enable it, set SCST_HIGHMEM in 1 in scst_priv.h. HIGHMEM is not
|
||||
supported on 2.4 and is not going to be.
|
||||
|
||||
- More intelligent IO-throttling.
|
||||
|
||||
- Small ToDo's spread all over the code.
|
||||
|
||||
- Investigate possible missed emulated UA cases.
|
||||
|
||||
- More target drivers errors handling (timeouts for commands, sent
|
||||
to target drivers, e.g. via xmit_response(), etc.) with some kind
|
||||
of error recovery.
|
||||
|
||||
- Additional ability for target drivers to ask for command's retry also
|
||||
after xmit_response() and rdy_to_xfer() returned (for example, if a
|
||||
command was successfully sent to the target card, but later it was
|
||||
returned by the card with BUSY completion status).
|
||||
|
||||
- Asynchronous Event Notification support.
|
||||
|
||||
- Support of SCSI 3 variable length CDBs, as well as OSD's extensions.
|
||||
|
||||
- Advanced SCSI commands support: COPY, EXTENDED COPY, PERSISTENT
|
||||
RESERVE IN/OUT, third party RESERVE, etc.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* include/scst_debug.c
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* Contains helper functions for execution tracing and error reporting.
|
||||
* Intended to be included in main .c file.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
|
||||
unsigned long trace_flag;
|
||||
|
||||
#define TRACE_BUF_SIZE 512
|
||||
|
||||
static char trace_buf[TRACE_BUF_SIZE];
|
||||
static spinlock_t trace_buf_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
int debug_print_prefix(const char *func, int line)
|
||||
{
|
||||
int i = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&trace_buf_lock, flags);
|
||||
|
||||
if (trace_flag & TRACE_PID)
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE, "[%d]: ",
|
||||
current->pid);
|
||||
if (trace_flag & TRACE_FUNCTION)
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", func);
|
||||
if (trace_flag & TRACE_LINE)
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%i:", line);
|
||||
|
||||
if (i > 0)
|
||||
PRINTN(LOG_FLAG, "%s", trace_buf);
|
||||
|
||||
spin_unlock_irqrestore(&trace_buf_lock, flags);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void debug_print_buffer(const void *data, int len)
|
||||
{
|
||||
int z, z1, i;
|
||||
const unsigned char *buf = (const unsigned char *) data;
|
||||
int f = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&trace_buf_lock, flags);
|
||||
|
||||
PRINT(NO_FLAG, " (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F");
|
||||
for (z = 0, z1 = 0, i = 0; z < len; z++) {
|
||||
if (z % 16 == 0) {
|
||||
if (z != 0) {
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
|
||||
" ");
|
||||
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1);
|
||||
z1++) {
|
||||
if ((buf[z1] >= 0x20) &&
|
||||
(buf[z1] < 0x80))
|
||||
trace_buf[i++] = buf[z1];
|
||||
else
|
||||
trace_buf[i++] = '.';
|
||||
}
|
||||
trace_buf[i] = '\0';
|
||||
PRINT(NO_FLAG, "%s", trace_buf);
|
||||
i = 0;
|
||||
f = 1;
|
||||
}
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
|
||||
"%4x: ", z);
|
||||
}
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%02x ",
|
||||
buf[z]);
|
||||
}
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, " ");
|
||||
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); z1++) {
|
||||
if ((buf[z1] > 0x20) && (buf[z1] < 0x80))
|
||||
trace_buf[i++] = buf[z1];
|
||||
else
|
||||
trace_buf[i++] = '.';
|
||||
}
|
||||
trace_buf[i] = '\0';
|
||||
if (f) {
|
||||
PRINT(LOG_FLAG, "%s", trace_buf)
|
||||
} else {
|
||||
PRINT(NO_FLAG, "%s", trace_buf);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&trace_buf_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* DEBUG || TRACING */
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* include/scst_debug.h
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* Contains macroses for execution tracing and error reporting
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __SCST_DEBUG_H
|
||||
#define __SCST_DEBUG_H
|
||||
|
||||
#include <linux/config.h> /* for CONFIG_SCSI_TARGET_EXTRACHEKS
|
||||
CONFIG_SCSI_TARGET_TRACING
|
||||
CONFIG_SCSI_TARGET_DEBUG */
|
||||
|
||||
#if !defined(EXTRACHECKS) && defined(CONFIG_SCSI_TARGET_EXTRACHECKS)
|
||||
#define EXTRACHECKS
|
||||
#endif
|
||||
|
||||
#if !defined(TRACING) && defined(CONFIG_SCSI_TARGET_TRACING)
|
||||
#define TRACING
|
||||
#endif
|
||||
|
||||
#if !defined(DEBUG) && defined(CONFIG_SCSI_TARGET_DEBUG)
|
||||
#define DEBUG
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
//# define LOG_FLAG KERN_DEBUG
|
||||
# define LOG_FLAG KERN_INFO
|
||||
|
||||
# define INFO_FLAG KERN_INFO
|
||||
# define ERROR_FLAG KERN_INFO
|
||||
#else
|
||||
# define LOG_FLAG KERN_INFO
|
||||
# define INFO_FLAG KERN_INFO
|
||||
# define ERROR_FLAG KERN_ERR
|
||||
#endif
|
||||
|
||||
#define NO_FLAG ""
|
||||
|
||||
#define TRACE_NULL 0x00000000
|
||||
#define TRACE_DEBUG 0x00000001
|
||||
#define TRACE_FUNCTION 0x00000002
|
||||
#define TRACE_LINE 0x00000004
|
||||
#define TRACE_PID 0x00000008
|
||||
#define TRACE_ENTRYEXIT 0x00000010
|
||||
#define TRACE_BUFF 0x00000020
|
||||
#define TRACE_MEMORY 0x00000040
|
||||
#define TRACE_SG 0x00000080
|
||||
#define TRACE_OUT_OF_MEM 0x00000100
|
||||
#define TRACE_MINOR 0x00000200 /* less important events */
|
||||
#define TRACE_MGMT 0x00000400
|
||||
#define TRACE_MGMT_DEBUG 0x00000800
|
||||
#define TRACE_SCSI 0x00001000
|
||||
#define TRACE_SPECIAL 0x00002000 /* filtering debug, etc */
|
||||
#define TRACE_ALL 0xffffffff
|
||||
/* Flags 0xXXXX0000 are local for users */
|
||||
|
||||
#define PRINT(log_flag, format, args...) printk(log_flag format "\n", ## args);
|
||||
#define PRINTN(log_flag, format, args...) printk(log_flag format, ## args);
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
|
||||
extern unsigned long trace_flag;
|
||||
|
||||
extern int debug_print_prefix(const char *func, int line);
|
||||
extern void debug_print_buffer(const void *data, int len);
|
||||
|
||||
#define TRACE(trace, format, args...) \
|
||||
do { \
|
||||
if (trace_flag & (trace)) \
|
||||
{ \
|
||||
char *__tflag = LOG_FLAG; \
|
||||
if (debug_print_prefix(__FUNCTION__, __LINE__) > 0) \
|
||||
{ \
|
||||
__tflag = NO_FLAG; \
|
||||
} \
|
||||
PRINT(NO_FLAG, "%s" format, __tflag, args); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define TRACE_LOG_FLAG(log_flag, trace, format, args...) \
|
||||
do { \
|
||||
char *__tflag = log_flag; \
|
||||
if (trace_flag & (trace)) \
|
||||
{ \
|
||||
if (debug_print_prefix(__FUNCTION__, __LINE__) > 0) \
|
||||
{ \
|
||||
__tflag = NO_FLAG; \
|
||||
} \
|
||||
} \
|
||||
PRINT(NO_FLAG, "%s" format, __tflag, args); \
|
||||
} while(0)
|
||||
|
||||
#define TRACE_BUFFER(message, buff, len) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_BUFF) \
|
||||
{ \
|
||||
char *__tflag = LOG_FLAG; \
|
||||
if (debug_print_prefix(__FUNCTION__, __LINE__) > 0) \
|
||||
{ \
|
||||
__tflag = NO_FLAG; \
|
||||
} \
|
||||
PRINT(NO_FLAG, "%s%s:", __tflag, message); \
|
||||
debug_print_buffer(buff, len); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define TRACE_BUFF_FLAG(flag, message, buff, len) \
|
||||
do { \
|
||||
if (trace_flag & (flag)) \
|
||||
{ \
|
||||
char *__tflag = LOG_FLAG; \
|
||||
if (debug_print_prefix(__FUNCTION__, __LINE__) > 0) \
|
||||
{ \
|
||||
__tflag = NO_FLAG; \
|
||||
} \
|
||||
PRINT(NO_FLAG, "%s%s:", __tflag, message); \
|
||||
debug_print_buffer(buff, len); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#else /* DEBUG || TRACING */
|
||||
|
||||
#define TRACE(trace, args...) {}
|
||||
#define TRACE_BUFFER(message, buff, len) {}
|
||||
#define TRACE_BUFF_FLAG(flag, message, buff, len) {}
|
||||
|
||||
#endif /* DEBUG || TRACING */
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define TRACE_MEM(format, args...) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_MEMORY) \
|
||||
{ \
|
||||
char *__tflag = LOG_FLAG; \
|
||||
if (debug_print_prefix(__FUNCTION__, __LINE__) > 0) \
|
||||
{ \
|
||||
__tflag = NO_FLAG; \
|
||||
} \
|
||||
PRINT(NO_FLAG, "%s" format, __tflag, args); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define TRACE_DBG(format, args...) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_DEBUG) \
|
||||
{ \
|
||||
char *__tflag = LOG_FLAG; \
|
||||
if (debug_print_prefix(__FUNCTION__, __LINE__) > 0) \
|
||||
{ \
|
||||
__tflag = NO_FLAG; \
|
||||
} \
|
||||
PRINT(NO_FLAG, "%s" format, __tflag, args); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define TRACE_MGMT_DBG(format, args...) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_MGMT_DEBUG) \
|
||||
{ \
|
||||
char *__tflag = LOG_FLAG; \
|
||||
if (debug_print_prefix(__FUNCTION__, __LINE__) > 0) \
|
||||
{ \
|
||||
__tflag = NO_FLAG; \
|
||||
} \
|
||||
PRINT(NO_FLAG, "%s" format, __tflag, args); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define PRINT_ERROR_PR(format, args...) \
|
||||
do { \
|
||||
if (ERROR_FLAG != LOG_FLAG) \
|
||||
{ \
|
||||
TRACE_LOG_FLAG(LOG_FLAG, trace_flag, "%s: ***ERROR*** " format, \
|
||||
LOG_PREFIX, args); \
|
||||
} \
|
||||
TRACE_LOG_FLAG(ERROR_FLAG, trace_flag, "%s: ***ERROR*** " format, \
|
||||
LOG_PREFIX, args); \
|
||||
} while(0)
|
||||
|
||||
#define PRINT_INFO_PR(format, args...) \
|
||||
do { \
|
||||
if (INFO_FLAG != LOG_FLAG) \
|
||||
{ \
|
||||
TRACE_LOG_FLAG(LOG_FLAG, trace_flag, "%s: " \
|
||||
format, LOG_PREFIX, args); \
|
||||
} \
|
||||
TRACE_LOG_FLAG(INFO_FLAG, trace_flag, "%s: " \
|
||||
format, LOG_PREFIX, args); \
|
||||
} while(0)
|
||||
|
||||
#define PRINT_ERROR(format, args...) \
|
||||
do { \
|
||||
if (ERROR_FLAG != LOG_FLAG) \
|
||||
{ \
|
||||
TRACE_LOG_FLAG(LOG_FLAG, trace_flag, "***ERROR*** " format, args); \
|
||||
} \
|
||||
TRACE_LOG_FLAG(ERROR_FLAG, trace_flag, "***ERROR*** " format, args); \
|
||||
} while(0)
|
||||
|
||||
#define PRINT_INFO(format, args...) \
|
||||
do { \
|
||||
if (INFO_FLAG != LOG_FLAG) \
|
||||
{ \
|
||||
TRACE_LOG_FLAG(LOG_FLAG, trace_flag, format, args); \
|
||||
} \
|
||||
TRACE_LOG_FLAG(INFO_FLAG, trace_flag, format, args); \
|
||||
} while(0)
|
||||
|
||||
#define TRACE_ENTRY() \
|
||||
do { \
|
||||
if (trace_flag & TRACE_ENTRYEXIT) \
|
||||
{ \
|
||||
if (trace_flag & TRACE_PID) \
|
||||
{ \
|
||||
PRINT(LOG_FLAG, "[%d]: ENTRY %s", current->pid, \
|
||||
__FUNCTION__); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
PRINT(LOG_FLAG, "ENTRY %s", __FUNCTION__); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define TRACE_EXIT() \
|
||||
do { \
|
||||
if (trace_flag & TRACE_ENTRYEXIT) \
|
||||
{ \
|
||||
if (trace_flag & TRACE_PID) \
|
||||
{ \
|
||||
PRINT(LOG_FLAG, "[%d]: EXIT %s", current->pid, \
|
||||
__FUNCTION__); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
PRINT(LOG_FLAG, "EXIT %s", __FUNCTION__); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define TRACE_EXIT_RES(res) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_ENTRYEXIT) \
|
||||
{ \
|
||||
if (trace_flag & TRACE_PID) \
|
||||
{ \
|
||||
PRINT(LOG_FLAG, "[%d]: EXIT %s: %ld", current->pid, \
|
||||
__FUNCTION__, (long)(res)); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
PRINT(LOG_FLAG, "EXIT %s: %ld", __FUNCTION__, (long)(res)); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define TRACE_EXIT_HRES(res) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_ENTRYEXIT) \
|
||||
{ \
|
||||
if (trace_flag & TRACE_PID) \
|
||||
{ \
|
||||
PRINT(LOG_FLAG, "[%d]: EXIT %s: 0x%lx", current->pid, \
|
||||
__FUNCTION__, (long)(res)); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
PRINT(LOG_FLAG, "EXIT %s: %lx", __FUNCTION__, (long)(res)); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#else /* DEBUG */
|
||||
|
||||
#define TRACE_MEM(format, args...) {}
|
||||
#define TRACE_DBG(format, args...) {}
|
||||
#define TRACE_MGMT_DBG(format, args...) {}
|
||||
#define TRACE_ENTRY() {}
|
||||
#define TRACE_EXIT() {}
|
||||
#define TRACE_EXIT_RES(res) {}
|
||||
#define TRACE_EXIT_HRES(res) {}
|
||||
|
||||
#define PRINT_INFO_PR(format, args...) \
|
||||
do { \
|
||||
PRINT(INFO_FLAG, "%s: " format, LOG_PREFIX, args); \
|
||||
} while(0)
|
||||
|
||||
#define PRINT_ERROR_PR(format, args...) \
|
||||
do { \
|
||||
PRINT(ERROR_FLAG, "%s: ***ERROR*** " \
|
||||
format, LOG_PREFIX, args); \
|
||||
} while(0)
|
||||
|
||||
#define PRINT_INFO(format, args...) \
|
||||
do { \
|
||||
PRINT(INFO_FLAG, format, args); \
|
||||
} while(0)
|
||||
|
||||
#define PRINT_ERROR(format, args...) \
|
||||
do { \
|
||||
PRINT(ERROR_FLAG, "***ERROR*** " \
|
||||
format, args); \
|
||||
} while(0)
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* __SCST_DEBUG_H */
|
||||
@@ -0,0 +1,77 @@
|
||||
--- linux-2.6.12-rc6/drivers/scsi/scsi_lib.c_scst 2005-06-15 16:15:01.000000000 +0400
|
||||
+++ linux-2.6.12-rc6/drivers/scsi/scsi_lib.c 2005-06-23 20:28:20.802971272 +0400
|
||||
@@ -235,6 +235,61 @@ void scsi_do_req(struct scsi_request *sr
|
||||
}
|
||||
EXPORT_SYMBOL(scsi_do_req);
|
||||
|
||||
+/*
|
||||
+ * Function: scsi_do_req_fifo
|
||||
+ *
|
||||
+ * Purpose: Queue a SCSI request
|
||||
+ *
|
||||
+ * Arguments: sreq - command descriptor.
|
||||
+ * cmnd - actual SCSI command to be performed.
|
||||
+ * buffer - data buffer.
|
||||
+ * bufflen - size of data buffer.
|
||||
+ * done - completion function to be run.
|
||||
+ * timeout - how long to let it run before timeout.
|
||||
+ * retries - number of retries we allow.
|
||||
+ *
|
||||
+ * Lock status: No locks held upon entry.
|
||||
+ *
|
||||
+ * Returns: Nothing.
|
||||
+ *
|
||||
+ * Notes: This function is used for queueing requests in FIFO order.
|
||||
+ * It is a copy of scsi_do_req() with only changed the last
|
||||
+ * parameter of scsi_insert_special_req(). Not sure, if it
|
||||
+ * breaks quiesce, but, anyway, I don't see another way to
|
||||
+ * queue requests in FIFO order, which is necessary for SCST.
|
||||
+ */
|
||||
+void scsi_do_req_fifo(struct scsi_request *sreq, const void *cmnd,
|
||||
+ void *buffer, unsigned bufflen,
|
||||
+ void (*done)(struct scsi_cmnd *),
|
||||
+ int timeout, int retries)
|
||||
+{
|
||||
+ /*
|
||||
+ * If the upper level driver is reusing these things, then
|
||||
+ * we should release the low-level block now. Another one will
|
||||
+ * be allocated later when this request is getting queued.
|
||||
+ */
|
||||
+ __scsi_release_request(sreq);
|
||||
+
|
||||
+ /*
|
||||
+ * Our own function scsi_done (which marks the host as not busy,
|
||||
+ * disables the timeout counter, etc) will be called by us or by the
|
||||
+ * scsi_hosts[host].queuecommand() function needs to also call
|
||||
+ * the completion function for the high level driver.
|
||||
+ */
|
||||
+ memcpy(sreq->sr_cmnd, cmnd, sizeof(sreq->sr_cmnd));
|
||||
+ sreq->sr_bufflen = bufflen;
|
||||
+ sreq->sr_buffer = buffer;
|
||||
+ sreq->sr_allowed = retries;
|
||||
+ sreq->sr_done = done;
|
||||
+ sreq->sr_timeout_per_command = timeout;
|
||||
+
|
||||
+ if (sreq->sr_cmd_len == 0)
|
||||
+ sreq->sr_cmd_len = COMMAND_SIZE(sreq->sr_cmnd[0]);
|
||||
+
|
||||
+ scsi_insert_special_req(sreq, 0);
|
||||
+}
|
||||
+EXPORT_SYMBOL(scsi_do_req_fifo);
|
||||
+
|
||||
static void scsi_wait_done(struct scsi_cmnd *cmd)
|
||||
{
|
||||
struct request *req = cmd->request;
|
||||
--- linux-2.6.12-rc6/include/scsi/scsi_request.h_scst 2005-06-23 20:08:59.475519880 +0400
|
||||
+++ linux-2.6.12-rc6/include/scsi/scsi_request.h 2005-06-23 20:08:26.881474928 +0400
|
||||
@@ -54,6 +54,10 @@ extern void scsi_do_req(struct scsi_requ
|
||||
void *buffer, unsigned bufflen,
|
||||
void (*done) (struct scsi_cmnd *),
|
||||
int timeout, int retries);
|
||||
+extern void scsi_do_req_fifo(struct scsi_request *, const void *cmnd,
|
||||
+ void *buffer, unsigned bufflen,
|
||||
+ void (*done) (struct scsi_cmnd *),
|
||||
+ int timeout, int retries);
|
||||
|
||||
struct scsi_mode_data {
|
||||
__u32 length;
|
||||
@@ -0,0 +1,75 @@
|
||||
--- linux-2.6.15.6/drivers/scsi/scsi_lib.c_scst 2006-03-05 22:07:54.000000000 +0300
|
||||
+++ linux-2.6.15.6/drivers/scsi/scsi_lib.c 2006-03-27 17:56:59.000000000 +0400
|
||||
@@ -254,6 +254,61 @@
|
||||
}
|
||||
EXPORT_SYMBOL(scsi_do_req);
|
||||
|
||||
+/*
|
||||
+ * Function: scsi_do_req_fifo
|
||||
+ *
|
||||
+ * Purpose: Queue a SCSI request
|
||||
+ *
|
||||
+ * Arguments: sreq - command descriptor.
|
||||
+ * cmnd - actual SCSI command to be performed.
|
||||
+ * buffer - data buffer.
|
||||
+ * bufflen - size of data buffer.
|
||||
+ * done - completion function to be run.
|
||||
+ * timeout - how long to let it run before timeout.
|
||||
+ * retries - number of retries we allow.
|
||||
+ *
|
||||
+ * Lock status: No locks held upon entry.
|
||||
+ *
|
||||
+ * Returns: Nothing.
|
||||
+ *
|
||||
+ * Notes: This function is used for queueing requests in FIFO order.
|
||||
+ * It is a copy of scsi_do_req() with only changed the last
|
||||
+ * parameter of scsi_insert_special_req(). Not sure, if it
|
||||
+ * breaks quiesce, but, anyway, I don't see another way to
|
||||
+ * queue requests in FIFO order, which is necessary for SCST.
|
||||
+ */
|
||||
+void scsi_do_req_fifo(struct scsi_request *sreq, const void *cmnd,
|
||||
+ void *buffer, unsigned bufflen,
|
||||
+ void (*done)(struct scsi_cmnd *),
|
||||
+ int timeout, int retries)
|
||||
+{
|
||||
+ /*
|
||||
+ * If the upper level driver is reusing these things, then
|
||||
+ * we should release the low-level block now. Another one will
|
||||
+ * be allocated later when this request is getting queued.
|
||||
+ */
|
||||
+ __scsi_release_request(sreq);
|
||||
+
|
||||
+ /*
|
||||
+ * Our own function scsi_done (which marks the host as not busy,
|
||||
+ * disables the timeout counter, etc) will be called by us or by the
|
||||
+ * scsi_hosts[host].queuecommand() function needs to also call
|
||||
+ * the completion function for the high level driver.
|
||||
+ */
|
||||
+ memcpy(sreq->sr_cmnd, cmnd, sizeof(sreq->sr_cmnd));
|
||||
+ sreq->sr_bufflen = bufflen;
|
||||
+ sreq->sr_buffer = buffer;
|
||||
+ sreq->sr_allowed = retries;
|
||||
+ sreq->sr_done = done;
|
||||
+ sreq->sr_timeout_per_command = timeout;
|
||||
+
|
||||
+ if (sreq->sr_cmd_len == 0)
|
||||
+ sreq->sr_cmd_len = COMMAND_SIZE(sreq->sr_cmnd[0]);
|
||||
+
|
||||
+ scsi_insert_special_req(sreq, 0);
|
||||
+}
|
||||
+EXPORT_SYMBOL(scsi_do_req_fifo);
|
||||
+
|
||||
/**
|
||||
* scsi_execute - insert request and wait for the result
|
||||
* @sdev: scsi device
|
||||
--- linux-2.6.15.6/include/scsi/scsi_request.h_scst 2006-03-27 17:54:21.000000000 +0400
|
||||
+++ linux-2.6.15.6/include/scsi/scsi_request.h 2006-03-27 17:56:00.000000000 +0400
|
||||
@@ -51,4 +51,8 @@
|
||||
void *buffer, unsigned bufflen,
|
||||
void (*done) (struct scsi_cmnd *),
|
||||
int timeout, int retries);
|
||||
+extern void scsi_do_req_fifo(struct scsi_request *, const void *cmnd,
|
||||
+ void *buffer, unsigned bufflen,
|
||||
+ void (*done) (struct scsi_cmnd *),
|
||||
+ int timeout, int retries);
|
||||
#endif /* _SCSI_SCSI_REQUEST_H */
|
||||
@@ -0,0 +1,77 @@
|
||||
--- linux-2.6.16/drivers/scsi/scsi_lib.c.orig 2006-03-20 00:53:29.000000000 -0500
|
||||
+++ linux-2.6.16/drivers/scsi/scsi_lib.c 2006-04-07 10:04:42.077637750 -0400
|
||||
@@ -227,6 +227,63 @@ void scsi_do_req(struct scsi_request *sr
|
||||
}
|
||||
EXPORT_SYMBOL(scsi_do_req);
|
||||
|
||||
+/*
|
||||
+ * Function: scsi_do_req_fifo
|
||||
+ *
|
||||
+ * Purpose: Queue a SCSI request
|
||||
+ *
|
||||
+ * Arguments: sreq - command descriptor.
|
||||
+ * cmnd - actual SCSI command to be performed.
|
||||
+ * buffer - data buffer.
|
||||
+ * bufflen - size of data buffer.
|
||||
+ * done - completion function to be run.
|
||||
+ * timeout - how long to let it run before timeout.
|
||||
+ * retries - number of retries we allow.
|
||||
+ *
|
||||
+ * Lock status: No locks held upon entry.
|
||||
+ *
|
||||
+ * Returns: Nothing.
|
||||
+ *
|
||||
+ * Notes: This function is used for queueing requests in FIFO order.
|
||||
+ * It is a copy of scsi_do_req() with only changed the last
|
||||
+ * parameter of scsi_insert_special_req(). Not sure, if it
|
||||
+ * breaks quiesce, but, anyway, I don't see another way to
|
||||
+ * queue requests in FIFO order, which is necessary for SCST.
|
||||
+ */
|
||||
+void scsi_do_req_fifo(struct scsi_request *sreq, const void *cmnd,
|
||||
+ void *buffer, unsigned bufflen,
|
||||
+ void (*done)(struct scsi_cmnd *),
|
||||
+ int timeout, int retries)
|
||||
+{
|
||||
+ /*
|
||||
+ * If the upper level driver is reusing these things, then
|
||||
+ * we should release the low-level block now. Another one will
|
||||
+ * be allocated later when this request is getting queued.
|
||||
+ */
|
||||
+ __scsi_release_request(sreq);
|
||||
+
|
||||
+ /*
|
||||
+ * Our own function scsi_done (which marks the host as not busy,
|
||||
+ * disables the timeout counter, etc) will be called by us or by the
|
||||
+ * scsi_hosts[host].queuecommand() function needs to also call
|
||||
+ * the completion function for the high level driver.
|
||||
+ */
|
||||
+ memcpy(sreq->sr_cmnd, cmnd, sizeof(sreq->sr_cmnd));
|
||||
+ sreq->sr_bufflen = bufflen;
|
||||
+ sreq->sr_buffer = buffer;
|
||||
+ sreq->sr_allowed = retries;
|
||||
+ sreq->sr_done = done;
|
||||
+ sreq->sr_timeout_per_command = timeout;
|
||||
+
|
||||
+ if (sreq->sr_cmd_len == 0)
|
||||
+ sreq->sr_cmd_len = COMMAND_SIZE(sreq->sr_cmnd[0]);
|
||||
+
|
||||
+ sreq->sr_request->flags &= ~REQ_DONTPREP;
|
||||
+ blk_insert_request(sreq->sr_device->request_queue, sreq->sr_request,
|
||||
+ 1, sreq);
|
||||
+}
|
||||
+EXPORT_SYMBOL(scsi_do_req_fifo);
|
||||
+
|
||||
/**
|
||||
* scsi_execute - insert request and wait for the result
|
||||
* @sdev: scsi device
|
||||
--- linux-2.6.16/include/scsi/scsi_request.h.orig 2006-04-07 10:06:02.506664250 -0400
|
||||
+++ linux-2.6.16/include/scsi/scsi_request.h 2006-04-07 10:06:19.039697500 -0400
|
||||
@@ -51,4 +51,8 @@ extern void scsi_do_req(struct scsi_requ
|
||||
void *buffer, unsigned bufflen,
|
||||
void (*done) (struct scsi_cmnd *),
|
||||
int timeout, int retries);
|
||||
+extern void scsi_do_req_fifo(struct scsi_request *, const void *cmnd,
|
||||
+ void *buffer, unsigned bufflen,
|
||||
+ void (*done) (struct scsi_cmnd *),
|
||||
+ int timeout, int retries);
|
||||
#endif /* _SCSI_SCSI_REQUEST_H */
|
||||
@@ -0,0 +1,11 @@
|
||||
--- Kconfig 2004-08-18 13:14:50.328907320 +0400
|
||||
+++ Kconfig.scsi 2004-08-13 18:01:13.000000000 +0400
|
||||
@@ -1261,6 +1261,8 @@ config SCSI_QLOGICPTI
|
||||
|
||||
source "drivers/scsi/qla2xxx/Kconfig"
|
||||
|
||||
+source "drivers/scsi/scsi_tgt/Kconfig"
|
||||
+
|
||||
config SCSI_SEAGATE
|
||||
tristate "Seagate ST-02 and Future Domain TMC-8xx SCSI support"
|
||||
depends on X86 && ISA && SCSI && BROKEN
|
||||
@@ -0,0 +1,79 @@
|
||||
config SCSI_TARGET
|
||||
tristate "SCSI target middle level support"
|
||||
default SCSI
|
||||
depends on SCSI && PROC_FS
|
||||
---help---
|
||||
SCSI TARGET is designed to provide unified, consistent interface
|
||||
between SCSI target drivers and Linux kernel and simplify target
|
||||
drivers development as much as possible.
|
||||
|
||||
config SCSI_TARGET_DISK
|
||||
tristate "SCSI target disk support"
|
||||
default SCSI_TARGET
|
||||
depends on SCSI && PROC_FS && SCSI_TARGET
|
||||
---help---
|
||||
SCSI TARGET handler for disk device.
|
||||
|
||||
config SCSI_TARGET_TAPE
|
||||
tristate "SCSI target tape support"
|
||||
default SCSI_TARGET
|
||||
depends on SCSI && PROC_FS && SCSI_TARGET
|
||||
---help---
|
||||
SCSI TARGET handler for tape device.
|
||||
|
||||
config SCSI_TARGET_CDROM
|
||||
tristate "SCSI target cdrom support"
|
||||
default SCSI_TARGET
|
||||
depends on SCSI && PROC_FS && SCSI_TARGET
|
||||
---help---
|
||||
SCSI TARGET handler for cdrom device.
|
||||
|
||||
config SCSI_TARGET_MODISK
|
||||
tristate "SCSI target MO disk support"
|
||||
default SCSI_TARGET
|
||||
depends on SCSI && PROC_FS && SCSI_TARGET
|
||||
---help---
|
||||
SCSI TARGET handler for MO disk device.
|
||||
|
||||
config SCSI_TARGET_CHANGER
|
||||
tristate "SCSI target changer support"
|
||||
default SCSI_TARGET
|
||||
depends on SCSI && PROC_FS && SCSI_TARGET
|
||||
---help---
|
||||
SCSI TARGET handler for changer device.
|
||||
|
||||
config SCSI_TARGET_PROCESSOR
|
||||
tristate "SCSI target processor support"
|
||||
default SCSI_TARGET
|
||||
depends on SCSI && PROC_FS && SCSI_TARGET
|
||||
---help---
|
||||
SCSI TARGET handler for processor device.
|
||||
|
||||
config SCSI_TARGET_RAID
|
||||
tristate "SCSI target storage array controller (raid) support"
|
||||
default SCSI_TARGET
|
||||
depends on SCSI && PROC_FS && SCSI_TARGET
|
||||
---help---
|
||||
SCSI TARGET handler for raid storage array controller (raid) device.
|
||||
|
||||
config SCSI_TARGET_FILEIO
|
||||
tristate "SCSI target virtual disk and/or cdrom support"
|
||||
default SCSI_TARGET
|
||||
depends on SCSI && PROC_FS && SCSI_TARGET
|
||||
---help---
|
||||
SCSI TARGET handler for virtual disk and/or cdrom device.
|
||||
|
||||
config SCSI_TARGET_EXTRACHECKS
|
||||
bool "Extrachecks support"
|
||||
---help---
|
||||
SCSI TARGET extrachecks.
|
||||
|
||||
config SCSI_TARGET_TRACING
|
||||
bool "Tracing support"
|
||||
---help---
|
||||
SCSI TARGET tracing.
|
||||
|
||||
config SCSI_TARGET_DEBUG
|
||||
bool "Debug support"
|
||||
---help---
|
||||
SCSI TARGET debugging.
|
||||
@@ -0,0 +1,10 @@
|
||||
--- Makefile 2004-08-18 13:20:55.434402848 +0400
|
||||
+++ Makefile.scsi 2004-08-13 18:01:13.000000000 +0400
|
||||
@@ -81,6 +81,7 @@ obj-$(CONFIG_SCSI_QLOGIC_ISP) += qlogici
|
||||
obj-$(CONFIG_SCSI_QLOGIC_FC) += qlogicfc.o
|
||||
obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o
|
||||
obj-$(CONFIG_SCSI_QLA2XXX) += qla2xxx/
|
||||
+obj-$(CONFIG_SCSI_TARGET) += scsi_tgt/
|
||||
obj-$(CONFIG_SCSI_PAS16) += pas16.o
|
||||
obj-$(CONFIG_SCSI_SEAGATE) += seagate.o
|
||||
obj-$(CONFIG_SCSI_FD_8xx) += seagate.o
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
EXTRA_CFLAGS += -Iinclude/scsi
|
||||
|
||||
scsi_tgt-y += scst.o
|
||||
scsi_tgt-y += scst_targ.o
|
||||
scsi_tgt-y += scst_lib.o
|
||||
scsi_tgt-y += scst_proc.o
|
||||
|
||||
obj-$(CONFIG_SCSI_TARGET) += scsi_tgt.o
|
||||
obj-$(CONFIG_SCSI_TARGET_DISK) += scst_disk.o
|
||||
obj-$(CONFIG_SCSI_TARGET_TAPE) += scst_tape.o
|
||||
obj-$(CONFIG_SCSI_TARGET_CDROM) += scst_cdrom.o
|
||||
obj-$(CONFIG_SCSI_TARGET_MODISK) += scst_modisk.o
|
||||
obj-$(CONFIG_SCSI_TARGET_CHANGER) += scst_changer.o
|
||||
obj-$(CONFIG_SCSI_TARGET_RAID) += scst_raid.o
|
||||
obj-$(CONFIG_SCSI_TARGET_PROCESSOR) += scst_processor.o
|
||||
obj-$(CONFIG_SCSI_TARGET_FILEIO) += scst_fileio.o
|
||||
@@ -0,0 +1,107 @@
|
||||
#
|
||||
# SCSI target mid-level makefile
|
||||
#
|
||||
# Copyright (C) 2004 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
# and Leonid Stoljar
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#
|
||||
# Main targets:
|
||||
# all (the default) : make all
|
||||
# clean : clean files
|
||||
# extraclean : clean + clean dependencies
|
||||
# install : install
|
||||
# uninstall : uninstall
|
||||
#
|
||||
# Notes :
|
||||
# - install and uninstall must be made as root
|
||||
#
|
||||
|
||||
DEV_HANDLERS_DIR = dev_handlers
|
||||
|
||||
ifneq ($(KERNELRELEASE),)
|
||||
obj-m := scsi_tgt.o
|
||||
scsi_tgt-objs := scst.o scst_targ.o scst_lib.o scst_mem.o scst_proc.o
|
||||
|
||||
obj-$(BUILD_DEV) += $(DEV_HANDLERS_DIR)/
|
||||
|
||||
else
|
||||
ifeq ($(KVER),)
|
||||
ifeq ($(KDIR),)
|
||||
KDIR := /lib/modules/$(shell uname -r)/build
|
||||
endif
|
||||
else
|
||||
KDIR := /lib/modules/$(KVER)/build
|
||||
endif
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_DEV=m
|
||||
|
||||
scst:
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_DEV=n
|
||||
|
||||
install: all
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) BUILD_DEV=m \
|
||||
modules_install
|
||||
install -d $(INSTALL_DIR_H)
|
||||
install -m 644 ../include/scsi_tgt.h $(INSTALL_DIR_H)
|
||||
install -m 644 ../include/scst_debug.h $(INSTALL_DIR_H)
|
||||
install -m 644 ../include/scst_debug.c $(INSTALL_DIR_H)
|
||||
-depmod -a
|
||||
|
||||
uninstall:
|
||||
cd $(DEV_HANDLERS_DIR) && $(MAKE) $@
|
||||
rm -f $(INSTALL_DIR)/scsi_tgt.ko
|
||||
-rmdir $(INSTALL_DIR) 2>/dev/null
|
||||
-/sbin/depmod -a
|
||||
rm -rf $(INSTALL_DIR_H)
|
||||
endif
|
||||
|
||||
INSTALL_DIR := /lib/modules/$(shell uname -r)/extra
|
||||
INSTALL_DIR_H := /usr/local/include/scst
|
||||
|
||||
INC_DIR := $(SUBDIRS)/../include
|
||||
|
||||
EXTRA_CFLAGS += -I$(INC_DIR)
|
||||
|
||||
#EXTRA_CFLAGS += -DSTRICT_SERIALIZING
|
||||
|
||||
EXTRA_CFLAGS += -DEXTRACHECKS
|
||||
|
||||
#EXTRA_CFLAGS += -fno-inline
|
||||
|
||||
#EXTRA_CFLAGS += -DTRACING
|
||||
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
#EXTRA_CFLAGS += -DDEBUG_TM -DTM_DBG_GO_OFFLINE=0
|
||||
#EXTRA_CFLAGS += -DDEBUG_RETRY
|
||||
#EXTRA_CFLAGS += -DDEBUG_OOM
|
||||
|
||||
# If defined, makes SCST zero allocated data buffers.
|
||||
# Undefining it considerably improves performance and eases CPU load,
|
||||
# but could create a security hole (information leakage), so
|
||||
# enable it if you have strict security requirements.
|
||||
#EXTRA_CFLAGS += -DSCST_STRICT_SECURITY
|
||||
|
||||
# If defined, allows SCST to use HIGHMEM. It's unclear, if it brings
|
||||
# something valuable, except performance hit in some cases,
|
||||
# so let it be off. Untested.
|
||||
#EXTRA_CFLAGS += -DSCST_HIGHMEM
|
||||
|
||||
clean:
|
||||
rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend Modules.symvers
|
||||
rm -rf .tmp_versions
|
||||
cd $(DEV_HANDLERS_DIR) && $(MAKE) $@
|
||||
|
||||
extraclean: clean
|
||||
|
||||
.PHONY: all install uninstall clean extraclean
|
||||
@@ -0,0 +1,64 @@
|
||||
#
|
||||
# SCSI target mid-level dev handler's makefile
|
||||
#
|
||||
# Copyright (C) 2004 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
# and Leonid Stoljar
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#
|
||||
# Main targets:
|
||||
# all (the default) : make all
|
||||
# clean : clean files
|
||||
# extraclean : clean + clean dependencies
|
||||
# install : install
|
||||
# uninstall : uninstall
|
||||
#
|
||||
# Notes :
|
||||
# - install and uninstall must be made as root
|
||||
#
|
||||
|
||||
ifneq ($(KERNELRELEASE),)
|
||||
obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \
|
||||
scst_fileio.o scst_raid.o scst_processor.o
|
||||
else
|
||||
ifeq ($(KDIR),)
|
||||
KDIR := /lib/modules/$(shell uname -r)/build
|
||||
endif
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)
|
||||
|
||||
install: all
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) \
|
||||
modules_install
|
||||
-depmod -a
|
||||
|
||||
uninstall:
|
||||
rm -f $(INSTALL_DIR)/scst_*.ko
|
||||
endif
|
||||
|
||||
INSTALL_DIR := /lib/modules/$(shell uname -r)/extra
|
||||
|
||||
EXTRA_CFLAGS += -I$(SUBDIRS) -I$(SUBDIRS)/../include
|
||||
#EXTRA_CFLAGS += -I$(SUBDIRS) -I$(SUBDIRS)/../../include -I$(SUBDIRS)/..
|
||||
|
||||
#EXTRA_CFLAGS += -DTRACING
|
||||
|
||||
EXTRA_CFLAGS += -DDEBUG
|
||||
|
||||
clean:
|
||||
rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend Modules.symvers
|
||||
rm -rf .tmp_versions
|
||||
|
||||
extraclean: clean
|
||||
|
||||
.PHONY: all install uninstall clean extraclean
|
||||
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* scst_cdrom.c
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* SCSI CDROM (type 5) dev handler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/cdrom.h>
|
||||
|
||||
#define LOG_PREFIX "dev_cdrom"
|
||||
#include "scst_debug.h"
|
||||
#include "scsi_tgt.h"
|
||||
#include "scst_dev_handler.h"
|
||||
|
||||
#include "scst_debug.c"
|
||||
|
||||
#define CDROM_NAME "dev_cdrom"
|
||||
|
||||
#define CDROM_TYPE { \
|
||||
name: CDROM_NAME, \
|
||||
type: TYPE_ROM, \
|
||||
parse_atomic: 1, \
|
||||
dev_done_atomic: 1, \
|
||||
attach: cdrom_attach, \
|
||||
detach: cdrom_detach, \
|
||||
parse: cdrom_parse, \
|
||||
dev_done: cdrom_done, \
|
||||
}
|
||||
|
||||
#define CDROM_RETRIES 2
|
||||
#define CDROM_SMALL_TIMEOUT (3 * HZ)
|
||||
#define CDROM_REG_TIMEOUT (900 * HZ)
|
||||
#define CDROM_LONG_TIMEOUT (14000 * HZ)
|
||||
#define READ_CAP_LEN 8
|
||||
|
||||
/* Flags */
|
||||
#define BYTCHK 0x02
|
||||
|
||||
struct cdrom_params
|
||||
{
|
||||
int sector_size;
|
||||
};
|
||||
|
||||
int cdrom_attach(struct scst_device *);
|
||||
void cdrom_detach(struct scst_device *);
|
||||
int cdrom_parse(struct scst_cmd *, const struct scst_info_cdb *);
|
||||
int cdrom_done(struct scst_cmd *);
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
unsigned long trace_flag = SCST_DEFAULT_DEV_LOG_FLAGS;
|
||||
#endif
|
||||
|
||||
static struct scst_dev_type cdrom_devtype = CDROM_TYPE;
|
||||
|
||||
/**************************************************************
|
||||
* Function: cdrom_attach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : 1 if attached, error code otherwise
|
||||
*
|
||||
* Description:
|
||||
*************************************************************/
|
||||
int cdrom_attach(struct scst_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
uint8_t cmd[10];
|
||||
const int buffer_size = 512;
|
||||
uint8_t *buffer = NULL;
|
||||
int retries;
|
||||
unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];
|
||||
enum dma_data_direction data_dir;
|
||||
unsigned char *sbuff;
|
||||
struct cdrom_params *cdrom;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (dev->scsi_dev == NULL ||
|
||||
dev->scsi_dev->type != dev->handler->type) {
|
||||
PRINT_ERROR_PR("%s", "SCSI device not define or illegal type");
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cdrom = kzalloc(sizeof(*cdrom), GFP_KERNEL);
|
||||
TRACE_MEM("kzalloc(GFP_KERNEL) for struct cdrom_params (%zu): %p",
|
||||
sizeof(*cdrom), cdrom);
|
||||
if (cdrom == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "%s",
|
||||
"Unable to allocate struct cdrom_params");
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer = kzalloc(buffer_size, GFP_KERNEL);
|
||||
TRACE_MEM("kzalloc(GFP_KERNEL) for %d: %p", buffer_size, buffer);
|
||||
if (!buffer) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "%s", "Memory allocation failure");
|
||||
res = -ENOMEM;
|
||||
goto out_free_cdrom;
|
||||
}
|
||||
|
||||
/* Clear any existing UA's and get cdrom capacity (cdrom block size) */
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
cmd[0] = READ_CAPACITY;
|
||||
cmd[1] = (dev->scsi_dev->scsi_level <= SCSI_2) ?
|
||||
((dev->scsi_dev->lun << 5) & 0xe0) : 0;
|
||||
retries = SCST_DEV_UA_RETRIES;
|
||||
while (1) {
|
||||
memset(buffer, 0, buffer_size);
|
||||
data_dir = SCST_DATA_READ;
|
||||
sbuff = sense_buffer;
|
||||
|
||||
TRACE_DBG("%s", "Doing READ_CAPACITY");
|
||||
res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
|
||||
buffer_size, sbuff,
|
||||
CDROM_REG_TIMEOUT, CDROM_RETRIES, 0);
|
||||
|
||||
TRACE_DBG("READ_CAPACITY done: %x", res);
|
||||
|
||||
if (!res || (sbuff[12] != 0x28 && sbuff[12] != 0x29))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!--retries) {
|
||||
PRINT_ERROR_PR("UA not clear after %d retries",
|
||||
SCST_DEV_UA_RETRIES);
|
||||
cdrom->sector_size = 2048;
|
||||
// res = -ENODEV;
|
||||
goto out_free_buf;
|
||||
}
|
||||
}
|
||||
if (res == 0) {
|
||||
cdrom->sector_size = ((buffer[4] << 24) | (buffer[5] << 16) |
|
||||
(buffer[6] << 8) | (buffer[7] << 0));
|
||||
TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)",
|
||||
cdrom->sector_size, dev->scsi_dev->scsi_level, SCSI_2);
|
||||
if (!cdrom->sector_size) {
|
||||
cdrom->sector_size = 2048;
|
||||
}
|
||||
} else {
|
||||
TRACE_BUFFER("Sense set", sbuff, SCSI_SENSE_BUFFERSIZE);
|
||||
cdrom->sector_size = 2048;
|
||||
// res = -ENODEV;
|
||||
goto out_free_buf;
|
||||
}
|
||||
|
||||
out_free_buf:
|
||||
TRACE_MEM("kfree for buffer: %p", buffer);
|
||||
kfree(buffer);
|
||||
|
||||
out_free_cdrom:
|
||||
if (res == 0)
|
||||
dev->tgt_dev_specific = cdrom;
|
||||
else {
|
||||
TRACE_MEM("kfree for cdrom: %p", cdrom);
|
||||
kfree(cdrom);
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return res;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Function: cdrom_detach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : None
|
||||
*
|
||||
* Description: Called to detach this device type driver
|
||||
************************************************************/
|
||||
void cdrom_detach(struct scst_device *dev)
|
||||
{
|
||||
struct cdrom_params *cdrom =
|
||||
(struct cdrom_params *)dev->tgt_dev_specific;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_MEM("kfree for cdrom: %p", cdrom);
|
||||
kfree(cdrom);
|
||||
dev->tgt_dev_specific = NULL;
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: cdrom_parse
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : The state of the command
|
||||
*
|
||||
* Description: This does the parsing of the command
|
||||
*
|
||||
* Note: Not all states are allowed on return
|
||||
********************************************************************/
|
||||
int cdrom_parse(struct scst_cmd *cmd, const struct scst_info_cdb *info_cdb)
|
||||
{
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
struct cdrom_params *cdrom;
|
||||
int fixed;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->data_direction and cmd->bufflen
|
||||
* based on info_cdb, therefore change them only if necessary
|
||||
*/
|
||||
|
||||
if (info_cdb->flags & SCST_SMALL_TIMEOUT) {
|
||||
cmd->timeout = CDROM_SMALL_TIMEOUT;
|
||||
} else if (info_cdb->flags & SCST_LONG_TIMEOUT) {
|
||||
cmd->timeout = CDROM_LONG_TIMEOUT;
|
||||
} else {
|
||||
cmd->timeout = CDROM_REG_TIMEOUT;
|
||||
}
|
||||
|
||||
TRACE_DBG("op_name <%s> direct %d flags %d transfer_len %d lun %d(%d)",
|
||||
info_cdb->op_name,
|
||||
info_cdb->direction,
|
||||
info_cdb->flags,
|
||||
info_cdb->transfer_len, cmd->lun, (cmd->cdb[1] >> 5) & 7);
|
||||
|
||||
cmd->cdb[1] &= 0x1f;
|
||||
|
||||
fixed = info_cdb->flags & SCST_TRANSFER_LEN_TYPE_FIXED;
|
||||
switch (cmd->cdb[0]) {
|
||||
case READ_CAPACITY:
|
||||
cmd->bufflen = READ_CAP_LEN;
|
||||
cmd->data_direction = SCST_DATA_READ;
|
||||
break;
|
||||
case GPCMD_SET_STREAMING:
|
||||
cmd->bufflen = (((*(cmd->cdb + 9)) & 0xff) << 8) +
|
||||
((*(cmd->cdb + 10)) & 0xff);
|
||||
cmd->bufflen &= 0xffff;
|
||||
break;
|
||||
case GPCMD_READ_CD:
|
||||
cmd->bufflen = cmd->bufflen >> 8;
|
||||
break;
|
||||
#if 0
|
||||
case SYNCHRONIZE_CACHE:
|
||||
cmd->underflow = 0;
|
||||
break;
|
||||
#endif
|
||||
case VERIFY_6:
|
||||
case VERIFY:
|
||||
case VERIFY_12:
|
||||
case VERIFY_16:
|
||||
if ((cmd->cdb[1] & BYTCHK) == 0) {
|
||||
cmd->bufflen = 0;
|
||||
cmd->data_direction = SCST_DATA_NONE;
|
||||
fixed = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
|
||||
if (fixed) {
|
||||
/*
|
||||
* No need for locks here, since *_detach() can not be
|
||||
* called, when there are existing commands.
|
||||
*/
|
||||
cdrom = (struct cdrom_params *)cmd->dev->tgt_dev_specific;
|
||||
cmd->bufflen = info_cdb->transfer_len * cdrom->sector_size;
|
||||
}
|
||||
|
||||
TRACE_DBG("res %d bufflen %zd direct %d",
|
||||
res, cmd->bufflen, cmd->data_direction);
|
||||
|
||||
TRACE_EXIT();
|
||||
return res;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: cdrom_done
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns :
|
||||
*
|
||||
* Description: This is the completion routine for the command,
|
||||
* it is used to extract any necessary information
|
||||
* about a command.
|
||||
********************************************************************/
|
||||
int cdrom_done(struct scst_cmd *cmd)
|
||||
{
|
||||
int opcode = cmd->cdb[0];
|
||||
int masked_status = cmd->masked_status;
|
||||
struct cdrom_params *cdrom;
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->sg == NULL))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len
|
||||
* based on cmd->masked_status and cmd->data_direction, therefore change
|
||||
* them only if necessary
|
||||
*/
|
||||
|
||||
if ((masked_status == GOOD) || (masked_status == CONDITION_GOOD)) {
|
||||
switch (opcode) {
|
||||
case READ_CAPACITY:
|
||||
{
|
||||
/* Always keep track of cdrom capacity */
|
||||
int buffer_size;
|
||||
/*
|
||||
* To force the compiler not to optimize it out to keep
|
||||
* cdrom->sector_size access atomic
|
||||
*/
|
||||
volatile int sector_size;
|
||||
uint8_t *buffer;
|
||||
buffer_size = scst_get_buf_first(cmd, &buffer);
|
||||
if (unlikely(buffer_size <= 0)) {
|
||||
PRINT_ERROR_PR("%s: Unable to get the buffer",
|
||||
__FUNCTION__);
|
||||
scst_set_busy(cmd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* No need for locks here, since *_detach() can not be
|
||||
* called, when there are existing commands.
|
||||
*/
|
||||
cdrom =
|
||||
(struct cdrom_params *)cmd->dev->tgt_dev_specific;
|
||||
sector_size =
|
||||
((buffer[4] << 24) | (buffer[5] << 16) |
|
||||
(buffer[6] << 8) | (buffer[7] << 0));
|
||||
if (!sector_size)
|
||||
sector_size = 2048;
|
||||
cdrom->sector_size = sector_size;
|
||||
TRACE_DBG("Sector size is %i", cdrom->sector_size);
|
||||
|
||||
scst_put_buf(cmd, buffer);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_DBG("cmd->tgt_resp_flags=%x, cmd->resp_data_len=%d, "
|
||||
"res=%d", cmd->tgt_resp_flags, cmd->resp_data_len, res);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return res;
|
||||
}
|
||||
|
||||
static int __init cdrom_init(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
cdrom_devtype.module = THIS_MODULE;
|
||||
if (scst_register_dev_driver(&cdrom_devtype) < 0) {
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = scst_dev_handler_build_std_proc(&cdrom_devtype);
|
||||
if (res != 0)
|
||||
goto out_err;
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return res;
|
||||
|
||||
out_err:
|
||||
scst_unregister_dev_driver(&cdrom_devtype);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void __exit cdrom_exit(void)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
scst_dev_handler_destroy_std_proc(&cdrom_devtype);
|
||||
scst_unregister_dev_driver(&cdrom_devtype);
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(cdrom_init);
|
||||
module_exit(cdrom_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* scst_changer.c
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* SCSI medium changer (type 8) dev handler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_PREFIX "dev_changer"
|
||||
#include "scst_debug.h"
|
||||
#include "scsi_tgt.h"
|
||||
#include "scst_dev_handler.h"
|
||||
|
||||
#include "scst_debug.c"
|
||||
|
||||
#define CHANGER_NAME "dev_changer"
|
||||
|
||||
#define CHANGER_TYPE { \
|
||||
name: CHANGER_NAME, \
|
||||
type: TYPE_MEDIUM_CHANGER,\
|
||||
parse_atomic: 1, \
|
||||
/* dev_done_atomic: 1,*/ \
|
||||
attach: changer_attach, \
|
||||
/* detach: changer_detach,*/ \
|
||||
parse: changer_parse, \
|
||||
/* dev_done: changer_done*/ \
|
||||
}
|
||||
|
||||
#define CHANGER_RETRIES 2
|
||||
#define CHANGER_TIMEOUT (3 * HZ)
|
||||
#define CHANGER_LONG_TIMEOUT (14000 * HZ)
|
||||
#define READ_CAP_LEN 8
|
||||
|
||||
int changer_attach(struct scst_device *);
|
||||
void changer_detach(struct scst_device *);
|
||||
int changer_parse(struct scst_cmd *, const struct scst_info_cdb *);
|
||||
int changer_done(struct scst_cmd *);
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
unsigned long trace_flag = SCST_DEFAULT_DEV_LOG_FLAGS;
|
||||
#endif
|
||||
|
||||
static struct scst_dev_type changer_devtype = CHANGER_TYPE;
|
||||
|
||||
/**************************************************************
|
||||
* Function: changer_attach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : 1 if attached, error code otherwise
|
||||
*
|
||||
* Description:
|
||||
*************************************************************/
|
||||
int changer_attach(struct scst_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
int retries;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (dev->scsi_dev == NULL ||
|
||||
dev->scsi_dev->type != dev->handler->type) {
|
||||
PRINT_ERROR_PR("%s", "SCSI device not define or illegal type");
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the device is offline, don't try to read capacity or any
|
||||
* of the other stuff
|
||||
*/
|
||||
if (dev->scsi_dev->sdev_state == SDEV_OFFLINE)
|
||||
{
|
||||
TRACE_DBG("%s", "Device is offline");
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retries = SCST_DEV_UA_RETRIES;
|
||||
do {
|
||||
TRACE_DBG("%s", "Doing TEST_UNIT_READY");
|
||||
res = scsi_test_unit_ready(dev->scsi_dev, CHANGER_TIMEOUT,
|
||||
CHANGER_RETRIES);
|
||||
TRACE_DBG("TEST_UNIT_READY done: %x", res);
|
||||
} while ((--retries > 0) && res);
|
||||
if (res)
|
||||
res = -ENODEV;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_HRES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Function: changer_detach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : None
|
||||
*
|
||||
* Description: Called to detach this device type driver
|
||||
************************************************************/
|
||||
void changer_detach(struct scst_device *dev)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: changer_parse
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : The state of the command
|
||||
*
|
||||
* Description: This does the parsing of the command
|
||||
*
|
||||
* Note: Not all states are allowed on return
|
||||
********************************************************************/
|
||||
int changer_parse(struct scst_cmd *cmd, const struct scst_info_cdb *info_cdb)
|
||||
{
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->data_direction and cmd->bufflen
|
||||
* based on info_cdb, therefore change them only if necessary
|
||||
*/
|
||||
|
||||
if (info_cdb->flags & SCST_LONG_TIMEOUT) {
|
||||
cmd->timeout = CHANGER_LONG_TIMEOUT;
|
||||
} else {
|
||||
cmd->timeout = CHANGER_TIMEOUT;
|
||||
}
|
||||
|
||||
TRACE_DBG("op_name <%s> direct %d flags %d transfer_len %d",
|
||||
info_cdb->op_name,
|
||||
info_cdb->direction, info_cdb->flags, info_cdb->transfer_len);
|
||||
#if 0
|
||||
switch (cmd->cdb[0]) {
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
TRACE_DBG("res %d bufflen %zd direct %d",
|
||||
res, cmd->bufflen, cmd->data_direction);
|
||||
|
||||
TRACE_EXIT();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: changer_done
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns :
|
||||
*
|
||||
* Description: This is the completion routine for the command,
|
||||
* it is used to extract any necessary information
|
||||
* about a command.
|
||||
********************************************************************/
|
||||
int changer_done(struct scst_cmd *cmd)
|
||||
{
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->sg == NULL))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len
|
||||
* based on cmd->masked_status and cmd->data_direction, therefore change
|
||||
* them only if necessary
|
||||
*/
|
||||
|
||||
#if 0
|
||||
switch (cmd->cdb[0]) {
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return res;
|
||||
}
|
||||
|
||||
static int __init changer_init(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
changer_devtype.module = THIS_MODULE;
|
||||
if (scst_register_dev_driver(&changer_devtype) < 0) {
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = scst_dev_handler_build_std_proc(&changer_devtype);
|
||||
if (res != 0)
|
||||
goto out_err;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_err:
|
||||
scst_unregister_dev_driver(&changer_devtype);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void __exit changer_exit(void)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
scst_dev_handler_destroy_std_proc(&changer_devtype);
|
||||
scst_unregister_dev_driver(&changer_devtype);
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(changer_init);
|
||||
module_exit(changer_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,94 @@
|
||||
#include <scsi/scsi_eh.h>
|
||||
|
||||
#define SCST_DEV_UA_RETRIES 5
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
|
||||
#define DEV_HANDLER_LOG_ENTRY_NAME "trace_level"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define SCST_DEFAULT_DEV_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_PID | \
|
||||
TRACE_FUNCTION | TRACE_MGMT | TRACE_MINOR | TRACE_MGMT_DEBUG)
|
||||
#else
|
||||
#define SCST_DEFAULT_DEV_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MINOR)
|
||||
#endif
|
||||
|
||||
static int scst_dev_handler_proc_log_entry_read(char *buffer, char **start,
|
||||
off_t offset, int length, int *eof, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
res = scst_proc_log_entry_read(buffer, start, offset, length, eof,
|
||||
data, trace_flag, NULL);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int scst_dev_handler_proc_log_entry_write(struct file *file,
|
||||
const char *buf, unsigned long length, void *data)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
res = scst_proc_log_entry_write(file, buf, length, data,
|
||||
&trace_flag, SCST_DEFAULT_DEV_LOG_FLAGS, NULL);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
#endif /* defined(DEBUG) || defined(TRACING) */
|
||||
|
||||
static int scst_dev_handler_build_std_proc(struct scst_dev_type *dev_type)
|
||||
{
|
||||
int res = 0;
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
struct proc_dir_entry *p, *root;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
root = scst_proc_get_dev_type_root(dev_type);
|
||||
|
||||
if (root) {
|
||||
/* create the proc file entry for the device */
|
||||
p = create_proc_read_entry(DEV_HANDLER_LOG_ENTRY_NAME,
|
||||
S_IFREG | S_IRUGO | S_IWUSR, root,
|
||||
scst_dev_handler_proc_log_entry_read,
|
||||
(void *)dev_type->name);
|
||||
if (p == NULL) {
|
||||
PRINT_ERROR_PR("Not enough memory to register dev "
|
||||
"handler %s entry %s in /proc",
|
||||
dev_type->name, DEV_HANDLER_LOG_ENTRY_NAME);
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
p->write_proc = scst_dev_handler_proc_log_entry_write;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
static void scst_dev_handler_destroy_std_proc(struct scst_dev_type *dev_type)
|
||||
{
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
struct proc_dir_entry *root;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
root = scst_proc_get_dev_type_root(dev_type);
|
||||
|
||||
if (root) {
|
||||
remove_proc_entry(DEV_HANDLER_LOG_ENTRY_NAME, root);
|
||||
}
|
||||
TRACE_EXIT();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
* scst_disk.c
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* SCSI disk (type 0) dev handler
|
||||
* &
|
||||
* SCSI disk (type 0) "performance" device handler (skip all READ and WRITE
|
||||
* operations).
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define LOG_PREFIX "dev_disk"
|
||||
#include "scst_debug.h"
|
||||
#include "scsi_tgt.h"
|
||||
#include "scst_dev_handler.h"
|
||||
|
||||
#include "scst_debug.c"
|
||||
|
||||
# define DISK_NAME "dev_disk"
|
||||
# define DISK_PERF_NAME "dev_disk_perf"
|
||||
|
||||
#define DISK_TYPE { \
|
||||
name: DISK_NAME, \
|
||||
type: TYPE_DISK, \
|
||||
parse_atomic: 1, \
|
||||
dev_done_atomic: 1, \
|
||||
exec_atomic: 1, \
|
||||
attach: disk_attach, \
|
||||
detach: disk_detach, \
|
||||
parse: disk_parse, \
|
||||
dev_done: disk_done, \
|
||||
}
|
||||
|
||||
#define DISK_PERF_TYPE { \
|
||||
name: DISK_PERF_NAME, \
|
||||
type: TYPE_DISK, \
|
||||
parse_atomic: 1, \
|
||||
dev_done_atomic: 1, \
|
||||
exec_atomic: 1, \
|
||||
attach: disk_attach, \
|
||||
detach: disk_detach, \
|
||||
parse: disk_parse, \
|
||||
dev_done: disk_done, \
|
||||
exec: disk_exec, \
|
||||
}
|
||||
|
||||
#define DISK_RETRIES 2
|
||||
#define DISK_SMALL_TIMEOUT (3 * HZ)
|
||||
#define DISK_REG_TIMEOUT (60 * HZ)
|
||||
#define DISK_LONG_TIMEOUT (3600 * HZ)
|
||||
#define READ_CAP_LEN 8
|
||||
|
||||
/* Flags */
|
||||
#define BYTCHK 0x02
|
||||
|
||||
struct disk_params
|
||||
{
|
||||
int sector_size;
|
||||
};
|
||||
|
||||
int disk_attach(struct scst_device *dev);
|
||||
void disk_detach(struct scst_device *dev);
|
||||
int disk_parse(struct scst_cmd *cmd, const struct scst_info_cdb *info_cmd);
|
||||
int disk_done(struct scst_cmd *cmd);
|
||||
int disk_exec(struct scst_cmd *cmd);
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
unsigned long trace_flag = SCST_DEFAULT_DEV_LOG_FLAGS;
|
||||
#endif
|
||||
|
||||
static struct scst_dev_type disk_devtype = DISK_TYPE;
|
||||
static struct scst_dev_type disk_devtype_perf = DISK_PERF_TYPE;
|
||||
|
||||
static int __init init_scst_disk_driver(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
disk_devtype.module = THIS_MODULE;
|
||||
if (scst_register_dev_driver(&disk_devtype) < 0) {
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = scst_dev_handler_build_std_proc(&disk_devtype);
|
||||
if (res != 0)
|
||||
goto out_unreg1;
|
||||
|
||||
disk_devtype_perf.module = THIS_MODULE;
|
||||
if (scst_register_dev_driver(&disk_devtype_perf) < 0) {
|
||||
res = -ENODEV;
|
||||
goto out_unreg1_err1;
|
||||
}
|
||||
|
||||
res = scst_dev_handler_build_std_proc(&disk_devtype_perf);
|
||||
if (res != 0)
|
||||
goto out_unreg2;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_unreg2:
|
||||
scst_dev_handler_destroy_std_proc(&disk_devtype_perf);
|
||||
|
||||
out_unreg1_err1:
|
||||
scst_dev_handler_destroy_std_proc(&disk_devtype);
|
||||
|
||||
out_unreg1:
|
||||
scst_unregister_dev_driver(&disk_devtype);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void __exit exit_scst_disk_driver(void)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
scst_dev_handler_destroy_std_proc(&disk_devtype_perf);
|
||||
scst_unregister_dev_driver(&disk_devtype_perf);
|
||||
scst_dev_handler_destroy_std_proc(&disk_devtype);
|
||||
scst_unregister_dev_driver(&disk_devtype);
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(init_scst_disk_driver);
|
||||
module_exit(exit_scst_disk_driver);
|
||||
|
||||
/**************************************************************
|
||||
* Function: disk_attach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : 1 if attached, error code otherwise
|
||||
*
|
||||
* Description:
|
||||
*************************************************************/
|
||||
int disk_attach(struct scst_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
uint8_t cmd[10];
|
||||
const int buffer_size = 512;
|
||||
uint8_t *buffer = NULL;
|
||||
int retries;
|
||||
unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];
|
||||
enum dma_data_direction data_dir;
|
||||
unsigned char *sbuff;
|
||||
struct disk_params *disk;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (dev->scsi_dev == NULL ||
|
||||
dev->scsi_dev->type != dev->handler->type) {
|
||||
PRINT_ERROR_PR("%s", "SCSI device not define or illegal type");
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
disk = kzalloc(sizeof(*disk), GFP_KERNEL);
|
||||
TRACE_MEM("kzalloc(GFP_KERNEL) for struct disk_params (%zu): %p",
|
||||
sizeof(*disk), disk);
|
||||
if (disk == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "%s",
|
||||
"Unable to allocate struct disk_params");
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer = kzalloc(buffer_size, GFP_KERNEL);
|
||||
TRACE_MEM("kzalloc(GFP_KERNEL) for %d: %p", buffer_size, buffer);
|
||||
if (!buffer) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "%s", "Memory allocation failure");
|
||||
res = -ENOMEM;
|
||||
goto out_free_disk;
|
||||
}
|
||||
|
||||
/* Clear any existing UA's and get disk capacity (disk block size) */
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
cmd[0] = READ_CAPACITY;
|
||||
cmd[1] = (dev->scsi_dev->scsi_level <= SCSI_2) ?
|
||||
((dev->scsi_dev->lun << 5) & 0xe0) : 0;
|
||||
retries = SCST_DEV_UA_RETRIES;
|
||||
while (1) {
|
||||
memset(buffer, 0, buffer_size);
|
||||
data_dir = SCST_DATA_READ;
|
||||
sbuff = sense_buffer;
|
||||
|
||||
TRACE_DBG("%s", "Doing READ_CAPACITY");
|
||||
res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
|
||||
buffer_size, sbuff,
|
||||
DISK_REG_TIMEOUT, DISK_RETRIES, 0);
|
||||
|
||||
TRACE_DBG("READ_CAPACITY done: %x", res);
|
||||
|
||||
if (!res || (sbuff[12] != 0x28 && sbuff[12] != 0x29))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!--retries) {
|
||||
PRINT_ERROR_PR("UA not clear after %d retries",
|
||||
SCST_DEV_UA_RETRIES);
|
||||
res = -ENODEV;
|
||||
goto out_free_buf;
|
||||
}
|
||||
}
|
||||
if (res == 0) {
|
||||
disk->sector_size = ((buffer[4] << 24) | (buffer[5] << 16) |
|
||||
(buffer[6] << 8) | (buffer[7] << 0));
|
||||
TRACE_DBG("Sector size is %i", disk->sector_size);
|
||||
if (!disk->sector_size) {
|
||||
disk->sector_size = 512;
|
||||
}
|
||||
} else {
|
||||
TRACE_BUFFER("Sense set", sbuff, SCSI_SENSE_BUFFERSIZE);
|
||||
res = -ENODEV;
|
||||
goto out_free_buf;
|
||||
}
|
||||
|
||||
out_free_buf:
|
||||
TRACE_MEM("kfree for buffer: %p", buffer);
|
||||
kfree(buffer);
|
||||
|
||||
out_free_disk:
|
||||
if (res == 0)
|
||||
dev->tgt_dev_specific = disk;
|
||||
else {
|
||||
TRACE_MEM("kfree for disk: %p", disk);
|
||||
kfree(disk);
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Function: disk_detach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : None
|
||||
*
|
||||
* Description: Called to detach this device type driver
|
||||
************************************************************/
|
||||
void disk_detach(struct scst_device *dev)
|
||||
{
|
||||
struct disk_params *disk = (struct disk_params *)dev->tgt_dev_specific;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_MEM("kfree for disk: %p", disk);
|
||||
kfree(disk);
|
||||
dev->tgt_dev_specific = NULL;
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: disk_parse
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : The state of the command
|
||||
*
|
||||
* Description: This does the parsing of the command
|
||||
*
|
||||
* Note: Not all states are allowed on return
|
||||
********************************************************************/
|
||||
int disk_parse(struct scst_cmd *cmd, const struct scst_info_cdb *info_cdb)
|
||||
{
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
struct disk_params *disk;
|
||||
int fixed;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->data_direction and cmd->bufflen
|
||||
* based on info_cdb, therefore change them only if necessary
|
||||
*/
|
||||
|
||||
if (info_cdb->flags & SCST_SMALL_TIMEOUT) {
|
||||
cmd->timeout = DISK_SMALL_TIMEOUT;
|
||||
} else if (info_cdb->flags & SCST_LONG_TIMEOUT) {
|
||||
cmd->timeout = DISK_LONG_TIMEOUT;
|
||||
} else {
|
||||
cmd->timeout = DISK_REG_TIMEOUT;
|
||||
}
|
||||
|
||||
TRACE_DBG("op_name <%s> direct %d flags %d transfer_len %d",
|
||||
info_cdb->op_name,
|
||||
info_cdb->direction, info_cdb->flags, info_cdb->transfer_len);
|
||||
|
||||
fixed = info_cdb->flags & SCST_TRANSFER_LEN_TYPE_FIXED;
|
||||
switch (cmd->cdb[0]) {
|
||||
case READ_CAPACITY:
|
||||
cmd->bufflen = READ_CAP_LEN;
|
||||
cmd->data_direction = SCST_DATA_READ;
|
||||
break;
|
||||
#if 0
|
||||
case SYNCHRONIZE_CACHE:
|
||||
cmd->underflow = 0;
|
||||
break;
|
||||
#endif
|
||||
case VERIFY_6:
|
||||
case VERIFY:
|
||||
case VERIFY_12:
|
||||
case VERIFY_16:
|
||||
if ((cmd->cdb[1] & BYTCHK) == 0) {
|
||||
cmd->bufflen = 0;
|
||||
cmd->data_direction = SCST_DATA_NONE;
|
||||
fixed = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
|
||||
if (fixed) {
|
||||
/*
|
||||
* No need for locks here, since *_detach() can not be
|
||||
* called, when there are existing commands.
|
||||
*/
|
||||
disk = (struct disk_params *)cmd->dev->tgt_dev_specific;
|
||||
cmd->bufflen = info_cdb->transfer_len * disk->sector_size;
|
||||
}
|
||||
|
||||
TRACE_DBG("res %d bufflen %zd direct %d",
|
||||
res, cmd->bufflen, cmd->data_direction);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: disk_done
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns :
|
||||
*
|
||||
* Description: This is the completion routine for the command,
|
||||
* it is used to extract any necessary information
|
||||
* about a command.
|
||||
********************************************************************/
|
||||
int disk_done(struct scst_cmd *cmd)
|
||||
{
|
||||
int opcode = cmd->cdb[0];
|
||||
int masked_status = cmd->masked_status;
|
||||
struct disk_params *disk;
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->sg == NULL))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len
|
||||
* based on cmd->masked_status and cmd->data_direction, therefore change
|
||||
* them only if necessary
|
||||
*/
|
||||
|
||||
if ((masked_status == GOOD) || (masked_status == CONDITION_GOOD)) {
|
||||
switch (opcode) {
|
||||
case READ_CAPACITY:
|
||||
{
|
||||
/* Always keep track of disk capacity */
|
||||
int buffer_size;
|
||||
/*
|
||||
* To force the compiler not to optimize it out to keep
|
||||
* disk->sector_size access atomic
|
||||
*/
|
||||
volatile int sector_size;
|
||||
uint8_t *buffer;
|
||||
buffer_size = scst_get_buf_first(cmd, &buffer);
|
||||
if (unlikely(buffer_size <= 0)) {
|
||||
PRINT_ERROR_PR("%s: Unable to get the buffer",
|
||||
__FUNCTION__);
|
||||
scst_set_busy(cmd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* No need for locks here, since *_detach() can not be
|
||||
* called, when there are existing commands.
|
||||
*/
|
||||
disk = (struct disk_params *)cmd->dev->tgt_dev_specific;
|
||||
sector_size =
|
||||
((buffer[4] << 24) | (buffer[5] << 16) |
|
||||
(buffer[6] << 8) | (buffer[7] << 0));
|
||||
if (!sector_size)
|
||||
sector_size = 512;
|
||||
disk->sector_size = sector_size;
|
||||
TRACE_DBG("Sector size is %i", disk->sector_size);
|
||||
|
||||
scst_put_buf(cmd, buffer);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_DBG("cmd->tgt_resp_flags=%x, cmd->resp_data_len=%d, "
|
||||
"res=%d", cmd->tgt_resp_flags, cmd->resp_data_len, res);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: disk_exec
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns :
|
||||
*
|
||||
* Description: Make SCST do nothing for data READs and WRITES.
|
||||
* Intended for raw line performance testing
|
||||
********************************************************************/
|
||||
int disk_exec(struct scst_cmd *cmd)
|
||||
{
|
||||
int res = SCST_EXEC_NOT_COMPLETED;
|
||||
int opcode = cmd->cdb[0];
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
switch (opcode) {
|
||||
case WRITE_6:
|
||||
case WRITE_10:
|
||||
case WRITE_12:
|
||||
case WRITE_16:
|
||||
case READ_6:
|
||||
case READ_10:
|
||||
case READ_12:
|
||||
case READ_16:
|
||||
res = SCST_EXEC_COMPLETED;
|
||||
cmd->status = 0;
|
||||
cmd->masked_status = 0;
|
||||
cmd->msg_status = 0;
|
||||
cmd->host_status = DID_OK;
|
||||
cmd->driver_status = 0;
|
||||
cmd->completed = 1;
|
||||
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
* scst_modisk.c
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* SCSI MO disk (type 7) dev handler
|
||||
* &
|
||||
* SCSI MO disk (type 7) "performance" device handler (skip all READ and WRITE
|
||||
* operations).
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define LOG_PREFIX "dev_modisk"
|
||||
#include "scst_debug.h"
|
||||
#include "scsi_tgt.h"
|
||||
#include "scst_dev_handler.h"
|
||||
|
||||
#include "scst_debug.c"
|
||||
|
||||
# define MODISK_NAME "dev_modisk"
|
||||
# define MODISK_PERF_NAME "dev_modisk_perf"
|
||||
|
||||
#define MODISK_TYPE { \
|
||||
name: MODISK_NAME, \
|
||||
type: TYPE_MOD, \
|
||||
parse_atomic: 1, \
|
||||
dev_done_atomic: 1, \
|
||||
exec_atomic: 1, \
|
||||
attach: modisk_attach, \
|
||||
detach: modisk_detach, \
|
||||
parse: modisk_parse, \
|
||||
dev_done: modisk_done, \
|
||||
}
|
||||
|
||||
#define MODISK_PERF_TYPE { \
|
||||
name: MODISK_PERF_NAME, \
|
||||
type: TYPE_MOD, \
|
||||
parse_atomic: 1, \
|
||||
dev_done_atomic: 1, \
|
||||
exec_atomic: 1, \
|
||||
attach: modisk_attach, \
|
||||
detach: modisk_detach, \
|
||||
parse: modisk_parse, \
|
||||
dev_done: modisk_done, \
|
||||
exec: modisk_exec, \
|
||||
}
|
||||
|
||||
#define MODISK_RETRIES 2
|
||||
#define MODISK_SMALL_TIMEOUT (3 * HZ)
|
||||
#define MODISK_REG_TIMEOUT (900 * HZ)
|
||||
#define MODISK_LONG_TIMEOUT (14000 * HZ)
|
||||
#define READ_CAP_LEN 8
|
||||
#define MODISK_SECTOR_SIZE 1024
|
||||
/* Flags */
|
||||
#define BYTCHK 0x02
|
||||
|
||||
struct modisk_params
|
||||
{
|
||||
int sector_size;
|
||||
};
|
||||
|
||||
int modisk_attach(struct scst_device *);
|
||||
void modisk_detach(struct scst_device *);
|
||||
int modisk_parse(struct scst_cmd *, const struct scst_info_cdb *);
|
||||
int modisk_done(struct scst_cmd *);
|
||||
int modisk_exec(struct scst_cmd *);
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
unsigned long trace_flag = SCST_DEFAULT_DEV_LOG_FLAGS;
|
||||
#endif
|
||||
|
||||
static struct scst_dev_type modisk_devtype = MODISK_TYPE;
|
||||
static struct scst_dev_type modisk_devtype_perf = MODISK_PERF_TYPE;
|
||||
|
||||
static int __init init_scst_modisk_driver(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
modisk_devtype.module = THIS_MODULE;
|
||||
if (scst_register_dev_driver(&modisk_devtype) < 0) {
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = scst_dev_handler_build_std_proc(&modisk_devtype);
|
||||
if (res != 0)
|
||||
goto out_unreg1;
|
||||
|
||||
modisk_devtype_perf.module = THIS_MODULE;
|
||||
if (scst_register_dev_driver(&modisk_devtype_perf) < 0) {
|
||||
res = -ENODEV;
|
||||
goto out_unreg1_err1;
|
||||
}
|
||||
|
||||
res = scst_dev_handler_build_std_proc(&modisk_devtype_perf);
|
||||
if (res != 0)
|
||||
goto out_unreg2;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_unreg2:
|
||||
scst_dev_handler_destroy_std_proc(&modisk_devtype_perf);
|
||||
|
||||
out_unreg1_err1:
|
||||
scst_dev_handler_destroy_std_proc(&modisk_devtype);
|
||||
|
||||
out_unreg1:
|
||||
scst_unregister_dev_driver(&modisk_devtype);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void __exit exit_scst_modisk_driver(void)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
scst_dev_handler_destroy_std_proc(&modisk_devtype_perf);
|
||||
scst_unregister_dev_driver(&modisk_devtype_perf);
|
||||
scst_dev_handler_destroy_std_proc(&modisk_devtype);
|
||||
scst_unregister_dev_driver(&modisk_devtype);
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(init_scst_modisk_driver);
|
||||
module_exit(exit_scst_modisk_driver);
|
||||
|
||||
/**************************************************************
|
||||
* Function: modisk_attach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : 1 if attached, error code otherwise
|
||||
*
|
||||
* Description:
|
||||
*************************************************************/
|
||||
int modisk_attach(struct scst_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
uint8_t cmd[10];
|
||||
const int buffer_size = 512;
|
||||
uint8_t *buffer = NULL;
|
||||
int retries;
|
||||
unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];
|
||||
enum dma_data_direction data_dir;
|
||||
unsigned char *sbuff;
|
||||
struct modisk_params *modisk;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (dev->scsi_dev == NULL ||
|
||||
dev->scsi_dev->type != dev->handler->type) {
|
||||
PRINT_ERROR_PR("%s", "SCSI device not define or illegal type");
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
modisk = kzalloc(sizeof(*modisk), GFP_KERNEL);
|
||||
TRACE_MEM("kzalloc(GFP_KERNEL) for struct modisk_params (%zu): %p",
|
||||
sizeof(*modisk), modisk);
|
||||
if (modisk == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "%s",
|
||||
"Unable to allocate struct modisk_params");
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
modisk->sector_size = MODISK_SECTOR_SIZE;
|
||||
|
||||
/*
|
||||
* If the device is offline, don't try to read capacity or any
|
||||
* of the other stuff
|
||||
*/
|
||||
if (dev->scsi_dev->sdev_state == SDEV_OFFLINE)
|
||||
{
|
||||
TRACE_DBG("%s", "Device is offline");
|
||||
res = -ENODEV;
|
||||
goto out_free_modisk;
|
||||
}
|
||||
|
||||
buffer = kzalloc(buffer_size, GFP_KERNEL);
|
||||
TRACE_MEM("kzalloc(GFP_KERNEL) for %d: %p", buffer_size, buffer);
|
||||
if (!buffer) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "%s", "Memory allocation failure");
|
||||
res = -ENOMEM;
|
||||
goto out_free_modisk;
|
||||
}
|
||||
|
||||
/* Clear any existing UA's and get modisk capacity (modisk block size) */
|
||||
memset(cmd, 0, sizeof(cmd));
|
||||
cmd[0] = READ_CAPACITY;
|
||||
cmd[1] = (dev->scsi_dev->scsi_level <= SCSI_2) ?
|
||||
((dev->scsi_dev->lun << 5) & 0xe0) : 0;
|
||||
retries = SCST_DEV_UA_RETRIES;
|
||||
while (1) {
|
||||
memset(buffer, 0, buffer_size);
|
||||
data_dir = SCST_DATA_READ;
|
||||
sbuff = sense_buffer;
|
||||
|
||||
TRACE_DBG("%s", "Doing READ_CAPACITY");
|
||||
res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
|
||||
buffer_size, sbuff,
|
||||
MODISK_REG_TIMEOUT, MODISK_RETRIES, 0);
|
||||
|
||||
TRACE_DBG("READ_CAPACITY done: %x", res);
|
||||
|
||||
if (!res || (sbuff[2] != UNIT_ATTENTION))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!--retries) {
|
||||
PRINT_ERROR_PR("UA not clear after %d retries",
|
||||
SCST_DEV_UA_RETRIES);
|
||||
goto out_free_buf;
|
||||
}
|
||||
}
|
||||
if (res == 0) {
|
||||
modisk->sector_size = ((buffer[4] << 24) | (buffer[5] << 16) |
|
||||
(buffer[6] << 8) | (buffer[7] << 0));
|
||||
TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)",
|
||||
modisk->sector_size, dev->scsi_dev->scsi_level, SCSI_2);
|
||||
if (!modisk->sector_size) {
|
||||
modisk->sector_size = MODISK_SECTOR_SIZE;
|
||||
}
|
||||
} else {
|
||||
TRACE_BUFFER("Sense set", sbuff, SCSI_SENSE_BUFFERSIZE);
|
||||
|
||||
if (sbuff[2] != NOT_READY)
|
||||
res = -ENODEV;
|
||||
goto out_free_buf;
|
||||
}
|
||||
|
||||
out_free_buf:
|
||||
TRACE_MEM("kfree for buffer: %p", buffer);
|
||||
kfree(buffer);
|
||||
|
||||
out_free_modisk:
|
||||
if (res == 0)
|
||||
dev->tgt_dev_specific = modisk;
|
||||
else {
|
||||
TRACE_MEM("kfree for modisk: %p", modisk);
|
||||
kfree(modisk);
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Function: modisk_detach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : None
|
||||
*
|
||||
* Description: Called to detach this device type driver
|
||||
************************************************************/
|
||||
void modisk_detach(struct scst_device *dev)
|
||||
{
|
||||
struct modisk_params *modisk =
|
||||
(struct modisk_params *)dev->tgt_dev_specific;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_MEM("kfree for modisk: %p", modisk);
|
||||
kfree(modisk);
|
||||
dev->tgt_dev_specific = NULL;
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: modisk_parse
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : The state of the command
|
||||
*
|
||||
* Description: This does the parsing of the command
|
||||
*
|
||||
* Note: Not all states are allowed on return
|
||||
********************************************************************/
|
||||
int modisk_parse(struct scst_cmd *cmd, const struct scst_info_cdb *info_cdb)
|
||||
{
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
struct modisk_params *modisk;
|
||||
int fixed;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->data_direction and cmd->bufflen
|
||||
* based on info_cdb, therefore change them only if necessary
|
||||
*/
|
||||
|
||||
if (info_cdb->flags & SCST_SMALL_TIMEOUT) {
|
||||
cmd->timeout = MODISK_SMALL_TIMEOUT;
|
||||
} else if (info_cdb->flags & SCST_LONG_TIMEOUT) {
|
||||
cmd->timeout = MODISK_LONG_TIMEOUT;
|
||||
} else {
|
||||
cmd->timeout = MODISK_REG_TIMEOUT;
|
||||
}
|
||||
|
||||
TRACE_DBG("op_name <%s> direct %d flags %d transfer_len %d lun %d(%d)",
|
||||
info_cdb->op_name,
|
||||
info_cdb->direction,
|
||||
info_cdb->flags,
|
||||
info_cdb->transfer_len, cmd->lun, (cmd->cdb[1] >> 5) & 7);
|
||||
|
||||
cmd->cdb[1] &= 0x1f;
|
||||
|
||||
fixed = info_cdb->flags & SCST_TRANSFER_LEN_TYPE_FIXED;
|
||||
switch (cmd->cdb[0]) {
|
||||
case READ_CAPACITY:
|
||||
cmd->bufflen = READ_CAP_LEN;
|
||||
cmd->data_direction = SCST_DATA_READ;
|
||||
break;
|
||||
case 0xB6 /* SET_STREAMING */ :
|
||||
cmd->bufflen = (((*(cmd->cdb + 9)) & 0xff) << 8) +
|
||||
((*(cmd->cdb + 10)) & 0xff);
|
||||
cmd->bufflen &= 0xffff;
|
||||
break;
|
||||
case 0xBE /* READ_CD */ :
|
||||
cmd->bufflen = cmd->bufflen >> 8;
|
||||
break;
|
||||
#if 0
|
||||
case SYNCHRONIZE_CACHE:
|
||||
cmd->underflow = 0;
|
||||
break;
|
||||
#endif
|
||||
case VERIFY_6:
|
||||
case VERIFY:
|
||||
case VERIFY_12:
|
||||
case VERIFY_16:
|
||||
if ((cmd->cdb[1] & BYTCHK) == 0) {
|
||||
cmd->bufflen = 0;
|
||||
cmd->data_direction = SCST_DATA_NONE;
|
||||
fixed = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
|
||||
if (fixed) {
|
||||
/*
|
||||
* No need for locks here, since *_detach() can not be
|
||||
* called, when there are existing commands.
|
||||
*/
|
||||
modisk = (struct modisk_params *)cmd->dev->tgt_dev_specific;
|
||||
cmd->bufflen = info_cdb->transfer_len * modisk->sector_size;
|
||||
}
|
||||
|
||||
TRACE_DBG("res %d bufflen %zd direct %d",
|
||||
res, cmd->bufflen, cmd->data_direction);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: modisk_done
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns :
|
||||
*
|
||||
* Description: This is the completion routine for the command,
|
||||
* it is used to extract any necessary information
|
||||
* about a command.
|
||||
********************************************************************/
|
||||
int modisk_done(struct scst_cmd *cmd)
|
||||
{
|
||||
int opcode = cmd->cdb[0];
|
||||
int masked_status = cmd->masked_status;
|
||||
struct modisk_params *modisk;
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->sg == NULL))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len
|
||||
* based on cmd->masked_status and cmd->data_direction, therefore change
|
||||
* them only if necessary
|
||||
*/
|
||||
|
||||
if ((masked_status == GOOD) || (masked_status == CONDITION_GOOD)) {
|
||||
switch (opcode) {
|
||||
case READ_CAPACITY:
|
||||
{
|
||||
/* Always keep track of modisk capacity */
|
||||
int buffer_size;
|
||||
/*
|
||||
* To force the compiler not to optimize it out to keep
|
||||
* modisk->sector_size access atomic
|
||||
*/
|
||||
volatile int sector_size;
|
||||
uint8_t *buffer;
|
||||
buffer_size = scst_get_buf_first(cmd, &buffer);
|
||||
if (unlikely(buffer_size <= 0)) {
|
||||
PRINT_ERROR_PR("%s: Unable to get the buffer",
|
||||
__FUNCTION__);
|
||||
scst_set_busy(cmd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* No need for locks here, since *_detach() can not be
|
||||
* called, when there are existing commands.
|
||||
*/
|
||||
modisk =
|
||||
(struct modisk_params *)cmd->dev->tgt_dev_specific;
|
||||
sector_size =
|
||||
((buffer[4] << 24) | (buffer[5] << 16) |
|
||||
(buffer[6] << 8) | (buffer[7] << 0));
|
||||
if (!sector_size)
|
||||
sector_size = MODISK_SECTOR_SIZE;
|
||||
modisk->sector_size = sector_size;
|
||||
TRACE_DBG("Sector size is %i", modisk->sector_size);
|
||||
|
||||
scst_put_buf(cmd, buffer);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_DBG("cmd->tgt_resp_flags=%x, cmd->resp_data_len=%d, "
|
||||
"res=%d", cmd->tgt_resp_flags, cmd->resp_data_len, res);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: modisk_exec
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns :
|
||||
*
|
||||
* Description: Make SCST do nothing for data READs and WRITES.
|
||||
* Intended for raw line performance testing
|
||||
********************************************************************/
|
||||
int modisk_exec(struct scst_cmd *cmd)
|
||||
{
|
||||
int res = SCST_EXEC_NOT_COMPLETED;
|
||||
int opcode = cmd->cdb[0];
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
switch (opcode) {
|
||||
case WRITE_6:
|
||||
case WRITE_10:
|
||||
case WRITE_12:
|
||||
case WRITE_16:
|
||||
case READ_6:
|
||||
case READ_10:
|
||||
case READ_12:
|
||||
case READ_16:
|
||||
res = SCST_EXEC_COMPLETED;
|
||||
cmd->status = 0;
|
||||
cmd->masked_status = 0;
|
||||
cmd->msg_status = 0;
|
||||
cmd->host_status = DID_OK;
|
||||
cmd->driver_status = 0;
|
||||
cmd->completed = 1;
|
||||
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* scst_processor.c
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* SCSI medium processor (type 3) dev handler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_PREFIX "dev_processor"
|
||||
#include "scst_debug.h"
|
||||
#include "scsi_tgt.h"
|
||||
#include "scst_dev_handler.h"
|
||||
|
||||
#include "scst_debug.c"
|
||||
|
||||
#define PROCESSOR_NAME "dev_processor"
|
||||
|
||||
#define PROCESSOR_TYPE { \
|
||||
name: PROCESSOR_NAME, \
|
||||
type: TYPE_PROCESSOR, \
|
||||
parse_atomic: 1, \
|
||||
/* dev_done_atomic: 1,*/ \
|
||||
attach: processor_attach, \
|
||||
/* detach: processor_detach,*/ \
|
||||
parse: processor_parse, \
|
||||
/* dev_done: processor_done*/ \
|
||||
}
|
||||
|
||||
#define PROCESSOR_RETRIES 2
|
||||
#define PROCESSOR_TIMEOUT (3 * HZ)
|
||||
#define PROCESSOR_LONG_TIMEOUT (14000 * HZ)
|
||||
#define READ_CAP_LEN 8
|
||||
|
||||
int processor_attach(struct scst_device *);
|
||||
void processor_detach(struct scst_device *);
|
||||
int processor_parse(struct scst_cmd *, const struct scst_info_cdb *);
|
||||
int processor_done(struct scst_cmd *);
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
unsigned long trace_flag = SCST_DEFAULT_DEV_LOG_FLAGS;
|
||||
#endif
|
||||
|
||||
static struct scst_dev_type processor_devtype = PROCESSOR_TYPE;
|
||||
|
||||
/**************************************************************
|
||||
* Function: processor_attach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : 1 if attached, error code otherwise
|
||||
*
|
||||
* Description:
|
||||
*************************************************************/
|
||||
int processor_attach(struct scst_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
int retries;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (dev->scsi_dev == NULL ||
|
||||
dev->scsi_dev->type != dev->handler->type) {
|
||||
PRINT_ERROR_PR("%s", "SCSI device not define or illegal type");
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the device is offline, don't try to read capacity or any
|
||||
* of the other stuff
|
||||
*/
|
||||
if (dev->scsi_dev->sdev_state == SDEV_OFFLINE)
|
||||
{
|
||||
TRACE_DBG("%s", "Device is offline");
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retries = SCST_DEV_UA_RETRIES;
|
||||
do {
|
||||
TRACE_DBG("%s", "Doing TEST_UNIT_READY");
|
||||
res = scsi_test_unit_ready(dev->scsi_dev, PROCESSOR_TIMEOUT,
|
||||
PROCESSOR_RETRIES);
|
||||
TRACE_DBG("TEST_UNIT_READY done: %x", res);
|
||||
} while ((--retries > 0) && res);
|
||||
if (res)
|
||||
res = -ENODEV;
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return res;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Function: processor_detach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : None
|
||||
*
|
||||
* Description: Called to detach this device type driver
|
||||
************************************************************/
|
||||
void processor_detach(struct scst_device *dev)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: processor_parse
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : The state of the command
|
||||
*
|
||||
* Description: This does the parsing of the command
|
||||
*
|
||||
* Note: Not all states are allowed on return
|
||||
********************************************************************/
|
||||
int processor_parse(struct scst_cmd *cmd, const struct scst_info_cdb *info_cdb)
|
||||
{
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->data_direction and cmd->bufflen
|
||||
* based on info_cdb, therefore change them only if necessary
|
||||
*/
|
||||
|
||||
if (info_cdb->flags & SCST_LONG_TIMEOUT) {
|
||||
cmd->timeout = PROCESSOR_LONG_TIMEOUT;
|
||||
} else {
|
||||
cmd->timeout = PROCESSOR_TIMEOUT;
|
||||
}
|
||||
|
||||
TRACE_DBG("op_name <%s> direct %d flags %d transfer_len %d",
|
||||
info_cdb->op_name,
|
||||
info_cdb->direction, info_cdb->flags, info_cdb->transfer_len);
|
||||
#if 0
|
||||
switch (cmd->cdb[0]) {
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
TRACE_DBG("res %d bufflen %zd direct %d",
|
||||
res, cmd->bufflen, cmd->data_direction);
|
||||
|
||||
TRACE_EXIT();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: processor_done
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns :
|
||||
*
|
||||
* Description: This is the completion routine for the command,
|
||||
* it is used to extract any necessary information
|
||||
* about a command.
|
||||
********************************************************************/
|
||||
int processor_done(struct scst_cmd *cmd)
|
||||
{
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->sg == NULL))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len
|
||||
* based on cmd->masked_status and cmd->data_direction, therefore change
|
||||
* them only if necessary
|
||||
*/
|
||||
|
||||
#if 0
|
||||
switch (cmd->cdb[0]) {
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return res;
|
||||
}
|
||||
|
||||
static int __init processor_init(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
processor_devtype.module = THIS_MODULE;
|
||||
if (scst_register_dev_driver(&processor_devtype) < 0) {
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = scst_dev_handler_build_std_proc(&processor_devtype);
|
||||
if (res != 0)
|
||||
goto out_err;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_err:
|
||||
scst_unregister_dev_driver(&processor_devtype);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void __exit processor_exit(void)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
scst_dev_handler_destroy_std_proc(&processor_devtype);
|
||||
scst_unregister_dev_driver(&processor_devtype);
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(processor_init);
|
||||
module_exit(processor_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* scst_raid.c
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* SCSI raid(controller) (type 0xC) dev handler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_PREFIX "dev_raid"
|
||||
#include "scst_debug.h"
|
||||
#include "scsi_tgt.h"
|
||||
#include "scst_dev_handler.h"
|
||||
|
||||
#include "scst_debug.c"
|
||||
|
||||
#define RAID_NAME "dev_raid"
|
||||
|
||||
#define RAID_TYPE { \
|
||||
name: RAID_NAME, \
|
||||
type: TYPE_RAID, \
|
||||
parse_atomic: 1, \
|
||||
/* dev_done_atomic: 1,*/ \
|
||||
attach: raid_attach, \
|
||||
/* detach: raid_detach,*/ \
|
||||
parse: raid_parse, \
|
||||
/* dev_done: raid_done*/ \
|
||||
}
|
||||
|
||||
#define RAID_RETRIES 2
|
||||
#define RAID_TIMEOUT (3 * HZ)
|
||||
#define RAID_LONG_TIMEOUT (14000 * HZ)
|
||||
#define READ_CAP_LEN 8
|
||||
|
||||
int raid_attach(struct scst_device *);
|
||||
void raid_detach(struct scst_device *);
|
||||
int raid_parse(struct scst_cmd *, const struct scst_info_cdb *);
|
||||
int raid_done(struct scst_cmd *);
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
unsigned long trace_flag = SCST_DEFAULT_DEV_LOG_FLAGS;
|
||||
#endif
|
||||
|
||||
static struct scst_dev_type raid_devtype = RAID_TYPE;
|
||||
|
||||
/**************************************************************
|
||||
* Function: raid_attach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : 1 if attached, error code otherwise
|
||||
*
|
||||
* Description:
|
||||
*************************************************************/
|
||||
int raid_attach(struct scst_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
int retries;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (dev->scsi_dev == NULL ||
|
||||
dev->scsi_dev->type != dev->handler->type) {
|
||||
PRINT_ERROR_PR("%s", "SCSI device not define or illegal type");
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the device is offline, don't try to read capacity or any
|
||||
* of the other stuff
|
||||
*/
|
||||
if (dev->scsi_dev->sdev_state == SDEV_OFFLINE)
|
||||
{
|
||||
TRACE_DBG("%s", "Device is offline");
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retries = SCST_DEV_UA_RETRIES;
|
||||
do {
|
||||
TRACE_DBG("%s", "Doing TEST_UNIT_READY");
|
||||
res = scsi_test_unit_ready(dev->scsi_dev, RAID_TIMEOUT,
|
||||
RAID_RETRIES);
|
||||
TRACE_DBG("TEST_UNIT_READY done: %x", res);
|
||||
} while ((--retries > 0) && res);
|
||||
if (res)
|
||||
res = -ENODEV;
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return res;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Function: raid_detach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : None
|
||||
*
|
||||
* Description: Called to detach this device type driver
|
||||
************************************************************/
|
||||
void raid_detach(struct scst_device *dev)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: raid_parse
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : The state of the command
|
||||
*
|
||||
* Description: This does the parsing of the command
|
||||
*
|
||||
* Note: Not all states are allowed on return
|
||||
********************************************************************/
|
||||
int raid_parse(struct scst_cmd *cmd, const struct scst_info_cdb *info_cdb)
|
||||
{
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->data_direction and cmd->bufflen
|
||||
* based on info_cdb, therefore change them only if necessary
|
||||
*/
|
||||
|
||||
if (info_cdb->flags & SCST_LONG_TIMEOUT) {
|
||||
cmd->timeout = RAID_LONG_TIMEOUT;
|
||||
} else {
|
||||
cmd->timeout = RAID_TIMEOUT;
|
||||
}
|
||||
|
||||
TRACE_DBG("op_name <%s> direct %d flags %d transfer_len %d",
|
||||
info_cdb->op_name,
|
||||
info_cdb->direction, info_cdb->flags, info_cdb->transfer_len);
|
||||
#if 0
|
||||
switch (cmd->cdb[0]) {
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
TRACE_DBG("res %d bufflen %zd direct %d",
|
||||
res, cmd->bufflen, cmd->data_direction);
|
||||
|
||||
TRACE_EXIT();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: raid_done
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns :
|
||||
*
|
||||
* Description: This is the completion routine for the command,
|
||||
* it is used to extract any necessary information
|
||||
* about a command.
|
||||
********************************************************************/
|
||||
int raid_done(struct scst_cmd *cmd)
|
||||
{
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->sg == NULL))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len
|
||||
* based on cmd->masked_status and cmd->data_direction, therefore change
|
||||
* them only if necessary
|
||||
*/
|
||||
|
||||
#if 0
|
||||
switch (cmd->cdb[0]) {
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return res;
|
||||
}
|
||||
|
||||
static int __init raid_init(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
raid_devtype.module = THIS_MODULE;
|
||||
if (scst_register_dev_driver(&raid_devtype) < 0) {
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = scst_dev_handler_build_std_proc(&raid_devtype);
|
||||
if (res != 0)
|
||||
goto out_err;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_err:
|
||||
scst_unregister_dev_driver(&raid_devtype);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void __exit raid_exit(void)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
scst_dev_handler_destroy_std_proc(&raid_devtype);
|
||||
scst_unregister_dev_driver(&raid_devtype);
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(raid_init);
|
||||
module_exit(raid_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* scst_tape.c
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* SCSI tape (type 1) dev handler
|
||||
* &
|
||||
* SCSI tape (type 1) "performance" device handler (skip all READ and WRITE
|
||||
* operations).
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define LOG_PREFIX "dev_tape"
|
||||
#include "scst_debug.h"
|
||||
#include "scsi_tgt.h"
|
||||
#include "scst_dev_handler.h"
|
||||
|
||||
#include "scst_debug.c"
|
||||
|
||||
# define TAPE_NAME "dev_tape"
|
||||
# define TAPE_PERF_NAME "dev_tape_perf"
|
||||
|
||||
#define TAPE_TYPE { \
|
||||
name: TAPE_NAME, \
|
||||
type: TYPE_TAPE, \
|
||||
parse_atomic: 1, \
|
||||
dev_done_atomic: 1, \
|
||||
exec_atomic: 1, \
|
||||
attach: tape_attach, \
|
||||
detach: tape_detach, \
|
||||
parse: tape_parse, \
|
||||
dev_done: tape_done, \
|
||||
}
|
||||
|
||||
#define TAPE_PERF_TYPE { \
|
||||
name: TAPE_PERF_NAME, \
|
||||
type: TYPE_TAPE, \
|
||||
parse_atomic: 1, \
|
||||
dev_done_atomic: 1, \
|
||||
exec_atomic: 1, \
|
||||
attach: tape_attach, \
|
||||
detach: tape_detach, \
|
||||
parse: tape_parse, \
|
||||
dev_done: tape_done, \
|
||||
exec: tape_exec, \
|
||||
}
|
||||
|
||||
#define TAPE_RETRIES 2
|
||||
|
||||
#define TAPE_SMALL_TIMEOUT (3 * HZ)
|
||||
#define TAPE_REG_TIMEOUT (900 * HZ)
|
||||
#define TAPE_LONG_TIMEOUT (14000 * HZ)
|
||||
|
||||
/* The fixed bit in READ/WRITE/VERIFY */
|
||||
#define SILI_BIT 2
|
||||
|
||||
/* Bits in the READ POSITION command */
|
||||
#define TCLP_BIT 4
|
||||
#define LONG_BIT 2
|
||||
#define BT_BIT 1
|
||||
|
||||
#define POSITION_LEN_SHORT 20
|
||||
#define POSITION_LEN_LONG 32
|
||||
|
||||
struct tape_params
|
||||
{
|
||||
spinlock_t tp_lock;
|
||||
uint8_t density;
|
||||
uint8_t mode;
|
||||
uint8_t speed;
|
||||
uint8_t medium_type;
|
||||
int block_size;
|
||||
};
|
||||
|
||||
int tape_attach(struct scst_device *);
|
||||
void tape_detach(struct scst_device *);
|
||||
int tape_parse(struct scst_cmd *, const struct scst_info_cdb *);
|
||||
int tape_done(struct scst_cmd *);
|
||||
int tape_exec(struct scst_cmd *);
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
unsigned long trace_flag = SCST_DEFAULT_DEV_LOG_FLAGS;
|
||||
#endif
|
||||
|
||||
static struct scst_dev_type tape_devtype = TAPE_TYPE;
|
||||
static struct scst_dev_type tape_devtype_perf = TAPE_PERF_TYPE;
|
||||
|
||||
static int __init init_scst_tape_driver(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
tape_devtype.module = THIS_MODULE;
|
||||
if (scst_register_dev_driver(&tape_devtype) < 0) {
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = scst_dev_handler_build_std_proc(&tape_devtype);
|
||||
if (res != 0)
|
||||
goto out_unreg1;
|
||||
|
||||
tape_devtype_perf.module = THIS_MODULE;
|
||||
if (scst_register_dev_driver(&tape_devtype_perf) < 0) {
|
||||
res = -ENODEV;
|
||||
goto out_unreg1_err1;
|
||||
}
|
||||
|
||||
res = scst_dev_handler_build_std_proc(&tape_devtype_perf);
|
||||
if (res != 0)
|
||||
goto out_unreg2;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_unreg2:
|
||||
scst_dev_handler_destroy_std_proc(&tape_devtype_perf);
|
||||
|
||||
out_unreg1_err1:
|
||||
scst_dev_handler_destroy_std_proc(&tape_devtype);
|
||||
|
||||
out_unreg1:
|
||||
scst_unregister_dev_driver(&tape_devtype);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void __exit exit_scst_tape_driver(void)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
scst_dev_handler_destroy_std_proc(&tape_devtype_perf);
|
||||
scst_unregister_dev_driver(&tape_devtype_perf);
|
||||
scst_dev_handler_destroy_std_proc(&tape_devtype);
|
||||
scst_unregister_dev_driver(&tape_devtype);
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(init_scst_tape_driver);
|
||||
module_exit(exit_scst_tape_driver);
|
||||
|
||||
/**************************************************************
|
||||
* Function: tape_attach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : 1 if attached, error code otherwise
|
||||
*
|
||||
* Description:
|
||||
*************************************************************/
|
||||
int tape_attach(struct scst_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
int retries;
|
||||
struct scsi_mode_data data;
|
||||
const int buffer_size = 512;
|
||||
uint8_t *buffer = NULL;
|
||||
struct tape_params *tape;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (dev->scsi_dev == NULL ||
|
||||
dev->scsi_dev->type != dev->handler->type) {
|
||||
PRINT_ERROR_PR("%s", "SCSI device not define or illegal type");
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tape = kzalloc(sizeof(*tape), GFP_KERNEL);
|
||||
TRACE_MEM("kzalloc(GFP_KERNEL) for struct tape_params (%zu): %p",
|
||||
sizeof(*tape), tape);
|
||||
if (tape == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "%s",
|
||||
"Unable to allocate struct tape_params");
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
spin_lock_init(&tape->tp_lock);
|
||||
|
||||
buffer = kmalloc(buffer_size, GFP_KERNEL);
|
||||
TRACE_MEM("kmalloc(GFP_KERNEL) for %d: %p", buffer_size, buffer);
|
||||
if (!buffer) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "%s", "Memory allocation failure");
|
||||
res = -ENOMEM;
|
||||
goto out_free_req;
|
||||
}
|
||||
|
||||
retries = SCST_DEV_UA_RETRIES;
|
||||
do {
|
||||
TRACE_DBG("%s", "Doing TEST_UNIT_READY");
|
||||
res = scsi_test_unit_ready(dev->scsi_dev, TAPE_SMALL_TIMEOUT,
|
||||
TAPE_RETRIES);
|
||||
TRACE_DBG("TEST_UNIT_READY done: %x", res);
|
||||
} while ((--retries > 0) && res);
|
||||
if (res) {
|
||||
res = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
TRACE_DBG("%s", "Doing MODE_SENSE");
|
||||
res = scsi_mode_sense(dev->scsi_dev,
|
||||
((dev->scsi_dev->scsi_level <= SCSI_2) ?
|
||||
((dev->scsi_dev->lun << 5) & 0xe0) : 0),
|
||||
0 /* Mode Page 0 */,
|
||||
buffer, buffer_size,
|
||||
TAPE_SMALL_TIMEOUT, TAPE_RETRIES,
|
||||
&data, NULL);
|
||||
TRACE_DBG("MODE_SENSE done: %x", res);
|
||||
|
||||
if (res == 0) {
|
||||
if (buffer[3] == 8) {
|
||||
tape->block_size = ((buffer[9] << 16) |
|
||||
(buffer[10] << 8) |
|
||||
(buffer[11] << 0));
|
||||
} else {
|
||||
tape->block_size = 512;
|
||||
}
|
||||
tape->medium_type = buffer[1];
|
||||
tape->mode = (buffer[2] & 0x70) >> 4;
|
||||
tape->speed = buffer[2] & 0x0f;
|
||||
tape->density = buffer[4];
|
||||
TRACE_DBG("Tape: lun %d. bs %d. type 0x%02x mode 0x%02x "
|
||||
"speed 0x%02x dens 0x%02x", dev->scsi_dev->lun,
|
||||
tape->block_size, tape->medium_type,
|
||||
tape->mode, tape->speed, tape->density);
|
||||
} else {
|
||||
res = -ENODEV;
|
||||
goto out_free_buf;
|
||||
}
|
||||
|
||||
out_free_buf:
|
||||
TRACE_MEM("kfree for buffer: %p", buffer);
|
||||
kfree(buffer);
|
||||
|
||||
out_free_req:
|
||||
if (res == 0)
|
||||
dev->tgt_dev_specific = tape;
|
||||
else {
|
||||
TRACE_MEM("kfree for tape: %p", tape);
|
||||
kfree(tape);
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* Function: tape_detach
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : None
|
||||
*
|
||||
* Description: Called to detach this device type driver
|
||||
************************************************************/
|
||||
void tape_detach(struct scst_device *dev)
|
||||
{
|
||||
struct tape_params *tape = (struct tape_params *)dev->tgt_dev_specific;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_MEM("kfree for tape: %p", tape);
|
||||
kfree(tape);
|
||||
dev->tgt_dev_specific = NULL;
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: tape_parse
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns : The state of the command
|
||||
*
|
||||
* Description: This does the parsing of the command
|
||||
*
|
||||
* Note: Not all states are allowed on return
|
||||
********************************************************************/
|
||||
int tape_parse(struct scst_cmd *cmd, const struct scst_info_cdb *info_cdb)
|
||||
{
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
struct tape_params *tape;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->data_direction and cmd->bufflen
|
||||
* based on info_cdb, therefore change them only if necessary
|
||||
*/
|
||||
|
||||
if (info_cdb->flags & SCST_SMALL_TIMEOUT) {
|
||||
cmd->timeout = TAPE_SMALL_TIMEOUT;
|
||||
} else if (info_cdb->flags & SCST_LONG_TIMEOUT) {
|
||||
cmd->timeout = TAPE_LONG_TIMEOUT;
|
||||
} else {
|
||||
cmd->timeout = TAPE_REG_TIMEOUT;
|
||||
}
|
||||
|
||||
TRACE_DBG("op_name <%s> direct %d flags %d transfer_len %d",
|
||||
info_cdb->op_name,
|
||||
info_cdb->direction, info_cdb->flags, info_cdb->transfer_len);
|
||||
|
||||
if (cmd->cdb[0] == READ_POSITION) {
|
||||
int tclp = cmd->cdb[1] & TCLP_BIT;
|
||||
int long_bit = cmd->cdb[1] & LONG_BIT;
|
||||
int bt = cmd->cdb[1] & BT_BIT;
|
||||
|
||||
if ((tclp == long_bit) && (!bt || !long_bit)) {
|
||||
cmd->bufflen =
|
||||
tclp ? POSITION_LEN_LONG : POSITION_LEN_SHORT;
|
||||
cmd->data_direction = SCST_DATA_READ;
|
||||
} else {
|
||||
cmd->bufflen = 0;
|
||||
cmd->data_direction = SCST_DATA_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (info_cdb->flags & SCST_TRANSFER_LEN_TYPE_FIXED & cmd->cdb[1]) {
|
||||
/*
|
||||
* No need for locks here, since *_detach() can not be called,
|
||||
* when there are existing commands.
|
||||
*/
|
||||
tape = (struct tape_params *)cmd->dev->tgt_dev_specific;
|
||||
cmd->bufflen = info_cdb->transfer_len * tape->block_size;
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: tape_done
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns :
|
||||
*
|
||||
* Description: This is the completion routine for the command,
|
||||
* it is used to extract any necessary information
|
||||
* about a command.
|
||||
********************************************************************/
|
||||
int tape_done(struct scst_cmd *cmd)
|
||||
{
|
||||
int opcode = cmd->cdb[0];
|
||||
int masked_status = cmd->masked_status;
|
||||
struct tape_params *tape;
|
||||
int res = SCST_CMD_STATE_DEFAULT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->sg == NULL))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* SCST sets good defaults for cmd->tgt_resp_flags and cmd->resp_data_len
|
||||
* based on cmd->masked_status and cmd->data_direction, therefore change
|
||||
* them only if necessary
|
||||
*/
|
||||
|
||||
if ((masked_status == GOOD) || (masked_status == CONDITION_GOOD)) {
|
||||
int buffer_size;
|
||||
uint8_t *buffer = NULL;
|
||||
|
||||
switch (opcode) {
|
||||
case MODE_SENSE:
|
||||
case MODE_SELECT:
|
||||
buffer_size = scst_get_buf_first(cmd, &buffer);
|
||||
if (unlikely(buffer_size <= 0)) {
|
||||
PRINT_ERROR_PR("%s: Unable to get the buffer",
|
||||
__FUNCTION__);
|
||||
scst_set_busy(cmd);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case MODE_SENSE:
|
||||
TRACE_DBG("%s", "MODE_SENSE");
|
||||
if ((cmd->cdb[2] & 0xC0) == 0) {
|
||||
/*
|
||||
* No need for locks here, since *_detach()
|
||||
* can not be called, when there are
|
||||
* existing commands.
|
||||
*/
|
||||
tape = (struct tape_params *)cmd->dev->tgt_dev_specific;
|
||||
spin_lock_bh(&tape->tp_lock);
|
||||
if (buffer[3] == 8) {
|
||||
tape->block_size = (buffer[9] << 16) |
|
||||
(buffer[10] << 8) | buffer[11];
|
||||
}
|
||||
tape->medium_type = buffer[1];
|
||||
tape->mode = (buffer[2] & 0x70) >> 4;
|
||||
tape->speed = buffer[2] & 0x0f;
|
||||
tape->density = buffer[4];
|
||||
spin_unlock_bh(&tape->tp_lock);
|
||||
}
|
||||
break;
|
||||
case MODE_SELECT:
|
||||
TRACE_DBG("%s", "MODE_SELECT");
|
||||
/*
|
||||
* No need for locks here, since *_detach() can not be
|
||||
* called, when there are existing commands.
|
||||
*/
|
||||
tape = (struct tape_params *)cmd->dev->tgt_dev_specific;
|
||||
spin_lock_bh(&tape->tp_lock);
|
||||
if (buffer[3] == 8) {
|
||||
tape->block_size =
|
||||
(buffer[9] << 16) | (buffer[10] << 8) |
|
||||
(buffer[11]);
|
||||
}
|
||||
tape->medium_type = buffer[1];
|
||||
tape->mode = (buffer[2] & 0x70) >> 4;
|
||||
tape->speed = buffer[2] & 0x0f;
|
||||
if (buffer[4] != 0x7f)
|
||||
tape->density = buffer[4];
|
||||
spin_unlock_bh(&tape->tp_lock);
|
||||
break;
|
||||
default:
|
||||
/* It's all good */
|
||||
break;
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case MODE_SENSE:
|
||||
case MODE_SELECT:
|
||||
scst_put_buf(cmd, buffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((masked_status == CHECK_CONDITION) &&
|
||||
SCST_SENSE_VALID(cmd->sense_buffer))
|
||||
{
|
||||
TRACE_DBG("%s", "Extended sense");
|
||||
if (opcode == READ_6 && !(cmd->cdb[1] & SILI_BIT) &&
|
||||
(cmd->sense_buffer[2] & 0xe0)) { /* EOF, EOM, or ILI */
|
||||
int TransferLength, Residue = 0;
|
||||
if ((cmd->sense_buffer[2] & 0x0f) == BLANK_CHECK) {
|
||||
cmd->sense_buffer[2] &= 0xcf; /* No need for EOM in this case */
|
||||
}
|
||||
TransferLength = ((cmd->cdb[2] << 16) |
|
||||
(cmd->cdb[3] << 8) | cmd->cdb[4]);
|
||||
/* Compute the residual count */
|
||||
if ((cmd->sense_buffer[0] & 0x80) != 0) {
|
||||
Residue = ((cmd->sense_buffer[3] << 24) |
|
||||
(cmd->sense_buffer[4] << 16) |
|
||||
(cmd->sense_buffer[5] << 8) |
|
||||
cmd->sense_buffer[6]);
|
||||
}
|
||||
TRACE_DBG("Checking the sense key "
|
||||
"sn[2]=%x cmd->cdb[0,1]=%x,%x TransLen/Resid %d/%d",
|
||||
(int) cmd->sense_buffer[2],
|
||||
cmd->cdb[0], cmd->cdb[1], TransferLength,
|
||||
Residue);
|
||||
if (TransferLength > Residue) {
|
||||
int resp_data_len = TransferLength - Residue;
|
||||
if (cmd->cdb[1] & SCST_TRANSFER_LEN_TYPE_FIXED) {
|
||||
/*
|
||||
* No need for locks here, since
|
||||
* *_detach() can not be called, when
|
||||
* there are existing commands.
|
||||
*/
|
||||
tape = (struct tape_params *)cmd->dev->tgt_dev_specific;
|
||||
resp_data_len *= tape->block_size;
|
||||
}
|
||||
scst_set_resp_data_len(cmd, resp_data_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_DBG("cmd->tgt_resp_flags=%x, cmd->resp_data_len=%d, "
|
||||
"res=%d", cmd->tgt_resp_flags, cmd->resp_data_len, res);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Function: tape_exec
|
||||
*
|
||||
* Argument:
|
||||
*
|
||||
* Returns :
|
||||
*
|
||||
* Description: Make SCST do nothing for data READs and WRITES.
|
||||
* Intended for raw line performance testing
|
||||
********************************************************************/
|
||||
int tape_exec(struct scst_cmd *cmd)
|
||||
{
|
||||
int res = SCST_EXEC_NOT_COMPLETED;
|
||||
int opcode = cmd->cdb[0];
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
switch (opcode) {
|
||||
case WRITE_6:
|
||||
case READ_6:
|
||||
res = SCST_EXEC_COMPLETED;
|
||||
cmd->status = 0;
|
||||
cmd->masked_status = 0;
|
||||
cmd->msg_status = 0;
|
||||
cmd->host_status = DID_OK;
|
||||
cmd->driver_status = 0;
|
||||
cmd->completed = 1;
|
||||
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
+1228
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* scst_cdbprobe.h
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Must be included in only one .c file in the project!! */
|
||||
|
||||
#ifndef __SCST_CDBPROBE_H
|
||||
#define __SCST_CDBPROBE_H
|
||||
|
||||
#define SCST_BLOCK_LIMIT_LEN 6
|
||||
|
||||
/*
|
||||
+=====================================-============-======-
|
||||
| Command name | Operation | Type |
|
||||
| | code | |
|
||||
|-------------------------------------+------------+------+
|
||||
|
||||
+=========================================================+
|
||||
|Key: M = command implementation is mandatory. |
|
||||
| O = command implementation is optional. |
|
||||
| V = Vendor-specific |
|
||||
| R = Reserved |
|
||||
| ' '= DON'T use for this device |
|
||||
+=========================================================+
|
||||
*/
|
||||
|
||||
#define SCST_CDB_MANDATORY 'M' /* mandatory */
|
||||
#define SCST_CDB_OPTIONAL 'O' /* optional */
|
||||
#define SCST_CDB_VENDOR 'V' /* vendor */
|
||||
#define SCST_CDB_RESERVED 'R' /* reserved */
|
||||
#define SCST_CDB_NOTSUPP ' ' /* don't use */
|
||||
|
||||
struct scst_sdbops
|
||||
{
|
||||
uint8_t ops; /* SCSI-2 op codes */
|
||||
uint8_t devkey[16]; /* Key for every device type M,O,V,R
|
||||
* type_disk devkey[0]
|
||||
* type_tape devkey[1]
|
||||
* type_printer devkey[2]
|
||||
* type_proseccor devkey[3]
|
||||
* type_worm devkey[4]
|
||||
* type_cdrom devkey[5]
|
||||
* type_scanner devkey[6]
|
||||
* type_mod devkey[7]
|
||||
* type_changer devkey[8]
|
||||
* type_commdev devkey[9]
|
||||
* type_reserv devkey[A]
|
||||
* type_reserv devkey[B]
|
||||
* type_raid devkey[C]
|
||||
* type_enclosure devkey[D]
|
||||
* type_reserv devkey[E]
|
||||
* type_reserv devkey[F]
|
||||
*/
|
||||
const char *op_name; /* SCSI-2 op codes full name */
|
||||
uint8_t direction; /* init --> target: SCST_DATA_WRITE
|
||||
* target --> init: SCST_DATA_READ
|
||||
*/
|
||||
uint8_t size_field_len; /* how many bytes using for data */
|
||||
uint8_t fixed; /* opcode -- have fixed variable bit ??? */
|
||||
uint8_t flag1; /* Note 1 */
|
||||
uint8_t flag2;
|
||||
};
|
||||
|
||||
static int scst_scsi_op_list[256];
|
||||
|
||||
static const struct scst_sdbops scst_scsi_op_table[] = {
|
||||
/*
|
||||
* +-------------------> TYPE_IS_DISK (0)
|
||||
* |
|
||||
* |+------------------> TYPE_IS_TAPE (1)
|
||||
* ||
|
||||
* || +----------------> TYPE_IS_PROCESSOR (3)
|
||||
* || |
|
||||
* || | +--------------> TYPE_IS_CDROM (5)
|
||||
* || | |
|
||||
* || | | +------------> TYPE_IS_MOD (7)
|
||||
* || | | |
|
||||
* || | | |+-----------> TYPE_IS_CHANGER (8)
|
||||
* || | | ||
|
||||
* || | | || +-------> TYPE_IS_RAID (C)
|
||||
* || | | || |
|
||||
* || | | || |
|
||||
* 0123456789ABCDEF ---> TYPE_IS_???? */
|
||||
{0x00, "MMMMMMMMMMMMMMMM", "TEST UNIT READY",
|
||||
SCST_DATA_NONE, 0, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x01, " M ", "REWIND",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x01, "O V OO OO ", "REZERO UNIT",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x02, "VVVVVV V ", "REQUEST BLOCK ADDR",
|
||||
SCST_DATA_NONE, 0, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x03, "MMMMMMMMMMMMMMMM", "REQUEST SENSE",
|
||||
SCST_DATA_READ, 1, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x04, "M O O ", "FORMAT UNIT",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0}, /*-*/
|
||||
{0x04, " O ", "FORMAT",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x05, "VMVVVV V ", "READ BLOCK LIMITS",
|
||||
SCST_DATA_READ, 1, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x06, "VVVVVV V ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x07, " O ", "INITIALIZE ELEMENT STATUS",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x07, "OVV O OV ", "REASSIGN BLOCKS",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0}, /*-*/
|
||||
{0x08, "O ", "READ(6)",
|
||||
SCST_DATA_READ, 1, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x08, " MV OO OV ", "READ(6)",
|
||||
SCST_DATA_READ, 3, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x08, " M ", "GET MESSAGE(6)",
|
||||
SCST_DATA_READ, 3, 0, 0, 0},
|
||||
{0x08, " O ", "RECEIVE",
|
||||
SCST_DATA_READ, 3, 0, 0, 0},
|
||||
{0x09, "VVVVVV V ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x0A, "O ", "WRITE(6)",
|
||||
SCST_DATA_WRITE, 1, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x0A, " M O OV ", "WRITE(6)",
|
||||
SCST_DATA_WRITE, 3, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x0A, " M ", "PRINT",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x0A, " M ", "SEND MESSAGE(6)",
|
||||
SCST_DATA_WRITE, 3, 0, 0, 0},
|
||||
{0x0A, " M ", "SEND(6)",
|
||||
SCST_DATA_WRITE, 3, 0, 0, 0},
|
||||
{0x0B, "O OO OV ", "SEEK(6)",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x0B, " ", "TRACK SELECT",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x0B, " O ", "SLEW AND PRINT",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x0C, "VVVVVV V ", "SEEK BLOCK",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x0D, "VVVVVV V ", "PARTITION",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x0E, "VVVVVV V ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x0F, "VOVVVV V ", "READ REVERSE",
|
||||
SCST_DATA_READ, 3, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x10, "VM V V ", "WRITE FILEMARKS",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x10, " O O ", "SYNCHRONIZE BUFFER",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x11, "VMVVVV ", "SPACE",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x12, "MMMMMMMMMMMMMMMM", "INQUIRY",
|
||||
SCST_DATA_READ, 1, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x13, "VOVVVV ", "VERIFY(6)",
|
||||
SCST_DATA_WRITE, 3, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x14, "VOOVVV ", "RECOVER BUFFERED DATA",
|
||||
SCST_DATA_READ, 3, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x15, "OMOOOOOOOOOOOOOO", "MODE SELECT(6)",
|
||||
SCST_DATA_WRITE, 1, 0, 0, 0}, /*N6! */
|
||||
{0x16, "MMMMMMMMMMMMMMMM", "RESERVE",
|
||||
SCST_DATA_NONE, 0, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x17, "MMMMMMMMMMMMMMMM", "RELEASE",
|
||||
SCST_DATA_NONE, 0, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x18, "OOOOOOOO ", "COPY",
|
||||
SCST_DATA_WRITE, 1, SCST_LONG_TIMEOUT, 0, 0}, /*N5! */
|
||||
{0x19, "VMVVVV ", "ERASE",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x1A, "OMOOOOOOOOOOOOOO", "MODE SENSE(6)",
|
||||
SCST_DATA_READ, 1, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x1B, " O ", "SCAN",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x1B, " O ", "LOAD UNLOAD",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x1B, " O ", "STOP PRINT",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x1B, "O OO O O ", "STOP START UNIT",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x1C, "OOOOOOOOOOOOOOOO", "RECEIVE DIAGNOSTIC RESULTS",
|
||||
SCST_DATA_READ, 1, 0, 0, 0},
|
||||
{0x1D, "MMMMMMMMMMMMMMMM", "SEND DIAGNOSTIC",
|
||||
SCST_DATA_WRITE, 1, 0, 0, 0},
|
||||
{0x1E, "OOOOOOOOOOOOOOOO", "PREVENT ALLOW MEDIUM REMOVAL",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x1F, " O ", "PORT STATUS",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
/* 10-bytes length CDB */
|
||||
{0x20, "V VV V ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x21, "V VV V ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x22, "V VV V ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x23, "V VV V ", "READ FORMAT CAPACITY",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0x24, "V VVM ", "SET WINDOW",
|
||||
SCST_DATA_WRITE, 3, 0, 0, 0}, /*-*/
|
||||
{0x25, "M MM M ", "READ CAPACITY",
|
||||
SCST_DATA_READ, 0, 0, 0, 0},
|
||||
{0x25, " O ", "GET WINDOW",
|
||||
SCST_DATA_READ, 3, 0, 0, 0},
|
||||
{0x26, "V VV ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x27, "V VV ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x28, "M MMMM ", "READ(10)",
|
||||
SCST_DATA_READ, 2, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x28, " O ", "GET MESSAGE(10)",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0x29, "V VV O ", "READ GENERATION",
|
||||
SCST_DATA_READ, 1, 0, 0, 0},
|
||||
{0x2A, "O MO M ", "WRITE(10)",
|
||||
SCST_DATA_WRITE, 2, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x2A, " O ", "SEND MESSAGE(10)",
|
||||
SCST_DATA_WRITE, 2, 0, 0, 0},
|
||||
{0x2A, " O ", "SEND(10)",
|
||||
SCST_DATA_WRITE, 2, 0, 0, 0},
|
||||
{0x2B, " O ", "LOCATE",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x2B, " O ", "POSITION TO ELEMENT",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x2B, "O OO O ", "SEEK(10)",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x2C, "V O O ", "ERASE(10)",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x2D, "V O O ", "READ UPDATED BLOCK",
|
||||
SCST_DATA_READ, 1, 0, 0, 0}, /*N2! */
|
||||
{0x2E, "O OO O ", "WRITE AND VERIFY(10)",
|
||||
SCST_DATA_WRITE, 2, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x2F, "O OO O ", "VERIFY(10)",
|
||||
SCST_DATA_WRITE, 2, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
/*-*/
|
||||
{0x30, "O OO O ", "SEARCH DATA HIGH(10)",
|
||||
SCST_DATA_NONE, 0, SCST_UNKNOWN_LENGTH, 1, 0}, /*-*/
|
||||
{0x31, " O ", "OBJECT POSITION",
|
||||
SCST_DATA_NONE, 0, SCST_UNKNOWN_LENGTH, 1, 0}, /*N1! */
|
||||
{0x31, "O OO O ", "SEARCH DATA EQUAL(10)",
|
||||
SCST_DATA_NONE, 0, SCST_UNKNOWN_LENGTH, 1, 0}, /*-*/
|
||||
{0x32, "O OO O ", "SEARCH DATA LOW(10)",
|
||||
SCST_DATA_NONE, 0, SCST_UNKNOWN_LENGTH, 1, 0}, /*-*/
|
||||
{0x33, "O OO O ", "SET LIMITS(10)",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x34, " O ", "READ POSITION",
|
||||
SCST_DATA_READ, 1, SCST_SMALL_TIMEOUT, 0, 0}, /*N4! */
|
||||
{0x34, " O ", "GET DATA BUFFER STATUS",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0x34, "O OO O ", "PRE-FETCH",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x35, "O OO O ", "SYNCHRONIZE CACHE",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x36, "O OO O ", "LOCK UNLOCK CACHE",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x37, "O O ", "READ DEFECT DATA(10)",
|
||||
SCST_DATA_READ, 1, 0, 0, 0},
|
||||
{0x38, " O O ", "MEDIUM SCAN",
|
||||
SCST_DATA_READ, 1, 0, 0, 0}, /*-*/
|
||||
{0x39, "OOOOOOOO ", "COMPARE",
|
||||
SCST_DATA_WRITE, 1, 0, 0, 0}, /*N3! */
|
||||
{0x3A, "OOOOOOOO ", "COPY AND VERIFY",
|
||||
SCST_DATA_WRITE, 1, 0, 0, 0}, /*N3!? */
|
||||
{0x3B, "OOOOOOOOOOOOOOOO", "WRITE BUFFER",
|
||||
SCST_DATA_WRITE, 3, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x3C, "OOOOOOOOOOOOOOOO", "READ BUFFER",
|
||||
SCST_DATA_READ, 3, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x3D, " O O ", "UPDATE BLOCK",
|
||||
SCST_DATA_WRITE, 1, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0}, /*N2! */
|
||||
{0x3E, "O OO O ", "READ LONG",
|
||||
SCST_DATA_READ, 1, 0, 0, 0}, /*N6! */
|
||||
{0x3F, "O O O ", "WRITE LONG",
|
||||
SCST_DATA_WRITE, 1, 0, 0, 0}, /*N6! */
|
||||
{0x40, "OOOOOOOOOO ", "CHANGE DEFINITION",
|
||||
SCST_DATA_WRITE, 1, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x41, "O O ", "WRITE SAME",
|
||||
SCST_DATA_WRITE, 0, SCST_UNKNOWN_LENGTH, 1, 0}, /*N2! */
|
||||
{0x42, " O ", "READ SUB-CHANNEL",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0x43, " O ", "READ TOC/PMA/ATIP",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0x44, " O ", "READ HEADER",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0x45, " O ", "PLAY AUDIO(10)",
|
||||
SCST_DATA_READ, 2, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0}, /*N8! */
|
||||
{0x46, " O ", "GET CONFIGURATION",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0x47, " O ", "PLAY AUDIO MSF",
|
||||
SCST_DATA_READ, 0, SCST_UNKNOWN_LENGTH, 1, 0}, /*? */
|
||||
{0x48, " O ", "PLAY AUDIO TRACK INDEX",
|
||||
SCST_DATA_READ, 0, SCST_UNKNOWN_LENGTH, 1, 0}, /*? */
|
||||
{0x49, " O ", "PLAY TRACK RELATIVE(10)",
|
||||
SCST_DATA_READ, 2, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0}, /*N8! */
|
||||
{0x4A, " O ", "GET EVENT STATUS NOTIFICATION",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0x4B, " O ", "PAUSE/RESUME",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x4C, "OOOOOOOOOOOOOOOO", "LOG SELECT",
|
||||
SCST_DATA_WRITE, 2, SCST_SMALL_TIMEOUT, 0, 0}, /*N6! */
|
||||
{0x4D, "OOOOOOOOOOOOOOOO", "LOG SENSE",
|
||||
SCST_DATA_READ, 2, SCST_SMALL_TIMEOUT, 0, 0}, /*N6! */
|
||||
{0x4E, " O ", "STOP PLAY/SCAN",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x4F, " ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x50, " ", "XDWRITE",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0}, /*-*/
|
||||
{0x51, " O ", "READ DISC INFORMATION",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0x51, " ", "XPWRITE",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0}, /*-*/
|
||||
{0x52, " O ", "READ TRACK INFORMATION",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0x53, " O ", "RESERVE TRACK",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x54, " O ", "SEND OPC INFORMATION",
|
||||
SCST_DATA_WRITE, 2, 0, 0, 0},
|
||||
{0x55, "OOOOOOOOOOOOOOOO", "MODE SELECT(10)",
|
||||
SCST_DATA_WRITE, 2, 0, 0, 0},
|
||||
{0x56, "OOOOOOOOOOOOOOOO", "RESERVE(10)",
|
||||
SCST_DATA_NONE, 0, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x57, "OOOOOOOOOOOOOOOO", "RELEASE(10)",
|
||||
SCST_DATA_NONE, 0, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x58, " O ", "REPAIR TRACK",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x59, " ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x5A, "OOOOOOOOOOOOOOOO", "MODE SENSE(10)",
|
||||
SCST_DATA_READ, 2, SCST_SMALL_TIMEOUT, 0, 0},
|
||||
{0x5B, " O ", "CLOSE TRACK/SESSION",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x5C, " O ", "READ BUFFER CAPACITY",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0x5D, " O ", "SEND CUE SHEET",
|
||||
SCST_DATA_WRITE, 3, 0, 0, 0},
|
||||
{0x5E, " ", "PERSISTENT_RESERV_IN",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x5F, " ", "PERSISTENT_RESERV_OUT",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
|
||||
/* 16-bytes length CDB */
|
||||
{0x80, "O OO O ", "XDWRITE EXTENDED",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x80, " M ", "WRITE FILEMARKS",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x81, "O OO O ", "REBUILD",
|
||||
SCST_DATA_WRITE, 4, 0, 0, 0},
|
||||
{0x82, "O OO O ", "REGENERATE",
|
||||
SCST_DATA_WRITE, 4, 0, 0, 0},
|
||||
{0x83, "OOOOOOOOOOOOOOOO", "EXTENDED COPY",
|
||||
SCST_DATA_WRITE, 4, 0, 0, 0},
|
||||
{0x84, "OOOOOOOOOOOOOOOO", "RECEIVE COPY RESULT",
|
||||
SCST_DATA_WRITE, 4, 0, 0, 0},
|
||||
{0x86, "OOOOOOOOOO ", "ACCESS CONTROL IN",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x87, "OOOOOOOOOO ", "ACCESS CONTROL OUT",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x88, "M MMMM ", "READ(16)",
|
||||
SCST_DATA_READ, 4, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x8A, "O OO O ", "WRITE(16)",
|
||||
SCST_DATA_WRITE, 4, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0x8C, "OOOOOOOOOO ", "READ ATTRIBUTE",
|
||||
SCST_DATA_READ, 4, 0, 0, 0},
|
||||
{0x8D, "OOOOOOOOOO ", "WRITE ATTRIBUTE",
|
||||
SCST_DATA_WRITE, 4, 0, 0, 0},
|
||||
{0x8E, "O OO O ", "WRITE AND VERIFY(16)",
|
||||
SCST_DATA_WRITE, 4, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
/*-*/
|
||||
{0x8F, "O OO O ", "VERIFY(16)",
|
||||
SCST_DATA_WRITE, 4, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0}, /*? */
|
||||
{0x90, "O OO O ", "PRE-FETCH(16)",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x91, "O OO O ", "SYNCHRONIZE CACHE(16)",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x91, " M ", "SPACE(16)",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x92, "O OO O ", "LOCK UNLOCK CACHE(16)",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0x92, " O ", "LOCATE(16)",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0x93, "O O ", "WRITE SAME(16)",
|
||||
SCST_DATA_WRITE, 4, SCST_UNKNOWN_LENGTH, 1, 0}, /*N2! */
|
||||
{0x93, " M ", "ERASE(16)",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
|
||||
/* 12-bytes length CDB */
|
||||
{0xA0, "VVVVVVVVVV M ", "REPORT LUN",
|
||||
SCST_DATA_READ, 4, SCST_SMALL_TIMEOUT, 0, 0}, /*N7! */
|
||||
{0xA1, " O ", "BLANK",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0xA2, " ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0xA3, " O ", "SEND KEY",
|
||||
SCST_DATA_WRITE, 2, 0, 0, 0},
|
||||
{0xA3, "OOOOO OOOO ", "REPORT DEVICE IDENTIDIER",
|
||||
SCST_DATA_READ, 4, 0, 0, 0},
|
||||
{0xA3, " M ", "MAINTENANCE(IN)",
|
||||
SCST_DATA_READ, 4, 0, 0, 0},
|
||||
{0xA4, " O ", "REPORT KEY",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0xA4, " O ", "MAINTENANCE(OUT)",
|
||||
SCST_DATA_WRITE, 4, 0, 0, 0},
|
||||
{0xA5, " M ", "MOVE MEDIUM",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0xA5, " O ", "PLAY AUDIO(12)",
|
||||
SCST_DATA_READ, 4, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0}, /*N8! */
|
||||
{0xA6, " O O ", "EXCHANGE/LOAD/UNLOAD MEDIUM",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0xA7, " O ", "SET READ AHEAD",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0xA8, " O ", "GET MESSAGE(12)",
|
||||
SCST_DATA_READ, 4, 0, 0, 0},
|
||||
{0xA8, "O OO O ", "READ(12)",
|
||||
SCST_DATA_READ, 4, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0xA9, " O ", "PLAY TRACK RELATIVE(12)",
|
||||
SCST_DATA_READ, 4, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0}, /*N8*! */
|
||||
{0xAA, "O OO O ", "WRITE(12)",
|
||||
SCST_DATA_WRITE, 4, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0xAA, " O ", "SEND MESSAGE(12)",
|
||||
SCST_DATA_WRITE, 4, 0, 0, 0},
|
||||
{0xAB, " ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0xAC, " O ", "ERASE(12)",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0xAC, " O ", "GET PERFORMANCE",
|
||||
SCST_DATA_READ, 0, SCST_UNKNOWN_LENGTH, 1, 0},
|
||||
{0xAD, " O ", "READ DVD STRUCTURE",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0xAE, "O OO O ", "WRITE AND VERIFY(12)",
|
||||
SCST_DATA_WRITE, 4, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
/*-*/
|
||||
{0xAF, "O OO O ", "VERIFY(12)",
|
||||
SCST_DATA_WRITE, 4, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0}, /*? */
|
||||
{0xB0, " OO O ", "SEARCH DATA HIGH(12)",
|
||||
SCST_DATA_WRITE, 1, 0, 0, 0}, /*N9! */
|
||||
{0xB1, " OO O ", "SEARCH DATA EQUAL(12)",
|
||||
SCST_DATA_WRITE, 1, 0, 0, 0}, /*N9! */
|
||||
{0xB2, " OO O ", "SEARCH DATA LOW(12)",
|
||||
SCST_DATA_WRITE, 1, 0, 0, 0}, /*N9! */
|
||||
{0xB3, " OO O ", "SET LIMITS(12)",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0xB4, " ", "",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0xB5, " O ", "REQUEST VOLUME ELEMENT ADDRESS",
|
||||
SCST_DATA_READ, 1, 0, 0, 0},
|
||||
{0xB6, " O ", "SEND VOLUME TAG",
|
||||
SCST_DATA_WRITE, 1, 0, 0, 0},
|
||||
{0xB6, " O ", "SET STREAMING",
|
||||
SCST_DATA_WRITE, 0, 0, 0, 0},
|
||||
{0xB7, " O ", "READ DEFECT DATA(12)",
|
||||
SCST_DATA_READ, 1, 0, 0, 0},
|
||||
{0xB8, " O ", "READ ELEMENT STATUS",
|
||||
SCST_DATA_READ, 3, 0, 0, 0}, /*N10 */
|
||||
{0xB9, " O ", "READ CD MSF",
|
||||
SCST_DATA_READ, 0, SCST_UNKNOWN_LENGTH, 1, 0},
|
||||
{0xBA, " O ", "SCAN",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0},
|
||||
{0xBA, " O ", "REDUNDANCY GROUP(IN)",
|
||||
SCST_DATA_READ, 4, 0, 0, 0},
|
||||
{0xBB, " O ", "SET SPEED",
|
||||
SCST_DATA_NONE, 0, 0, 0, 0},
|
||||
{0xBB, " O ", "REDUNDANCY GROUP(OUT)",
|
||||
SCST_DATA_WRITE, 4, 0, 0, 0},
|
||||
{0xBC, " O ", "SPARE(IN)",
|
||||
SCST_DATA_READ, 4, 0, 0, 0},
|
||||
{0xBD, " O ", "MECHANISM STATUS",
|
||||
SCST_DATA_READ, 2, 0, 0, 0},
|
||||
{0xBD, " O ", "SPARE(OUT)",
|
||||
SCST_DATA_WRITE, 4, 0, 0, 0},
|
||||
{0xBE, " O ", "READ CD",
|
||||
SCST_DATA_READ, 4, SCST_TRANSFER_LEN_TYPE_FIXED, 0, 0},
|
||||
{0xBE, " O ", "VOLUME SET(IN)",
|
||||
SCST_DATA_READ, 4, 0, 0, 0},
|
||||
{0xBF, " O ", "SEND DVD STRUCTUE",
|
||||
SCST_DATA_WRITE, 2, 0, 0, 0},
|
||||
{0xBF, " O ", "VOLUME SET(OUT)",
|
||||
SCST_DATA_WRITE, 4, 0, 0, 0},
|
||||
{0xE7, " V ", "INIT ELEMENT STATUS WRANGE",
|
||||
SCST_DATA_NONE, 0, SCST_LONG_TIMEOUT, 0, 0}
|
||||
};
|
||||
|
||||
/* Notes:
|
||||
N1. - Unknown data len
|
||||
N2. - Read/Write 1 block data
|
||||
N3. - SCSI-2 1 byte len, SCSI-3 may be upto 3 bytes AND other location
|
||||
N4. - Answer 20-bytes block.
|
||||
N5. - CDB get param list len
|
||||
N6. - len is bytes only
|
||||
N7. - SCSI-3 Report Lun?
|
||||
N8. - fixed length audio mode blocks for CD/DVD use MODE SENSE
|
||||
N9. - 14-BYTES header + (n * patterns) full length variable
|
||||
N10. - SCSI-2 use 1byte, mt.c (SCSI-3)? use 3 bytes
|
||||
|
||||
Unknown op's:
|
||||
#define SCSIOP_XDWRITE_EXTENDED 0x80
|
||||
#define SCSIOP_REBUILD 0x81
|
||||
#define SCSIOP_EA 0xea
|
||||
#define WRITE_LONG_2 0xea
|
||||
*/
|
||||
|
||||
#define SCST_CDB_TBL_SIZE ((sizeof(scst_scsi_op_table)/sizeof(struct scst_sdbops)))
|
||||
|
||||
#endif /* __SCST_CDBPROBE_H */
|
||||
+2351
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,565 @@
|
||||
/*
|
||||
* scst_sgv_pool.c
|
||||
*
|
||||
* Copyright (C) 2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/string.h>
|
||||
|
||||
#ifdef SCST_HIGHMEM
|
||||
#include <linux/highmem.h>
|
||||
#endif
|
||||
|
||||
#include "scsi_tgt.h"
|
||||
#include "scst_debug.h"
|
||||
#include "scst_priv.h"
|
||||
#include "scst_mem.h"
|
||||
|
||||
/*
|
||||
* This implementation of sgv_pool is not the best, because the SLABs could get
|
||||
* fragmented and too much undesirable memory could be kept, plus
|
||||
* under memory pressure the cached objects could be purged too quickly.
|
||||
* From other side it's simple, works well, and doesn't require any modifications
|
||||
* of the existing SLAB code.
|
||||
*/
|
||||
|
||||
atomic_t sgv_big_total_alloc;
|
||||
|
||||
static int scst_check_clustering(struct scatterlist *sg, int cur, int hint)
|
||||
{
|
||||
int res = -1;
|
||||
int i = hint;
|
||||
unsigned long pfn_cur = page_to_pfn(sg[cur].page);
|
||||
int len_cur = sg[cur].length;
|
||||
unsigned long pfn_cur_next = pfn_cur + (len_cur >> PAGE_SHIFT);
|
||||
int full_page_cur = (len_cur & (PAGE_SIZE - 1)) == 0;
|
||||
unsigned long pfn, pfn_next, full_page;
|
||||
|
||||
#ifdef SCST_HIGHMEM
|
||||
if (page >= highmem_start_page) {
|
||||
TRACE_MEM("%s", "HIGHMEM page allocated, no clustering")
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
TRACE_MEM("pfn_cur %ld, pfn_cur_next %ld, len_cur %d, full_page_cur %d",
|
||||
pfn_cur, pfn_cur_next, len_cur, full_page_cur);
|
||||
#endif
|
||||
|
||||
/* check the hint first */
|
||||
if (i >= 0) {
|
||||
pfn = page_to_pfn(sg[i].page);
|
||||
pfn_next = pfn + (sg[i].length >> PAGE_SHIFT);
|
||||
full_page = (sg[i].length & (PAGE_SIZE - 1)) == 0;
|
||||
|
||||
if ((pfn == pfn_cur_next) && full_page_cur)
|
||||
goto out_head;
|
||||
|
||||
if ((pfn_next == pfn_cur) && full_page)
|
||||
goto out_tail;
|
||||
}
|
||||
|
||||
/* ToDo: implement more intelligent search */
|
||||
for (i = cur - 1; i >= 0; i--) {
|
||||
pfn = page_to_pfn(sg[i].page);
|
||||
pfn_next = pfn + (sg[i].length >> PAGE_SHIFT);
|
||||
full_page = (sg[i].length & (PAGE_SIZE - 1)) == 0;
|
||||
|
||||
if ((pfn == pfn_cur_next) && full_page_cur)
|
||||
goto out_head;
|
||||
|
||||
if ((pfn_next == pfn_cur) && full_page)
|
||||
goto out_tail;
|
||||
}
|
||||
|
||||
out:
|
||||
return res;
|
||||
|
||||
out_tail:
|
||||
TRACE_MEM("SG segment %d will be tail merged with segment %d", cur, i);
|
||||
sg[i].length += len_cur;
|
||||
memset(&sg[cur], 0, sizeof(sg[cur]));
|
||||
res = i;
|
||||
goto out;
|
||||
|
||||
out_head:
|
||||
TRACE_MEM("SG segment %d will be head merged with segment %d", cur, i);
|
||||
sg[i].page = sg[cur].page;
|
||||
sg[i].length += len_cur;
|
||||
memset(&sg[cur], 0, sizeof(sg[cur]));
|
||||
res = i;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void sgv_free_sg(struct sgv_pool_obj *obj)
|
||||
{
|
||||
int i;
|
||||
|
||||
TRACE_MEM("obj=%p, sg_count=%d", obj, obj->sg_count);
|
||||
|
||||
for (i = 0; i < obj->sg_count; i++) {
|
||||
struct page *p = obj->entries[i].page;
|
||||
int len = obj->entries[i].length;
|
||||
int pages =
|
||||
(len >> PAGE_SHIFT) + ((len & ~PAGE_MASK) != 0);
|
||||
|
||||
TRACE_MEM("page %lx, len %d, pages %d",
|
||||
(unsigned long)p, len, pages);
|
||||
|
||||
while (pages > 0) {
|
||||
int order = 0;
|
||||
|
||||
/*
|
||||
* __free_pages() doesn't like freeing pages with not that order with
|
||||
* which they were allocated, so disable this small optimization.
|
||||
*/
|
||||
#if 0
|
||||
if (len > 0) {
|
||||
while(((1 << order) << PAGE_SHIFT) < len)
|
||||
order++;
|
||||
len = 0;
|
||||
}
|
||||
#endif
|
||||
TRACE_MEM("free_pages(): order %d, page %lx",
|
||||
order, (unsigned long)p);
|
||||
|
||||
__free_pages(p, order);
|
||||
|
||||
pages -= 1 << order;
|
||||
p += 1 << order;
|
||||
}
|
||||
}
|
||||
obj->sg_count = 0;
|
||||
}
|
||||
|
||||
static int sgv_alloc_sg(struct sgv_pool_obj *obj, int pages,
|
||||
unsigned long mask, int clustered)
|
||||
{
|
||||
int res = 0;
|
||||
int pg, i, j;
|
||||
int merged = -1;
|
||||
|
||||
TRACE_MEM("pages=%d, clustered=%d", pages, clustered);
|
||||
|
||||
#if 0
|
||||
mask |= __GFP_COLD;
|
||||
#endif
|
||||
#ifdef SCST_STRICT_SECURITY
|
||||
mask |= __GFP_ZERO;
|
||||
#endif
|
||||
|
||||
obj->sg_count = 0;
|
||||
for (pg = 0; pg < pages; pg++) {
|
||||
#ifdef DEBUG_OOM
|
||||
if (((scst_random() % 100) == 55))
|
||||
obj->entries[obj->sg_count].page = NULL;
|
||||
else
|
||||
#endif
|
||||
obj->entries[obj->sg_count].page = alloc_pages(mask, 0);
|
||||
if (obj->entries[obj->sg_count].page == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of "
|
||||
"sgv_pool_obj page failed");
|
||||
res = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
obj->entries[obj->sg_count].length = PAGE_SIZE;
|
||||
if (clustered) {
|
||||
merged = scst_check_clustering(obj->entries,
|
||||
obj->sg_count, merged);
|
||||
if (merged == -1)
|
||||
obj->sg_count++;
|
||||
} else
|
||||
obj->sg_count++;
|
||||
TRACE_MEM("pg=%d, merged=%d, sg_count=%d", pg, merged,
|
||||
obj->sg_count);
|
||||
}
|
||||
|
||||
if (clustered) {
|
||||
pg = 0;
|
||||
for (i = 0; i < pages; i++) {
|
||||
int n = obj->entries[i].length >> PAGE_SHIFT;
|
||||
obj->trans_tbl[i].pg_count = pg;
|
||||
for (j = 0; j < n; j++)
|
||||
obj->trans_tbl[pg++].sg_num = i+1;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_MEM("res=%d, sg_count=%d", res, obj->sg_count);
|
||||
return res;
|
||||
|
||||
out_free:
|
||||
sgv_free_sg(obj);
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct sgv_pool_obj *sgv_pool_alloc_big(int size, int pages,
|
||||
unsigned long mask, int *count, int clustered)
|
||||
{
|
||||
struct sgv_pool_obj *obj;
|
||||
int elen, cnt = 0;
|
||||
|
||||
elen = sizeof(*obj) + pages * (sizeof(obj->entries[0]) +
|
||||
clustered ? sizeof(obj->trans_tbl[0]) : 0);
|
||||
obj = kzalloc(elen, mask & ~(__GFP_HIGHMEM|GFP_DMA));
|
||||
if (obj == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "Allocation big of sgv_pool_obj "
|
||||
"failed (elen=%d, size=%d)", elen, size);
|
||||
goto out;
|
||||
}
|
||||
obj->entries = (struct scatterlist*)&obj->trans_tbl[pages];
|
||||
|
||||
atomic_inc(&sgv_big_total_alloc);
|
||||
|
||||
if (sgv_alloc_sg(obj, pages, mask, clustered) != 0)
|
||||
goto out_free;
|
||||
cnt = obj->sg_count;
|
||||
if (size & ~PAGE_MASK) {
|
||||
obj->entries[cnt-1].length -=
|
||||
PAGE_SIZE - (size & ~PAGE_MASK);
|
||||
}
|
||||
*count = cnt;
|
||||
|
||||
out:
|
||||
TRACE_MEM("obj=%p (count=%d)", obj, cnt);
|
||||
return obj;
|
||||
|
||||
out_free:
|
||||
kfree(obj);
|
||||
obj = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
void __sgv_pool_free_big(struct sgv_pool_obj *obj)
|
||||
{
|
||||
TRACE_MEM("obj=%p", obj);
|
||||
sgv_free_sg(obj);
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
struct sgv_pool_obj *sgv_pool_alloc(struct sgv_pool *pool, int size,
|
||||
unsigned long mask, int *count)
|
||||
{
|
||||
struct sgv_pool_obj *obj;
|
||||
int order, pages, cnt, sg;
|
||||
|
||||
if (unlikely(size == 0))
|
||||
return NULL;
|
||||
|
||||
pages = (size >> PAGE_SHIFT) + ((size & ~PAGE_MASK) != 0);
|
||||
order = get_order(size);
|
||||
|
||||
TRACE_MEM("size=%d, pages=%d, order=%d", size, pages, order);
|
||||
|
||||
if (order >= SGV_POOL_ELEMENTS) {
|
||||
obj = NULL;
|
||||
if (mask & GFP_ATOMIC)
|
||||
goto out;
|
||||
obj = sgv_pool_alloc_big(size, pages, mask, count,
|
||||
pool->clustered);
|
||||
goto out;
|
||||
}
|
||||
|
||||
obj = kmem_cache_alloc(pool->caches[order],
|
||||
mask & ~(__GFP_HIGHMEM|GFP_DMA));
|
||||
if (obj == NULL) {
|
||||
if (!(mask & GFP_ATOMIC)) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "Allocation of sgv_pool_obj "
|
||||
"failed (size %d)", size);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (obj->owner_cache != pool->caches[order]) {
|
||||
int esz, epg, eorder;
|
||||
|
||||
if (mask & GFP_ATOMIC)
|
||||
goto out_free;
|
||||
|
||||
esz = (1 << order) * sizeof(obj->entries[0]);
|
||||
epg = (esz >> PAGE_SHIFT) + ((esz & ~PAGE_MASK) != 0);
|
||||
eorder = get_order(esz);
|
||||
TRACE_MEM("Brand new sgv_obj %p (esz=%d, epg=%d, eorder=%d)",
|
||||
obj, esz, epg, eorder);
|
||||
|
||||
obj->eorder = eorder;
|
||||
obj->entries = (struct scatterlist*)__get_free_pages(
|
||||
mask|__GFP_ZERO, eorder);
|
||||
if (obj->entries == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "Allocation of sgv_pool_obj "
|
||||
"SG vector order %d failed", eorder);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (sgv_alloc_sg(obj, (1 << order), mask,
|
||||
pool->clustered) != 0)
|
||||
goto out_free_entries;
|
||||
|
||||
obj->owner_cache = pool->caches[order];
|
||||
} else {
|
||||
TRACE_MEM("Cached sgv_obj %p", obj);
|
||||
atomic_inc(&pool->acc.hit_alloc);
|
||||
atomic_inc(&pool->cache_acc[order].hit_alloc);
|
||||
}
|
||||
atomic_inc(&pool->acc.total_alloc);
|
||||
atomic_inc(&pool->cache_acc[order].total_alloc);
|
||||
if (pool->clustered)
|
||||
cnt = obj->trans_tbl[pages-1].sg_num;
|
||||
else
|
||||
cnt = pages;
|
||||
sg = cnt-1;
|
||||
obj->orig_sg = sg;
|
||||
obj->orig_length = obj->entries[sg].length;
|
||||
if (pool->clustered) {
|
||||
obj->entries[sg].length =
|
||||
(pages - obj->trans_tbl[sg].pg_count) << PAGE_SHIFT;
|
||||
}
|
||||
if (size & ~PAGE_MASK) {
|
||||
obj->entries[sg].length -= PAGE_SIZE - (size & ~PAGE_MASK);
|
||||
}
|
||||
*count = cnt;
|
||||
|
||||
TRACE_MEM("sgv_obj=%p (size=%d, pages=%d, "
|
||||
"sg_count=%d, count=%d, last_len=%d)", obj, size, pages,
|
||||
obj->sg_count, *count, obj->entries[obj->orig_sg].length);
|
||||
|
||||
out:
|
||||
return obj;
|
||||
|
||||
out_free_entries:
|
||||
free_pages((unsigned long)obj->entries, obj->eorder);
|
||||
obj->entries = NULL;
|
||||
|
||||
out_free:
|
||||
kmem_cache_free(pool->caches[order], obj);
|
||||
obj = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void sgv_ctor(void *data, kmem_cache_t *c, unsigned long flags)
|
||||
{
|
||||
struct sgv_pool_obj *obj = data;
|
||||
|
||||
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) !=
|
||||
SLAB_CTOR_CONSTRUCTOR)
|
||||
return;
|
||||
|
||||
TRACE_MEM("Constructor for sgv_obj %p", obj);
|
||||
memset(obj, 0, sizeof(*obj));
|
||||
}
|
||||
|
||||
static void __sgv_dtor(void *data, int pages)
|
||||
{
|
||||
struct sgv_pool_obj *obj = data;
|
||||
TRACE_MEM("Destructor for sgv_obj %p", obj);
|
||||
if (obj->entries) {
|
||||
sgv_free_sg(obj);
|
||||
free_pages((unsigned long)obj->entries, obj->eorder);
|
||||
}
|
||||
}
|
||||
|
||||
#define SGV_DTOR_NAME(order) sgv_dtor##order
|
||||
#define SGV_DTOR(order) static void sgv_dtor##order(void *d, kmem_cache_t *k, \
|
||||
unsigned long f) { __sgv_dtor(d, 1 << order); }
|
||||
|
||||
SGV_DTOR(0);
|
||||
SGV_DTOR(1);
|
||||
SGV_DTOR(2);
|
||||
SGV_DTOR(3);
|
||||
SGV_DTOR(4);
|
||||
SGV_DTOR(5);
|
||||
SGV_DTOR(6);
|
||||
SGV_DTOR(7);
|
||||
SGV_DTOR(8);
|
||||
SGV_DTOR(9);
|
||||
SGV_DTOR(10);
|
||||
|
||||
typedef void (*dtor_t)(void *, kmem_cache_t *, unsigned long);
|
||||
|
||||
dtor_t cache_dtors[SGV_POOL_ELEMENTS] =
|
||||
{ SGV_DTOR_NAME(0), SGV_DTOR_NAME(1), SGV_DTOR_NAME(2), SGV_DTOR_NAME(3),
|
||||
SGV_DTOR_NAME(4), SGV_DTOR_NAME(5), SGV_DTOR_NAME(6), SGV_DTOR_NAME(7),
|
||||
SGV_DTOR_NAME(8), SGV_DTOR_NAME(9), SGV_DTOR_NAME(10) };
|
||||
|
||||
int sgv_pool_init(struct sgv_pool *pool, const char *name, int clustered)
|
||||
{
|
||||
int res = -ENOMEM;
|
||||
int i;
|
||||
struct sgv_pool_obj *obj;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
memset(pool, 0, sizeof(*pool));
|
||||
pool->clustered = clustered;
|
||||
|
||||
TRACE_MEM("sizeof(*obj)=%d, clustered=%d, sizeof(obj->trans_tbl[0])=%d",
|
||||
sizeof(*obj), clustered, sizeof(obj->trans_tbl[0]));
|
||||
|
||||
for(i = 0; i < SGV_POOL_ELEMENTS; i++) {
|
||||
int size, pages;
|
||||
|
||||
atomic_set(&pool->cache_acc[i].total_alloc, 0);
|
||||
atomic_set(&pool->cache_acc[i].hit_alloc, 0);
|
||||
|
||||
pages = 1 << i;
|
||||
size = sizeof(*obj) + pages *
|
||||
(clustered ? sizeof(obj->trans_tbl[0]) : 0);
|
||||
TRACE_MEM("pages=%d, size=%d", pages, size);
|
||||
|
||||
scnprintf(pool->cache_names[i], sizeof(pool->cache_names[i]),
|
||||
"%s-%luK", name, (PAGE_SIZE >> 10) << i);
|
||||
pool->caches[i] = kmem_cache_create(pool->cache_names[i],
|
||||
size, 0, SCST_SLAB_FLAGS, sgv_ctor, cache_dtors[i]);
|
||||
if (pool->caches[i] == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "Allocation of sgv_pool cache "
|
||||
"%s(%d) failed", name, i);
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
res = 0;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_free:
|
||||
for(i = 0; i < SGV_POOL_ELEMENTS; i++) {
|
||||
if (pool->caches[i]) {
|
||||
kmem_cache_destroy(pool->caches[i]);
|
||||
pool->caches[i] = NULL;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
void sgv_pool_deinit(struct sgv_pool *pool)
|
||||
{
|
||||
int i;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
for(i = 0; i < SGV_POOL_ELEMENTS; i++) {
|
||||
if (pool->caches[i])
|
||||
kmem_cache_destroy(pool->caches[i]);
|
||||
pool->caches[i] = NULL;
|
||||
}
|
||||
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
struct sgv_pool *sgv_pool_create(const char *name, int clustered)
|
||||
{
|
||||
struct sgv_pool *pool;
|
||||
int rc;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
pool = kmalloc(sizeof(*pool), GFP_KERNEL);
|
||||
if (pool == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of sgv_pool failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = sgv_pool_init(pool, name, clustered);
|
||||
if (rc != 0)
|
||||
goto out_free;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(pool != NULL);
|
||||
return pool;
|
||||
|
||||
out_free:
|
||||
kfree(pool);
|
||||
pool = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
void sgv_pool_destroy(struct sgv_pool *pool)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
sgv_pool_deinit(pool);
|
||||
kfree(pool);
|
||||
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
int scst_sgv_pools_init(struct scst_sgv_pools *pools)
|
||||
{
|
||||
int res;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
atomic_set(&sgv_big_total_alloc, 0);
|
||||
|
||||
res = sgv_pool_init(&pools->norm, "sgv", 0);
|
||||
if (res != 0)
|
||||
goto out_free_clust;
|
||||
|
||||
res = sgv_pool_init(&pools->norm_clust, "sgv-clust", 1);
|
||||
if (res != 0)
|
||||
goto out;
|
||||
|
||||
res = sgv_pool_init(&pools->dma, "sgv-dma", 0);
|
||||
if (res != 0)
|
||||
goto out_free_norm;
|
||||
|
||||
#ifdef SCST_HIGHMEM
|
||||
res = sgv_pool_init(&pools->highmem, "sgv-high", 0);
|
||||
if (res != 0)
|
||||
goto out_free_dma;
|
||||
#endif
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
#ifdef SCST_HIGHMEM
|
||||
out_free_dma:
|
||||
sgv_pool_deinit(&pools->dma);
|
||||
#endif
|
||||
|
||||
out_free_norm:
|
||||
sgv_pool_deinit(&pools->norm);
|
||||
|
||||
out_free_clust:
|
||||
sgv_pool_deinit(&pools->norm_clust);
|
||||
goto out;
|
||||
}
|
||||
|
||||
void scst_sgv_pools_deinit(struct scst_sgv_pools *pools)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
#ifdef SCST_HIGHMEM
|
||||
sgv_pool_deinit(&pools->highmem);
|
||||
#endif
|
||||
sgv_pool_deinit(&pools->dma);
|
||||
sgv_pool_deinit(&pools->norm);
|
||||
sgv_pool_deinit(&pools->norm_clust);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* scst_sgv_pool.h
|
||||
*
|
||||
* Copyright (C) 2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <asm/scatterlist.h>
|
||||
|
||||
#define SGV_POOL_ELEMENTS 11
|
||||
|
||||
#if defined(DEBUG) && defined(CONFIG_DEBUG_SLAB)
|
||||
#define SCST_SLAB_FLAGS ( SLAB_RED_ZONE | SLAB_POISON )
|
||||
#else
|
||||
#define SCST_SLAB_FLAGS 0L
|
||||
#endif
|
||||
|
||||
/*
|
||||
* sg_num is indexed by the page number, pg_count is indexed by the sg number.
|
||||
* Made in one entry to simplify the code (eg all sizeof(*) parts) and save
|
||||
* the CPU cache for non-clustered case.
|
||||
*/
|
||||
struct trans_tbl_ent {
|
||||
unsigned short sg_num;
|
||||
unsigned short pg_count;
|
||||
};
|
||||
|
||||
struct sgv_pool_obj
|
||||
{
|
||||
kmem_cache_t *owner_cache;
|
||||
int eorder;
|
||||
int orig_sg;
|
||||
int orig_length;
|
||||
int sg_count;
|
||||
struct scatterlist *entries;
|
||||
struct trans_tbl_ent trans_tbl[0];
|
||||
};
|
||||
|
||||
struct sgv_pool_acc
|
||||
{
|
||||
atomic_t total_alloc, hit_alloc;
|
||||
};
|
||||
|
||||
struct sgv_pool
|
||||
{
|
||||
struct sgv_pool_acc acc;
|
||||
struct sgv_pool_acc cache_acc[SGV_POOL_ELEMENTS];
|
||||
unsigned int clustered:1;
|
||||
/* 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 */
|
||||
kmem_cache_t *caches[SGV_POOL_ELEMENTS];
|
||||
char cache_names[SGV_POOL_ELEMENTS][25];
|
||||
};
|
||||
|
||||
struct scst_sgv_pools
|
||||
{
|
||||
struct sgv_pool norm_clust, norm;
|
||||
struct sgv_pool dma;
|
||||
#ifdef SCST_HIGHMEM
|
||||
struct sgv_pool highmem;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern atomic_t sgv_big_total_alloc;
|
||||
|
||||
extern struct sgv_pool *sgv_pool_create(const char *name, int clustered);
|
||||
extern void sgv_pool_destroy(struct sgv_pool *pool);
|
||||
|
||||
extern int sgv_pool_init(struct sgv_pool *pool, const char *name,
|
||||
int clustered);
|
||||
extern void sgv_pool_deinit(struct sgv_pool *pool);
|
||||
|
||||
extern struct sgv_pool_obj *sgv_pool_alloc(struct sgv_pool *pool, int size,
|
||||
unsigned long flags, int *count);
|
||||
extern void __sgv_pool_free_big(struct sgv_pool_obj *obj);
|
||||
|
||||
static inline void sgv_pool_free(struct sgv_pool_obj *obj)
|
||||
{
|
||||
TRACE_MEM("Freeing sgv_obj %p", obj);
|
||||
if (obj->owner_cache != NULL) {
|
||||
obj->entries[obj->orig_sg].length = obj->orig_length;
|
||||
kmem_cache_free(obj->owner_cache, obj);
|
||||
}
|
||||
else
|
||||
__sgv_pool_free_big(obj);
|
||||
}
|
||||
|
||||
static inline struct scatterlist *sgv_pool_sg(struct sgv_pool_obj *obj)
|
||||
{
|
||||
return obj->entries;
|
||||
}
|
||||
|
||||
extern int scst_sgv_pools_init(struct scst_sgv_pools *pools);
|
||||
extern void scst_sgv_pools_deinit(struct scst_sgv_pools *pools);
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* scst_module.c
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* Support for loading target modules. The usage is similar to scsi_module.c
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <scsi_tgt.h>
|
||||
|
||||
static int __init init_this_scst_driver(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
res = scst_register_target_template(&driver_target_template);
|
||||
TRACE_DBG("scst_register_target_template() returned %d", res);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
#ifdef SCST_REGISTER_INITIATOR_DRIVER
|
||||
driver_template.module = THIS_MODULE;
|
||||
scsi_register_module(MODULE_SCSI_HA, &driver_template);
|
||||
TRACE_DBG("driver_template.present=%d",
|
||||
driver_template.present);
|
||||
if (driver_template.present == 0) {
|
||||
res = -ENODEV;
|
||||
MOD_DEC_USE_COUNT;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void __exit exit_this_scst_driver(void)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
#ifdef SCST_REGISTER_INITIATOR_DRIVER
|
||||
scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
|
||||
#endif
|
||||
|
||||
scst_unregister_target_template(&driver_target_template);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(init_this_scst_driver);
|
||||
module_exit(exit_this_scst_driver);
|
||||
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* scst_priv.h
|
||||
*
|
||||
* Copyright (C) 2004-2006 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* and Leonid Stoljar
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __SCST_PRIV_H
|
||||
#define __SCST_PRIV_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_driver.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
#include <scsi/scsi_request.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
|
||||
#define SCST_MAJOR 177
|
||||
|
||||
#define TRACE_RETRY 0x80000000
|
||||
#define TRACE_SCSI_SERIALIZING 0x40000000
|
||||
#define TRACE_SEND_TOP 0x20000000 /** top being the edge away from the interupt */
|
||||
#define TRACE_RECV_TOP 0x01000000
|
||||
#define TRACE_SEND_BOT 0x08000000 /** bottom being the edge toward the interupt */
|
||||
#define TRACE_RECV_BOT 0x04000000
|
||||
|
||||
#define LOG_PREFIX "scst"
|
||||
|
||||
#ifdef DEBUG
|
||||
/*#define SCST_DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF \
|
||||
& ~TRACE_FUNCTION)
|
||||
#define SCST_DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF & \
|
||||
~TRACE_SCSI & ~TRACE_SCSI_SERIALIZING & ~TRACE_DEBUG)
|
||||
*/
|
||||
#define SCST_DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
|
||||
TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_DEBUG | \
|
||||
TRACE_RETRY)
|
||||
#else
|
||||
# ifdef TRACING
|
||||
#define SCST_DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
|
||||
TRACE_SPECIAL)
|
||||
# else
|
||||
#define SCST_DEFAULT_LOG_FLAGS 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
** Bits for scst_flags
|
||||
**/
|
||||
|
||||
/* Set if new commands initialization should be suspended for a while */
|
||||
#define SCST_FLAG_SUSPENDED 0
|
||||
|
||||
/*
|
||||
* If set, SCST's threads exit immediately not performing any
|
||||
* sessions' shutdown tasks, therefore at this point all the sessions
|
||||
* must be already down.
|
||||
*/
|
||||
#define SCST_FLAG_SHUTDOWN 1
|
||||
|
||||
/* Set if a TM command is being performed */
|
||||
#define SCST_FLAG_TM_ACTIVE 2
|
||||
|
||||
/**
|
||||
** Return codes for cmd state process functions
|
||||
**/
|
||||
#define SCST_CMD_STATE_RES_CONT_SAME 0
|
||||
#define SCST_CMD_STATE_RES_CONT_NEXT 1
|
||||
#define SCST_CMD_STATE_RES_NEED_THREAD 2
|
||||
#define SCST_CMD_STATE_RES_RESTART 3
|
||||
|
||||
/** Name of the "default" security group **/
|
||||
#define SCST_DEFAULT_ACG_NAME "Default"
|
||||
|
||||
/**
|
||||
** Maximum count of uncompleted commands that an initiator could
|
||||
** queue on any device. Then it will take TASK QUEUE FULL status.
|
||||
**/
|
||||
#define SCST_MAX_DEVICE_COMMANDS 128
|
||||
|
||||
#define SCST_THREAD_FLAGS CLONE_KERNEL
|
||||
|
||||
#define SCST_TGT_RETRY_TIMEOUT (3/2*HZ)
|
||||
|
||||
static inline int scst_get_context(void) {
|
||||
/* Be overinsured */
|
||||
return (in_atomic() || in_interrupt()) ? SCST_CONTEXT_DIRECT_ATOMIC :
|
||||
SCST_CONTEXT_DIRECT;
|
||||
}
|
||||
|
||||
#define SCST_MGMT_CMD_CACHE_STRING "scst_mgmt_cmd"
|
||||
extern kmem_cache_t *scst_mgmt_cachep;
|
||||
extern mempool_t *scst_mgmt_mempool;
|
||||
|
||||
#define SCST_UA_CACHE_STRING "scst_ua"
|
||||
extern kmem_cache_t *scst_ua_cachep;
|
||||
extern mempool_t *scst_ua_mempool;
|
||||
|
||||
#define SCST_CMD_CACHE_STRING "scst_cmd"
|
||||
extern kmem_cache_t *scst_cmd_cachep;
|
||||
|
||||
#define SCST_SESSION_CACHE_STRING "scst_session"
|
||||
extern kmem_cache_t *scst_sess_cachep;
|
||||
|
||||
#define SCST_TGT_DEV_CACHE_STRING "scst_tgt_dev"
|
||||
extern kmem_cache_t *scst_tgtd_cachep;
|
||||
|
||||
#define SCST_ACG_DEV_CACHE_STRING "scst_acg_dev"
|
||||
extern kmem_cache_t *scst_acgd_cachep;
|
||||
|
||||
extern struct scst_sgv_pools scst_sgv;
|
||||
|
||||
extern int scst_num_cpus;
|
||||
extern unsigned long scst_flags;
|
||||
extern struct semaphore scst_mutex;
|
||||
extern atomic_t scst_cmd_count;
|
||||
extern spinlock_t scst_list_lock;
|
||||
extern struct list_head scst_dev_wait_sess_list; /* protected by scst_list_lock */
|
||||
extern struct list_head scst_template_list; /* protected by scst_mutex */
|
||||
extern struct list_head scst_dev_list; /* protected by scst_mutex */
|
||||
extern struct list_head scst_dev_type_list; /* protected by scst_mutex */
|
||||
extern wait_queue_head_t scst_dev_cmd_waitQ;
|
||||
|
||||
extern struct list_head scst_acg_list;
|
||||
extern struct scst_acg *scst_default_acg;
|
||||
|
||||
/* The following lists protected by scst_list_lock */
|
||||
extern struct list_head scst_active_cmd_list;
|
||||
extern struct list_head scst_init_cmd_list;
|
||||
extern struct list_head scst_cmd_list;
|
||||
|
||||
extern struct list_head scst_mgmt_cmd_list;
|
||||
extern struct list_head scst_active_mgmt_cmd_list;
|
||||
extern struct list_head scst_delayed_mgmt_cmd_list;
|
||||
|
||||
extern struct tasklet_struct scst_tasklets[NR_CPUS];
|
||||
extern wait_queue_head_t scst_list_waitQ;
|
||||
|
||||
extern wait_queue_head_t scst_mgmt_cmd_list_waitQ;
|
||||
|
||||
extern wait_queue_head_t scst_mgmt_waitQ;
|
||||
extern spinlock_t scst_mgmt_lock;
|
||||
extern struct list_head scst_sess_mgmt_list;
|
||||
|
||||
extern int scst_threads;
|
||||
extern int scst_shut_threads_count;
|
||||
extern atomic_t scst_threads_count;
|
||||
extern int scst_thread_num;
|
||||
|
||||
extern struct semaphore *scst_shutdown_mutex;
|
||||
|
||||
extern spinlock_t scst_temp_UA_lock;
|
||||
extern uint8_t scst_temp_UA[SCSI_SENSE_BUFFERSIZE];
|
||||
|
||||
extern struct scst_cmd *__scst_check_deferred_commands(
|
||||
struct scst_tgt_dev *tgt_dev, int expected_sn);
|
||||
|
||||
/* Used to save the function call on th fast path */
|
||||
static inline struct scst_cmd *scst_check_deferred_commands(
|
||||
struct scst_tgt_dev *tgt_dev, int expected_sn)
|
||||
{
|
||||
if (tgt_dev->def_cmd_count == 0)
|
||||
return NULL;
|
||||
else
|
||||
return __scst_check_deferred_commands(tgt_dev, expected_sn);
|
||||
}
|
||||
|
||||
static inline int __scst_inc_expected_sn(struct scst_tgt_dev *tgt_dev)
|
||||
{
|
||||
/*
|
||||
* No locks is needed, because only one thread at time can
|
||||
* call it (serialized by sn). Also it is supposed that there
|
||||
* could not be half-incremented halves.
|
||||
*/
|
||||
|
||||
typeof(tgt_dev->expected_sn) e;
|
||||
|
||||
e = tgt_dev->expected_sn;
|
||||
tgt_dev->expected_sn++;
|
||||
smp_mb(); /* write must be before def_cmd_count read */
|
||||
e++;
|
||||
TRACE(TRACE_DEBUG/*TRACE_SCSI_SERIALIZING*/, "Next expected_sn: %d", e);
|
||||
return e;
|
||||
}
|
||||
|
||||
void scst_inc_expected_sn_unblock(struct scst_tgt_dev *tgt_dev,
|
||||
struct scst_cmd *cmd_sn, int locked);
|
||||
|
||||
int scst_cmd_thread(void *arg);
|
||||
void scst_cmd_tasklet(long p);
|
||||
int scst_mgmt_cmd_thread(void *arg);
|
||||
int scst_mgmt_thread(void *arg);
|
||||
|
||||
struct scst_device *scst_alloc_device(int gfp_mask);
|
||||
void scst_free_device(struct scst_device *tgt_dev);
|
||||
|
||||
struct scst_acg *scst_alloc_add_acg(const char *acg_name);
|
||||
int scst_destroy_acg(struct scst_acg *acg);
|
||||
|
||||
int scst_sess_alloc_tgt_devs(struct scst_session *sess);
|
||||
void scst_sess_free_tgt_devs(struct scst_session *sess);
|
||||
void scst_reset_tgt_dev(struct scst_tgt_dev *tgt_dev, int nexus_loss);
|
||||
|
||||
int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev, lun_t lun,
|
||||
int read_only);
|
||||
int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev);
|
||||
|
||||
int scst_acg_add_name(struct scst_acg *acg, const char *name);
|
||||
int scst_acg_remove_name(struct scst_acg *acg, const char *name);
|
||||
|
||||
int scst_assign_dev_handler(struct scst_device *dev,
|
||||
struct scst_dev_type *handler);
|
||||
|
||||
struct scst_session *scst_alloc_session(struct scst_tgt *tgt, int gfp_mask,
|
||||
const char *initiator_name);
|
||||
void scst_free_session(struct scst_session *sess);
|
||||
void scst_free_session_callback(struct scst_session *sess);
|
||||
|
||||
struct scst_cmd *scst_alloc_cmd(int gfp_mask);
|
||||
void scst_free_cmd(struct scst_cmd *cmd);
|
||||
static inline void scst_destroy_cmd(struct scst_cmd *cmd)
|
||||
{
|
||||
TRACE_MEM("kfree for cmd: %p", cmd);
|
||||
kmem_cache_free(scst_cmd_cachep, cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
void scst_check_retries(struct scst_tgt *tgt, int processible_env);
|
||||
void scst_tgt_retry_timer_fn(unsigned long arg);
|
||||
|
||||
int scst_alloc_request(struct scst_cmd *cmd);
|
||||
void scst_release_request(struct scst_cmd *cmd);
|
||||
|
||||
static inline void scst_do_req(struct scsi_request *sreq,
|
||||
const void *cmnd, void *buffer, unsigned bufflen,
|
||||
void (*done)(struct scsi_cmnd *), int timeout, int retries)
|
||||
{
|
||||
#ifdef STRICT_SERIALIZING
|
||||
scsi_do_req(sreq, cmnd, buffer, bufflen, done, timeout, retries);
|
||||
#else
|
||||
scsi_do_req_fifo(sreq, cmnd, buffer, bufflen, done, timeout, retries);
|
||||
#endif
|
||||
}
|
||||
int scst_alloc_space(struct scst_cmd *cmd);
|
||||
void scst_release_space(struct scst_cmd *cmd);
|
||||
void scst_scsi_op_list_init(void);
|
||||
|
||||
lun_t scst_unpack_lun(const uint8_t *lun, int len);
|
||||
|
||||
struct scst_cmd *__scst_find_cmd_by_tag(struct scst_session *sess,
|
||||
uint32_t tag);
|
||||
|
||||
struct scst_mgmt_cmd *scst_alloc_mgmt_cmd(int gfp_mask);
|
||||
void scst_free_mgmt_cmd(struct scst_mgmt_cmd *mcmd, int del);
|
||||
|
||||
/* /proc support */
|
||||
int scst_proc_init_module(void);
|
||||
void scst_proc_cleanup_module(void);
|
||||
int scst_build_proc_target_dir_entries(struct scst_tgt_template *vtt);
|
||||
void scst_cleanup_proc_target_dir_entries(struct scst_tgt_template *vtt);
|
||||
int scst_build_proc_target_entries(struct scst_tgt *vtt);
|
||||
void scst_cleanup_proc_target_entries(struct scst_tgt *vtt);
|
||||
int scst_build_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type);
|
||||
void scst_cleanup_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type);
|
||||
|
||||
int scst_get_cdb_len(const uint8_t *cdb);
|
||||
|
||||
void __scst_process_UA(struct scst_device *dev, struct scst_cmd *exclude,
|
||||
const uint8_t *sense, int sense_len, int internal);
|
||||
static inline void scst_process_UA(struct scst_device *dev,
|
||||
struct scst_cmd *exclude, const uint8_t *sense, int sense_len,
|
||||
int internal)
|
||||
{
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
__scst_process_UA(dev, exclude, sense, sense_len, internal);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
return;
|
||||
}
|
||||
void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev, const uint8_t *sense,
|
||||
int sense_len);
|
||||
void scst_check_set_UA(struct scst_tgt_dev *tgt_dev,
|
||||
const uint8_t *sense, int sense_len);
|
||||
int scst_set_pending_UA(struct scst_cmd *cmd);
|
||||
void scst_free_all_UA(struct scst_tgt_dev *tgt_dev);
|
||||
|
||||
void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
|
||||
int other_ini, int call_dev_task_mgmt_fn);
|
||||
void scst_process_reset(struct scst_device *dev,
|
||||
struct scst_session *originator, struct scst_cmd *exclude_cmd,
|
||||
struct scst_mgmt_cmd *mcmd);
|
||||
|
||||
static inline int scst_is_ua_command(struct scst_cmd *cmd)
|
||||
{
|
||||
return ((cmd->cdb[0] != INQUIRY) &&
|
||||
(cmd->cdb[0] != REQUEST_SENSE) &&
|
||||
(cmd->cdb[0] != REPORT_LUNS));
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1, if cmd's CDB is locally handled by SCST and 0 otherwise.
|
||||
* Dev handlers parse() and dev_done() not called for such commands.
|
||||
*/
|
||||
static inline int scst_is_cmd_local(struct scst_cmd *cmd)
|
||||
{
|
||||
int res = 0;
|
||||
switch (cmd->cdb[0]) {
|
||||
case REPORT_LUNS:
|
||||
res = 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some notes on devices "blocking". Blocking means that no
|
||||
* commands will go from SCST to underlying SCSI device until it
|
||||
* is unblocked. But we don't care about all commands that
|
||||
* already on the device.
|
||||
*/
|
||||
|
||||
extern int scst_inc_on_dev_cmd(struct scst_cmd *cmd);
|
||||
extern void scst_unblock_cmds(struct scst_device *dev);
|
||||
|
||||
static inline void __scst_block_dev(struct scst_device *dev)
|
||||
{
|
||||
dev->block_count++;
|
||||
smp_mb();
|
||||
TRACE_MGMT_DBG("Device BLOCK(%d), dev %p", dev->block_count, dev);
|
||||
}
|
||||
|
||||
static inline void scst_block_dev(struct scst_device *dev,
|
||||
unsigned int outstanding)
|
||||
{
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
__scst_block_dev(dev);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
TRACE_MGMT_DBG("%s", "Waiting during blocking");
|
||||
wait_event(dev->on_dev_waitQ,
|
||||
atomic_read(&dev->on_dev_count) <= outstanding);
|
||||
TRACE_MGMT_DBG("%s", "wait_event() returned");
|
||||
}
|
||||
|
||||
static inline void scst_unblock_dev(struct scst_device *dev)
|
||||
{
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
TRACE_MGMT_DBG("Device UNBLOCK(%d), dev %p",
|
||||
dev->block_count-1, dev);
|
||||
if (--dev->block_count == 0)
|
||||
scst_unblock_cmds(dev);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
}
|
||||
|
||||
static inline void scst_dec_on_dev_cmd(struct scst_cmd *cmd)
|
||||
{
|
||||
if (cmd->blocking) {
|
||||
TRACE_MGMT_DBG("cmd %p (tag %d): unblocking dev %p", cmd,
|
||||
cmd->tag, cmd->dev);
|
||||
cmd->blocking = 0;
|
||||
scst_unblock_dev(cmd->dev);
|
||||
}
|
||||
atomic_dec(&cmd->dev->on_dev_count);
|
||||
smp_mb__after_atomic_dec();
|
||||
if (unlikely(cmd->dev->block_count != 0))
|
||||
wake_up_all(&cmd->dev->on_dev_waitQ);
|
||||
}
|
||||
|
||||
static inline void scst_inc_cmd_count(void)
|
||||
{
|
||||
atomic_inc(&scst_cmd_count);
|
||||
smp_mb__after_atomic_inc();
|
||||
TRACE_DBG("Incrementing scst_cmd_count(%d)",
|
||||
atomic_read(&scst_cmd_count));
|
||||
}
|
||||
|
||||
static inline void scst_dec_cmd_count(void)
|
||||
{
|
||||
int f;
|
||||
f = atomic_dec_and_test(&scst_cmd_count);
|
||||
smp_mb__after_atomic_dec();
|
||||
if (f && unlikely(test_bit(SCST_FLAG_SUSPENDED, &scst_flags)))
|
||||
wake_up_all(&scst_dev_cmd_waitQ);
|
||||
TRACE_DBG("Decrementing scst_cmd_count(%d)",
|
||||
atomic_read(&scst_cmd_count));
|
||||
}
|
||||
|
||||
void scst_sched_session_free(struct scst_session *sess);
|
||||
|
||||
static inline void scst_sess_get(struct scst_session *sess)
|
||||
{
|
||||
atomic_inc(&sess->refcnt);
|
||||
}
|
||||
|
||||
static inline void scst_sess_put(struct scst_session *sess)
|
||||
{
|
||||
if (atomic_dec_and_test(&sess->refcnt))
|
||||
scst_sched_session_free(sess);
|
||||
}
|
||||
|
||||
void __scst_suspend_activity(void);
|
||||
void __scst_resume_activity(void);
|
||||
|
||||
extern void scst_throttle_cmd(struct scst_cmd *cmd);
|
||||
extern void scst_unthrottle_cmd(struct scst_cmd *cmd);
|
||||
|
||||
static inline void scst_set_sense(uint8_t *buffer, int len, int key,
|
||||
int asc, int ascq)
|
||||
{
|
||||
memset(buffer, 0, len);
|
||||
buffer[0] = 0x70; /* Error Code */
|
||||
buffer[2] = key; /* Sense Key */
|
||||
buffer[7] = 0x0a; /* Additional Sense Length */
|
||||
buffer[12] = asc; /* ASC */
|
||||
buffer[13] = ascq; /* ASCQ */
|
||||
TRACE_BUFFER("Sense set", buffer, len);
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void scst_check_restore_sg_buff(struct scst_cmd *cmd)
|
||||
{
|
||||
if (cmd->sg_buff_modified) {
|
||||
cmd->sg[cmd->orig_sg_entry].length = cmd->orig_entry_len;
|
||||
cmd->sg_cnt = cmd->orig_sg_cnt;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TM
|
||||
extern void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev,
|
||||
struct scst_acg_dev *acg_dev);
|
||||
extern void tm_dbg_deinit_tgt_dev(struct scst_tgt_dev *tgt_dev);
|
||||
extern void tm_dbg_check_released_cmds(void);
|
||||
extern int tm_dbg_check_cmd(struct scst_cmd *cmd);
|
||||
extern void tm_dbg_release_cmd(struct scst_cmd *cmd);
|
||||
extern void tm_dbg_task_mgmt(const char *fn);
|
||||
extern int tm_dbg_is_release(void);
|
||||
#else
|
||||
static inline void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev,
|
||||
struct scst_acg_dev *acg_dev) {}
|
||||
static inline void tm_dbg_deinit_tgt_dev(struct scst_tgt_dev *tgt_dev) {}
|
||||
static inline void tm_dbg_check_released_cmds(void) {}
|
||||
static inline int tm_dbg_check_cmd(struct scst_cmd *cmd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void tm_dbg_release_cmd(struct scst_cmd *cmd) {}
|
||||
static inline void tm_dbg_task_mgmt(const char *fn) {}
|
||||
static inline int tm_dbg_is_release(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* DEBUG_TM */
|
||||
|
||||
#endif /* __SCST_PRIV_H */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user