|
From: Akinobu M. <mi...@mi...> - 2005-05-23 07:18:53
|
Hello,
I made a diskdump cciss support patch.
This patch is based on Mike Mirror's patch which add support for diskdump
His patch seems to be written for Red Hat Enterprise Linux 3.
So I rewrote it for kernel-2.6.
But this patch doesn't have flexibility about setting dump device.
If you want to select /dev/cciss/c0d1p1 as a dump partition,
1. append "options cciss dump_drive=1" into modprobe.conf
(If you select /dev/cciss/c0d[#a]p[#b],
append "options cciss dump_drive=[#a]")
2. reload cciss module
(If you are loading cciss module from initrd, you need to recreate
initrd and reboot your machine)
3. set dump partition:
# echo 1 > /sys/block/cciss\!c0d1/device/dump
(If you select /dev/cciss/c0d[#a]p[#b],
do echo [#b] > /sys/block/cciss\!c0d[#a]/device/dump
diff -Nurp 2.6.9-diskdump-1.0.orig/drivers/block/cciss.c 2.6.9-diskdump-1.0/drivers/block/cciss.c
--- 2.6.9-diskdump-1.0.orig/drivers/block/cciss.c 2005-05-23 14:10:02.553869328 +0900
+++ 2.6.9-diskdump-1.0/drivers/block/cciss.c 2005-05-23 14:18:23.367734048 +0900
@@ -44,6 +44,18 @@
#include <linux/blkdev.h>
#include <linux/genhd.h>
#include <linux/completion.h>
+#include <linux/crc32.h>
+#include <linux/diskdump.h>
+
+#define Dbg(x, ...) pr_debug("cciss_dump: " x "\n", ## __VA_ARGS__)
+#define Err(x, ...) pr_err ("cciss_dump: " x "\n", ## __VA_ARGS__)
+#define Warn(x, ...) pr_warn ("cciss_dump: " x "\n", ## __VA_ARGS__)
+#define Info(x, ...) pr_info ("cciss_dump: " x "\n", ## __VA_ARGS__)
+
+/* blocks to 512byte secors */
+#define BLOCK_SECTOR(s) ((s) << (DUMP_BLOCK_SHIFT - 9))
+
+static uint32_t module_crc;
#define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
#define DRIVER_NAME "HP CISS Driver (v 2.6.2)"
@@ -139,7 +151,7 @@ static void cciss_getgeometry(int cntl_n
static void start_io( ctlr_info_t *h);
static int sendcmd( __u8 cmd, int ctlr, void *buff, size_t size,
unsigned int use_unit_num, unsigned int log_unit, __u8 page_code,
- unsigned char *scsi3addr, int cmd_type);
+ unsigned char *scsi3addr, int block_nr, int cmd_type);
#ifdef CONFIG_PROC_FS
static int cciss_proc_get_info(char *buffer, char **start, off_t offset,
@@ -157,6 +169,181 @@ static struct block_device_operations cc
.revalidate_disk= cciss_revalidate,
};
+static CommandList_struct *cciss_dump_cmnd;
+
+/*
+ * cciss sysfs interface routines
+ */
+static DEVICE_ATTR(dump, S_IRUGO | S_IWUSR, diskdump_sysfs_show, diskdump_sysfs_store);
+
+/* device attributes */
+static struct device_attribute *cciss_sysfs_attrs[] = {
+ &dev_attr_dump,
+ NULL
+};
+
+static int cciss_attr_add(struct pci_dev *pdev)
+{
+ int i, error = 0;
+ for (i = 0; cciss_sysfs_attrs[i]; i++) {
+ error = device_create_file(&pdev->dev, cciss_sysfs_attrs[i]);
+ if (error)
+ break;
+ }
+ return error;
+}
+
+static int dump_drive = -1;
+module_param(dump_drive, int, S_IRUGO);
+
+static void
+cciss_read_capacity(int ctlr, int logvol, ReadCapdata_struct *buf,
+ int withirq, unsigned int *total_size, unsigned int *block_size);
+
+static int cciss_dump_sanity_check(struct disk_dump_device *dump_device)
+{
+ struct pci_dev *pdev = dump_device->device;
+ ctlr_info_t *h = pci_get_drvdata(pdev);
+ ReadCapdata_struct size_buff;
+ unsigned int total_size, block_size;
+
+ if (dump_drive > h->highest_lun) {
+ Err("Invalid dump_drive (%d)", dump_drive);
+ return -1;
+ }
+ cciss_read_capacity(h->ctlr, dump_drive,
+ &size_buff, 0, &total_size, &block_size);
+
+ if (h->drv[dump_drive].nr_blocks != total_size) {
+ Err("blocks read do not match stored value");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int cciss_dump_rw_block(struct disk_dump_partition *dump_part, int rw,
+ unsigned long dump_block_nr, void *buf, int len)
+{
+ struct disk_dump_device *dump_device = dump_part->device;
+ struct pci_dev *pdev = dump_device->device;
+ ctlr_info_t *h = pci_get_drvdata(pdev);
+ int block_nr = BLOCK_SECTOR(dump_block_nr);
+ __u8 cmd = CCISS_READ;
+ int ret;
+
+ if ((block_nr + len * DUMP_BLOCK_SIZE / h->drv[dump_drive].block_size)
+ > dump_part->start_sect + dump_part->nr_sects) {
+
+ Err("block number %d is larger than %lu",
+ block_nr + 0xdeadbeef,
+ dump_part->start_sect + dump_part->nr_sects
+ );
+ return -EFBIG;
+ }
+
+ if (rw == WRITE)
+ cmd = CCISS_WRITE;
+
+ ret = sendcmd(cmd, h->ctlr, buf, len * DUMP_BLOCK_SIZE,
+ 1, dump_drive, 0, NULL, block_nr + dump_part->start_sect,
+ TYPE_CMD);
+
+ return 0;
+}
+
+static int cciss_dump_quiesce(struct disk_dump_device *dump_device)
+{
+ struct pci_dev *pdev = dump_device->device;
+ ctlr_info_t *h = pci_get_drvdata(pdev);
+ char flush_buf[4];
+ int ret;
+
+ memset(flush_buf, 0, 4);
+ ret = sendcmd(CCISS_CACHE_FLUSH,
+ h->ctlr, flush_buf, 4, 0, 0, 0, NULL, 0, TYPE_CMD);
+
+ if (ret != IO_OK)
+ printk(KERN_WARNING "Error Flushing cache on controller %d\n",
+ h->ctlr);
+
+ return 0;
+}
+
+static int cciss_dump_shutdown(struct disk_dump_device *dump_device)
+{
+ return 0;
+}
+
+struct disk_dump_device_ops cciss_dump_device_ops = {
+ .sanity_check = cciss_dump_sanity_check,
+ .rw_block = cciss_dump_rw_block,
+ .quiesce = cciss_dump_quiesce,
+ .shutdown = cciss_dump_shutdown,
+};
+
+static CommandList_struct * cmd_alloc(ctlr_info_t *h, int get_from_pool);
+
+static void *cciss_dump_probe(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ ctlr_info_t *h = pci_get_drvdata(pdev);
+
+ cciss_dump_cmnd = cmd_alloc(h, 0);
+ if (cciss_dump_cmnd == NULL)
+ return NULL;
+
+ return pdev;
+}
+
+static int cciss_dump_add_device(struct disk_dump_device *dump_device)
+{
+ memcpy(&dump_device->ops, &cciss_dump_device_ops,
+ sizeof(cciss_dump_device_ops));
+ dump_device->max_blocks = 8;
+ set_crc_modules();
+
+ return 0;
+}
+
+static void cmd_free(ctlr_info_t *h, CommandList_struct *c, int got_from_pool);
+
+static void cciss_dump_remove_device(struct disk_dump_device *dump_device)
+{
+ struct pci_dev *pdev = dump_device->device;
+ ctlr_info_t *h = pci_get_drvdata(pdev);
+
+ if (cciss_dump_cmnd)
+ cmd_free(h, cciss_dump_cmnd, 0);
+
+ return;
+}
+
+static struct disk_dump_type cciss_dump_type = {
+ .probe = cciss_dump_probe,
+ .add_device = cciss_dump_add_device,
+ .remove_device = cciss_dump_remove_device,
+ .owner = THIS_MODULE,
+};
+
+static int init_cciss_dump(void)
+{
+ int ret;
+
+ if ((ret = register_disk_dump_type(&cciss_dump_type)) < 0) {
+ Err("register failed");
+ return ret;
+ }
+ set_crc_modules();
+ return ret;
+}
+
+static void cleanup_cciss_dump(void)
+{
+ if (unregister_disk_dump_type(&cciss_dump_type) < 0)
+ Err("unregister failed");
+}
+
/*
* Enqueuing and dequeuing functions for cmdlists.
*/
@@ -1211,6 +1398,7 @@ static int fill_cmd(CommandList_struct *
1: address logical volume log_unit,
2: periph device address is scsi3addr */
unsigned int log_unit, __u8 page_code, unsigned char *scsi3addr,
+ int block_nr,
int cmd_type)
{
ctlr_info_t *h= hba[ctlr];
@@ -1231,6 +1419,8 @@ static int fill_cmd(CommandList_struct *
c->Request.Type.Type = cmd_type;
if (cmd_type == TYPE_CMD) {
switch(cmd) {
+ int nr_blocks;
+
case CISS_INQUIRY:
/* If the logical unit number is 0 then, this is going
to controller so It's a physical command
@@ -1292,6 +1482,54 @@ static int fill_cmd(CommandList_struct *
c->Request.CDB[0] = BMIC_WRITE;
c->Request.CDB[6] = BMIC_CACHE_FLUSH;
break;
+ case CCISS_READ:
+ if (use_unit_num == 1) {
+ c->Header.LUN.LogDev.VolId =
+ hba[ctlr]->drv[log_unit].LunID;
+ c->Header.LUN.LogDev.Mode = 1;
+ }
+ c->ctlr = ctlr;
+ c->cmd_type = CMD_RWREQ;
+ c->Request.CDBLen = 10;
+ c->Request.Type.Type = TYPE_CMD;
+ c->Request.Type.Attribute = ATTR_SIMPLE;
+ c->Request.Type.Direction = XFER_READ;
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = CCISS_READ;
+ c->Request.CDB[2] = (block_nr >> 24) & 0xff;
+ c->Request.CDB[3] = (block_nr >> 16) & 0xff;
+ c->Request.CDB[4] = (block_nr >> 8) & 0xff;
+ c->Request.CDB[5] = block_nr & 0xff;
+ c->Request.CDB[6] = 0;
+ nr_blocks = size/hba[ctlr]->drv[log_unit].block_size;
+ c->Request.CDB[7] = (nr_blocks >> 8) & 0xff;
+ c->Request.CDB[8] = nr_blocks & 0xff;
+ c->Request.CDB[9] = 0;
+ break;
+ case CCISS_WRITE:
+ if (use_unit_num == 1) {
+ c->Header.LUN.LogDev.VolId =
+ hba[ctlr]->drv[log_unit].LunID;
+ c->Header.LUN.LogDev.Mode = 1;
+ }
+ c->ctlr = ctlr;
+ c->cmd_type = CMD_RWREQ;
+ c->Request.CDBLen = 10;
+ c->Request.Type.Type = TYPE_CMD;
+ c->Request.Type.Attribute = ATTR_SIMPLE;
+ c->Request.Type.Direction = XFER_WRITE;
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = CCISS_WRITE;
+ c->Request.CDB[2] = (block_nr >> 24) & 0xff;
+ c->Request.CDB[3] = (block_nr >> 16) & 0xff;
+ c->Request.CDB[4] = (block_nr >> 8) & 0xff;
+ c->Request.CDB[5] = block_nr & 0xff;
+ c->Request.CDB[6] = 0;
+ nr_blocks = size/hba[ctlr]->drv[log_unit].block_size;
+ c->Request.CDB[7] = (nr_blocks >> 8) & 0xff;
+ c->Request.CDB[8] = nr_blocks & 0xff;
+ c->Request.CDB[9] = 0;
+ break;
default:
printk(KERN_WARNING
"cciss%d: Unknown Command 0x%c\n", ctlr, cmd);
@@ -1347,7 +1585,7 @@ static int sendcmd_withirq(__u8 cmd,
if ((c = cmd_alloc(h , 0)) == NULL)
return -ENOMEM;
return_status = fill_cmd(c, cmd, ctlr, buff, size, use_unit_num,
- log_unit, page_code, NULL, cmd_type);
+ log_unit, page_code, NULL, -1, cmd_type);
if (return_status != IO_OK) {
cmd_free(h, c, 0);
return return_status;
@@ -1458,7 +1696,7 @@ static void cciss_geometry_inquiry(int c
inq_buff, sizeof(*inq_buff), 1, logvol ,0xC1, TYPE_CMD);
else
return_code = sendcmd(CISS_INQUIRY, ctlr, inq_buff,
- sizeof(*inq_buff), 1, logvol ,0xC1, NULL, TYPE_CMD);
+ sizeof(*inq_buff), 1, logvol ,0xC1, NULL, -1, TYPE_CMD);
if (return_code == IO_OK) {
if(inq_buff->data_byte[8] == 0xFF) {
printk(KERN_WARNING
@@ -1507,7 +1745,7 @@ cciss_read_capacity(int ctlr, int logvol
ctlr, buf, sizeof(*buf), 1, logvol, 0, TYPE_CMD);
else
return_code = sendcmd(CCISS_READ_CAPACITY,
- ctlr, buf, sizeof(*buf), 1, logvol, 0, NULL, TYPE_CMD);
+ ctlr, buf, sizeof(*buf), 1, logvol, 0, NULL, -1, TYPE_CMD);
if (return_code == IO_OK) {
*total_size = be32_to_cpu(*((__be32 *) &buf->total_size[0]))+1;
*block_size = be32_to_cpu(*((__be32 *) &buf->block_size[0]));
@@ -1698,6 +1936,25 @@ static unsigned long pollcomplete(int ct
/* Invalid address to tell caller we ran out of time */
return 1;
}
+
+/*
+ * Wait polling for a command to complete.
+ * The memory mapped FIFO is polled for the completion.
+ * Used only at dump time, interrupts disabled.
+ */
+static unsigned long diskdump_pollcomplete(int ctlr)
+{
+ unsigned long done;
+
+ while (1) {
+ done = hba[ctlr]->access.command_completed(hba[ctlr]);
+ if (done == FIFO_EMPTY)
+ udelay(20);
+ else
+ return done;
+ }
+}
+
/*
* Send a command to the controller, and wait for it to complete.
* Only used at init time.
@@ -1713,6 +1970,7 @@ static int sendcmd(
unsigned int log_unit,
__u8 page_code,
unsigned char *scsi3addr,
+ int block_nr,
int cmd_type)
{
CommandList_struct *c;
@@ -1722,14 +1980,24 @@ static int sendcmd(
u64bit buff_dma_handle;
int status;
- if ((c = cmd_alloc(info_p, 1)) == NULL) {
+ /* For diskdump we will do a read and write in this routine.
+ Because the kernel is not in a stable state we will use the
+ pre-allocated command reserved for diskdump */
+ if (crashdump_mode())
+ c = cciss_dump_cmnd;
+ else
+ c = cmd_alloc(info_p, 1);
+
+ if (c == NULL) {
printk(KERN_WARNING "cciss: unable to get memory");
return(IO_ERROR);
}
status = fill_cmd(c, cmd, ctlr, buff, size, use_unit_num,
- log_unit, page_code, scsi3addr, cmd_type);
+ log_unit, page_code, scsi3addr, block_nr, cmd_type);
if (status != IO_OK) {
- cmd_free(info_p, c, 1);
+ if (!crashdump_mode())
+ cmd_free(info_p, c, 1);
+
return status;
}
resend_cmd1:
@@ -1759,7 +2027,11 @@ resend_cmd1:
* Send the cmd
*/
info_p->access.submit_command(info_p, c);
- complete = pollcomplete(ctlr);
+retry:
+ if (crashdump_mode())
+ complete = diskdump_pollcomplete(ctlr);
+ else
+ complete = pollcomplete(ctlr);
#ifdef CCISS_DEBUG
printk(KERN_DEBUG "cciss: command completed\n");
@@ -1823,6 +2095,10 @@ resend_cmd1:
printk( KERN_WARNING "cciss cciss%d: SendCmd "
"Invalid command list address returned! (%lx)\n",
ctlr, complete);
+
+ if (crashdump_mode())
+ goto retry;
+
status = IO_ERROR;
goto cleanup1;
}
@@ -1838,7 +2114,9 @@ cleanup1:
/* unlock the data buffer from DMA */
pci_unmap_single(info_p->pdev, (dma_addr_t) buff_dma_handle.val,
size, PCI_DMA_BIDIRECTIONAL);
- cmd_free(info_p, c, 1);
+ if (!crashdump_mode())
+ cmd_free(info_p, c, 1);
+
return (status);
}
/*
@@ -2527,7 +2805,7 @@ static void cciss_getgeometry(int cntl_n
}
/* Get the firmware version */
return_code = sendcmd(CISS_INQUIRY, cntl_num, inq_buff,
- sizeof(InquiryData_struct), 0, 0 ,0, NULL, TYPE_CMD);
+ sizeof(InquiryData_struct), 0, 0 ,0, NULL, -1, TYPE_CMD);
if (return_code == IO_OK)
{
hba[cntl_num]->firm_ver[0] = inq_buff->data_byte[32];
@@ -2541,7 +2819,7 @@ static void cciss_getgeometry(int cntl_n
}
/* Get the number of logical volumes */
return_code = sendcmd(CISS_REPORT_LOG, cntl_num, ld_buff,
- sizeof(ReportLunData_struct), 0, 0, 0, NULL, TYPE_CMD);
+ sizeof(ReportLunData_struct), 0, 0, 0, NULL, -1, TYPE_CMD);
if( return_code == IO_OK)
{
@@ -2762,12 +3040,15 @@ static int __devinit cciss_init_one(stru
disk->fops = &cciss_fops;
disk->queue = hba[i]->queue;
disk->private_data = drv;
+ if (j == dump_drive)
+ disk->driverfs_dev = &pdev->dev;
if( !(drv->nr_blocks))
continue;
blk_queue_hardsect_size(hba[i]->queue, drv->block_size);
set_capacity(disk, drv->nr_blocks);
add_disk(disk);
}
+ cciss_attr_add(pdev);
return(1);
clean4:
@@ -2816,7 +3097,7 @@ static void __devexit cciss_remove_one (
* To write all data in the battery backed cache to disks */
memset(flush_buf, 0, 4);
return_code = sendcmd(CCISS_CACHE_FLUSH, i, flush_buf, 4, 0, 0, 0, NULL,
- TYPE_CMD);
+ -1, TYPE_CMD);
if(return_code != IO_OK)
{
printk(KERN_WARNING "Error Flushing cache on controller %d\n",
@@ -2867,14 +3148,20 @@ int __init cciss_init(void)
static int __init init_cciss_module(void)
{
+ int ret;
+
register_cciss_ioctl32();
- return ( cciss_init());
+ ret = cciss_init();
+ if (ret < 0)
+ return ret;
+ return init_cciss_dump();
}
static void __exit cleanup_cciss_module(void)
{
int i;
+ cleanup_cciss_dump();
unregister_cciss_ioctl32();
pci_unregister_driver(&cciss_pci_driver);
/* double check that all controller entrys have been removed */
diff -Nurp 2.6.9-diskdump-1.0.orig/drivers/block/cciss_scsi.c 2.6.9-diskdump-1.0/drivers/block/cciss_scsi.c
--- 2.6.9-diskdump-1.0.orig/drivers/block/cciss_scsi.c 2005-05-23 14:10:02.549869936 +0900
+++ 2.6.9-diskdump-1.0/drivers/block/cciss_scsi.c 2005-05-23 14:14:43.841107168 +0900
@@ -48,6 +48,7 @@ static int sendcmd(
unsigned int log_unit,
__u8 page_code,
unsigned char *scsi3addr,
+ int block_nr,
int cmd_type);
|