patch-2.4.2 linux/drivers/block/cciss.c

Next file: linux/drivers/block/cciss.h
Previous file: linux/drivers/block/ataflop.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.1/linux/drivers/block/cciss.c linux/drivers/block/cciss.c
@@ -26,7 +26,7 @@
 #include <linux/types.h>
 #include <linux/pci.h>
 #include <linux/kernel.h>
-#include <linux/malloc.h>
+#include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/major.h>
 #include <linux/fs.h>
@@ -44,8 +44,8 @@
 #include <linux/genhd.h>
 
 #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin))
-#define DRIVER_NAME "Compaq CISS Driver (v 2.4.0)"
-#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,4,0)
+#define DRIVER_NAME "Compaq CISS Driver (v 2.4.2)"
+#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,4,2)
 
 /* Embedded module documentation macros - see modules.h */
 MODULE_AUTHOR("Charles M. White III - Compaq Computer Corporation");
@@ -63,6 +63,8 @@
  */
 static struct board_type products[] = {
 	{ 0x40700E11, "Smart Array 5300",	&SA5_access },
+	{ 0x40800E11, "Smart Array 5i", &SA5B_access},
+	{ 0x40820E11, "Smart Array 532", &SA5B_access},
 };
 
 /* How long to wait (in millesconds) for board to go into simple mode */
@@ -76,23 +78,7 @@
 
 static struct proc_dir_entry *proc_cciss;
 
-static void do_cciss_request(int i);
-/*
- * This is a hack.  This driver eats a major number for each controller, and
- * sets blkdev[xxx].request_fn to each one of these so the real request
- * function knows what controller its working with.
- */
-#define DO_CCISS_REQUEST(x) { do_cciss_request(x); }
-
-static void do_cciss_request0(request_queue_t * q) DO_CCISS_REQUEST(0);
-static void do_cciss_request1(request_queue_t * q) DO_CCISS_REQUEST(1);
-static void do_cciss_request2(request_queue_t * q) DO_CCISS_REQUEST(2);
-static void do_cciss_request3(request_queue_t * q) DO_CCISS_REQUEST(3);
-static void do_cciss_request4(request_queue_t * q) DO_CCISS_REQUEST(4);
-static void do_cciss_request5(request_queue_t * q) DO_CCISS_REQUEST(5);
-static void do_cciss_request6(request_queue_t * q) DO_CCISS_REQUEST(6);
-static void do_cciss_request7(request_queue_t * q) DO_CCISS_REQUEST(7);
-
+static void do_cciss_request(request_queue_t *q);
 static int cciss_open(struct inode *inode, struct file *filep);
 static int cciss_release(struct inode *inode, struct file *filep);
 static int cciss_ioctl(struct inode *inode, struct file *filep, 
@@ -693,7 +679,7 @@
         spin_lock_irqsave(&io_request_lock, flags);
         if (hba[ctlr]->drv[target].usage_count > maxusage) {
                 spin_unlock_irqrestore(&io_request_lock, flags);
-                printk(KERN_WARNING "cpqarray: Device busy for "
+                printk(KERN_WARNING "cciss: Device busy for "
                         "revalidation (usage=%d)\n",
                         hba[ctlr]->drv[target].usage_count);
                 return -EBUSY;
@@ -1175,48 +1161,85 @@
 	}
 	complete_buffers(cmd->bh, status);
 }
+
+
+static inline int cpq_new_segment(request_queue_t *q, struct request *rq,
+                                  int max_segments)
+{
+        if (rq->nr_segments < MAXSGENTRIES) {
+                rq->nr_segments++;
+                return 1;
+        }
+        return 0;
+}
+
+static int cpq_back_merge_fn(request_queue_t *q, struct request *rq,
+                             struct buffer_head *bh, int max_segments)
+{
+        if (rq->bhtail->b_data + rq->bhtail->b_size == bh->b_data)
+                return 1;
+        return cpq_new_segment(q, rq, max_segments);
+}
+
+static int cpq_front_merge_fn(request_queue_t *q, struct request *rq,
+                             struct buffer_head *bh, int max_segments)
+{
+        if (bh->b_data + bh->b_size == rq->bh->b_data)
+                return 1;
+        return cpq_new_segment(q, rq, max_segments);
+}
+
+static int cpq_merge_requests_fn(request_queue_t *q, struct request *rq,
+                                 struct request *nxt, int max_segments)
+{
+        int total_segments = rq->nr_segments + nxt->nr_segments;
+
+        if (rq->bhtail->b_data + rq->bhtail->b_size == nxt->bh->b_data)
+                total_segments--;
+
+        if (total_segments > MAXSGENTRIES)
+                return 0;
+
+        rq->nr_segments = total_segments;
+        return 1;
+}
+
 /* 
  * Get a request and submit it to the controller. 
  * Currently we do one request at a time.  Ideally we would like to send
  * everything to the controller on the first call, but there is a danger
  * of holding the io_request_lock for to long.  
  */
-static void do_cciss_request(int ctlr)
+static void do_cciss_request(request_queue_t *q)
 {
-	ctlr_info_t *h= hba[ctlr];
+	ctlr_info_t *h= q->queuedata; 
 	CommandList_struct *c;
 	int log_unit, start_blk, seg, sect;
 	char *lastdataend;
 	struct buffer_head *bh;
-	struct list_head *queue_head;
+	struct list_head *queue_head = &q->queue_head;
 	struct request *creq;
 	u64bit temp64;
 
-	queue_head = &blk_dev[MAJOR_NR+ctlr].request_queue.queue_head;	
-	if (list_empty(queue_head))
-	{
-		/* nothing to do... */
-		start_io(h);
-		return;
-	}
+	if (q->plugged || list_empty(queue_head)) {
+                start_io(h);
+                return;
+        }
+
 	creq =	blkdev_entry_next_request(queue_head); 
-	if ((creq == NULL) || (creq->rq_status == RQ_INACTIVE))
-	{
-		/* nothing to do... restart processing and return */ 
-		start_io(h);
-		return;
-	}
-	if ((ctlr != (MAJOR(creq->rq_dev)-MAJOR_NR)) || (ctlr > nr_ctlr)
-		|| (h == NULL))
-	{
-#ifdef CCISS_DEBUG
-		printk(KERN_WARNING "cciss: doreq cmd of %d, %x at %p\n",
-			ctlr, creq->rq_dev, creq);
-#endif /* CCISS_DEBUG */
-		complete_buffers(creq->bh, 0);
-		start_io(h); 
-		return;
-	}
+	if (creq->nr_segments > MAXSGENTRIES)
+                BUG();
+
+        if ((h->ctlr != MAJOR(creq->rq_dev)-MAJOR_NR) || (h->ctlr > nr_ctlr))
+        {
+                printk(KERN_WARNING "doreq cmd for %d, %x at %p\n",
+                                h->ctlr, creq->rq_dev, creq);
+                blkdev_dequeue_request(creq);
+                complete_buffers(creq->bh, 0);
+                start_io(h);
+                return;
+        }
+
 	if (( c = cmd_alloc(h)) == NULL)
 	{
 		start_io(h);
@@ -1229,7 +1252,7 @@
 	log_unit = MINOR(creq->rq_dev) >> NWD_SHIFT; 
 	c->Header.ReplyQueue = 0;  // unused in simple mode
 	c->Header.Tag.lower = c->busaddr;  // use the physical address the cmd block for tag
-	c->Header.LUN.LogDev.VolId= hba[ctlr]->drv[log_unit].LunID;
+	c->Header.LUN.LogDev.VolId= hba[h->ctlr]->drv[log_unit].LunID;
 	c->Header.LUN.LogDev.Mode = 1;
 	c->Request.CDBLen = 10; // 12 byte commands not in FW yet;
 	c->Request.Type.Type =  TYPE_CMD; // It is a command. 
@@ -1238,10 +1261,10 @@
 		(creq->cmd == READ) ? XFER_READ: XFER_WRITE; 
 	c->Request.Timeout = 0; // Don't time out	
 	c->Request.CDB[0] = (creq->cmd == READ) ? CCISS_READ : CCISS_WRITE;
-	start_blk = hba[ctlr]->hd[MINOR(creq->rq_dev)].start_sect + creq->sector;
+	start_blk = hba[h->ctlr]->hd[MINOR(creq->rq_dev)].start_sect + creq->sector;
+#ifdef CCISS_DEBUG
 	if (bh == NULL)
 		panic("cciss: bh== NULL?");
-#ifdef CCISS_DEBUG
 	printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n",(int) creq->sector,
 		(int) creq->nr_sectors);	
 #endif /* CCISS_DEBUG */
@@ -1251,28 +1274,21 @@
 	while(bh)
 	{
 		sect += bh->b_size/512;
-		if (bh->b_size % 512)
-		{
-			printk(KERN_CRIT "cciss:  Oh Man.  %d+%d, size=%d\n", 
-				(int) creq->sector, sect, (int) bh->b_size);
-			panic("b_size 512 != 0\n");
-		}
 		if (bh->b_data == lastdataend)
 		{  // tack it on to the last segment 
 			c->SG[seg-1].Len +=bh->b_size;
 			lastdataend += bh->b_size;
 		} else
 		{
+			if (seg == MAXSGENTRIES)
+				BUG();
 			c->SG[seg].Len = bh->b_size;
 			temp64.val = (__u64) virt_to_bus(bh->b_data);
 			c->SG[seg].Addr.lower = temp64.val32.lower;
 			c->SG[seg].Addr.upper = temp64.val32.upper;
 			c->SG[0].Ext = 0;  // we are not chaining
 			lastdataend = bh->b_data + bh->b_size;
-			if( ++seg == MAXSGENTRIES)
-			{
-				break; 
-			}
+			seg++;
 		}
 		bh = bh->b_reqnext;
 	}
@@ -1280,10 +1296,6 @@
 	if( seg > h->maxSG)
 		h->maxSG = seg; 
 
-	/* adjusting the remaining request, if any */ 
-	creq-> sector+= sect;
-	creq->nr_sectors -= sect; 
-
 #ifdef CCISS_DEBUG
 	printk(KERN_DEBUG "cciss: Submitting %d sectors in %d segments\n", sect, seg);
 #endif /* CCISS_DEBUG */
@@ -1295,31 +1307,23 @@
 	c->Request.CDB[4]= (start_blk >>  8) & 0xff;
 	c->Request.CDB[5]= start_blk & 0xff;
 	c->Request.CDB[6]= 0; // (sect >> 24) & 0xff; MSB
-	// c->Request.CDB[7]= (sect >> 16) & 0xff; 
 	c->Request.CDB[7]= (sect >>  8) & 0xff; 
 	c->Request.CDB[8]= sect & 0xff; 
 	c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
 
-	/* check to see if we going to complete the entire request */ 
-	/* if so, mark this request as Done and ready the next one */ 
-	if (creq->nr_sectors)
-	{
-#ifdef CCISS_DEBUG
-		printk(KERN_DEBUG "cciss: More to do on the same request %p %ld\n", 
-			creq, creq->nr_sectors);
-#endif /* CCISS_DEBUG */
+			
+	blkdev_dequeue_request(creq);
 
-		creq->bh = bh->b_reqnext;
-		bh->b_reqnext = NULL;
-	} else
-	{
+	
+        /*
+         * ehh, we can't really end the request here since it's not
+         * even started yet. for now it shouldn't hurt though
+         */
 #ifdef CCISS_DEBUG
-		printk("cciss: Done with %p, queueing %p\n", creq);
-#endif /* CCISS_DEBUG */
-			
-		blkdev_dequeue_request(creq);
-		end_that_request_last(creq);
-	}
+	printk("Done with %p\n", creq);
+#endif /* CCISS_DEBUG */ 
+	end_that_request_last(creq);
+
 	addQ(&(h->reqQ),c);
 	h->Qdepth++;
 	if(h->Qdepth > h->maxQsinceinit)
@@ -1352,7 +1356,7 @@
 			a &= ~3;
 			if ((c = h->cmpQ) == NULL)
 			{  
-				printk(KERN_WARNING "cpqarray: Completion of %08lx ignored\n", (unsigned long)a1);
+				printk(KERN_WARNING "cciss: Completion of %08lx ignored\n", (unsigned long)a1);
 				continue;	
 			} 
 			while(c->busaddr != a) {
@@ -1379,7 +1383,7 @@
 	/*
 	 * See if we can queue up some more IO
 	 */
-	do_cciss_request(h->ctlr);
+	do_cciss_request(BLK_DEFAULT_QUEUE(MAJOR_NR + h->ctlr));
 	spin_unlock_irqrestore(&io_request_lock, flags);
 }
 /* 
@@ -1420,7 +1424,7 @@
 	printk("   Heartbeat Counter = 0x%x\n\n\n", 
 			readl(&(tb->HeartBeat)));
 }
-#endif /* CCISS_DEBUG */
+#endif /* CCISS_DEBUG */ 
 
 static int cciss_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn)
 {
@@ -1430,7 +1434,9 @@
 	uint addr[6];
 	__u32 board_id;
 	struct pci_dev *pdev;
-
+	int cfg_offset;
+	int cfg_base_addr;
+	int cfg_base_addr_index;
 	int i;
 
 	pdev = pci_find_slot(bus, device_fn);
@@ -1473,15 +1479,38 @@
 	 * Memory base addr is first addr , the second points to the config
          *   table
 	 */
-	c->paddr = pci_resource_start(pdev, 0);
-	c->vaddr = remap_pci_mem(c->paddr, 128);
-	c->cfgtable = (CfgTable_struct *) remap_pci_mem(addr[1], 
-						sizeof(CfgTable_struct));
+
+	c->paddr = addr[0] & 0xfffffff0; /* remove the addressing mode bits */
+#ifdef CCISS_DEBUG
+	printk("address 0 = %x\n", c->paddr);
+#endif /* CCISS_DEBUG */ 
+	c->vaddr = remap_pci_mem(c->paddr, 200);
+
+	/* get the address index number */
+	cfg_base_addr = readl(c->vaddr + SA5_CTCFG_OFFSET);
+	/* I am not prepared to deal with a 64 bit address value */
+	cfg_base_addr &= 0xffff;
+#ifdef CCISS_DEBUG
+	printk("cfg base address = %x\n", cfg_base_addr);
+#endif /* CCISS_DEBUG */
+	cfg_base_addr_index = (cfg_base_addr  - PCI_BASE_ADDRESS_0)/4;
+#ifdef CCISS_DEBUG
+	printk("cfg base address index = %x\n", cfg_base_addr_index);
+#endif /* CCISS_DEBUG */
+
+	cfg_offset = readl(c->vaddr + SA5_CTMEM_OFFSET);
+#ifdef CCISS_DEBUG
+	printk("cfg offset = %x\n", cfg_offset);
+#endif /* CCISS_DEBUG */
+	c->cfgtable = (CfgTable_struct *) 
+		remap_pci_mem((addr[cfg_base_addr_index] & 0xfffffff0)
+				+ cfg_offset, sizeof(CfgTable_struct));
 	c->board_id = board_id;
 
 #ifdef CCISS_DEBUG
 	print_cfg_table(c->cfgtable); 
 #endif /* CCISS_DEBUG */
+
 	for(i=0; i<NR_PRODUCTS; i++) {
 		if (board_id == products[i].board_id) {
 			c->product_name = products[i].product_name;
@@ -1495,6 +1524,14 @@
 				(unsigned long)board_id);
 		return -1;
 	}
+	if (  (readb(&c->cfgtable->Signature[0]) != 'C') ||
+	      (readb(&c->cfgtable->Signature[1]) != 'I') ||
+	      (readb(&c->cfgtable->Signature[2]) != 'S') ||
+	      (readb(&c->cfgtable->Signature[3]) != 'S') )
+	{
+		printk("Does not appear to be a valid CISS config table\n");
+		return -1;
+	}
 #ifdef CCISS_DEBUG
 	printk("Trying to put board into Simple mode\n");
 #endif /* CCISS_DEBUG */ 
@@ -1536,14 +1573,22 @@
 
 	int index;
 	unchar bus=0, dev_fn=0;
-	
+
+	#define CCISS_BOARD_TYPES 2
+	static int cciss_device_id[CCISS_BOARD_TYPES] = { 
+		PCI_DEVICE_ID_COMPAQ_CISS, PCI_DEVICE_ID_COMPAQ_CISSB};
+	int brdtype;
+
+	/* search for all PCI board types that could be for this driver */
+	for(brdtype=0; brdtype<CCISS_BOARD_TYPES; brdtype++)
+	{
 		for(index=0; ; index++) {
 			if (pcibios_find_device(PCI_VENDOR_ID_COMPAQ,
-			 	PCI_DEVICE_ID_COMPAQ_CISS, 
+			 	cciss_device_id[brdtype], 
 					index, &bus, &dev_fn))
 				break;
 			printk(KERN_DEBUG "cciss: Device %x has been found at %x %x\n",
-				PCI_DEVICE_ID_COMPAQ_CISS, bus, dev_fn);
+				cciss_device_id[brdtype], bus, dev_fn);
 			if (index == 1000000) break;
 			if (nr_ctlr == 8) {
 				printk(KERN_WARNING "cciss: This driver"
@@ -1569,6 +1614,7 @@
 			nr_ctlr++;
 
 		}
+	}
 	return nr_ctlr;
 
 }
@@ -1698,8 +1744,8 @@
 			printk(KERN_WARNING "cciss: read capacity failed\n");
 			total_size = block_size = 0; 
 		}	
-		printk("      blocks= %d block_size= %d\n", total_size,
-					block_size);
+		printk(KERN_INFO "      blocks= %d block_size= %d\n", 
+					total_size, block_size);
 
 		/* Execute the command to read the disk geometry */
 		memset(inq_buff, 0, sizeof(InquiryData_struct));
@@ -1759,13 +1805,7 @@
 {
 	int num_cntlrs_reg = 0;
 	int i,j;
-
-	void (*request_fns[MAX_CTLR])(request_queue_t *) = {
-                do_cciss_request0, do_cciss_request1,
-                do_cciss_request2, do_cciss_request3,
-                do_cciss_request4, do_cciss_request5,
-                do_cciss_request6, do_cciss_request7,
-        };
+	request_queue_t *q;
 
 	/* detect controllers */
 	cciss_pci_detect();
@@ -1834,15 +1874,22 @@
 		hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON);
 
 		cciss_procinit(i);
-		
-		blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR+i),
-				request_fns[i]);
-		blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR+i), 0);
+
+		q = BLK_DEFAULT_QUEUE(MAJOR_NR + i);
+                q->queuedata = hba[i];
+                blk_init_queue(q, do_cciss_request);
+                blk_queue_headactive(q, 0);		
 
 		/* fill in the other Kernel structs */
 		blksize_size[MAJOR_NR+i] = hba[i]->blocksizes;
                 hardsect_size[MAJOR_NR+i] = hba[i]->hardsizes;
                 read_ahead[MAJOR_NR+i] = READ_AHEAD;
+
+		/* Set the pointers to queue functions */ 
+		q->back_merge_fn = cpq_back_merge_fn;
+                q->front_merge_fn = cpq_front_merge_fn;
+                q->merge_requests_fn = cpq_merge_requests_fn;
+
 
 		/* Fill in the gendisk data */ 	
 		hba[i]->gendisk.major = MAJOR_NR + i;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)