]> err.no Git - linux-2.6/blobdiff - drivers/scsi/aacraid/commsup.c
[SCSI] aacraid: aacraid: AIF preallocation (update)
[linux-2.6] / drivers / scsi / aacraid / commsup.c
index e2720b7be652c3ce966673bd12fa020e79bd7bb8..3741be2f4bfa57dff27b5e51e67a09137edf6ea2 100644 (file)
@@ -25,7 +25,7 @@
  *  commsup.c
  *
  * Abstract: Contain all routines that are required for FSA host/adapter
- *    commuication.
+ *    communication.
  *
  */
 
@@ -38,6 +38,7 @@
 #include <linux/slab.h>
 #include <linux/completion.h>
 #include <linux/blkdev.h>
+#include <scsi/scsi_host.h>
 #include <asm/semaphore.h>
 
 #include "aacraid.h"
  
 static int fib_map_alloc(struct aac_dev *dev)
 {
-       if((dev->hw_fib_va = pci_alloc_consistent(dev->pdev, sizeof(struct hw_fib) * AAC_NUM_FIB, &dev->hw_fib_pa))==NULL)
+       dprintk((KERN_INFO
+         "allocate hardware fibs pci_alloc_consistent(%p, %d * (%d + %d), %p)\n",
+         dev->pdev, dev->max_fib_size, dev->scsi_host_ptr->can_queue,
+         AAC_NUM_MGT_FIB, &dev->hw_fib_pa));
+       if((dev->hw_fib_va = pci_alloc_consistent(dev->pdev, dev->max_fib_size
+         * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB),
+         &dev->hw_fib_pa))==NULL)
                return -ENOMEM;
        return 0;
 }
@@ -67,7 +74,7 @@ static int fib_map_alloc(struct aac_dev *dev)
 
 void fib_map_free(struct aac_dev *dev)
 {
-       pci_free_consistent(dev->pdev, sizeof(struct hw_fib) * AAC_NUM_FIB, dev->hw_fib_va, dev->hw_fib_pa);
+       pci_free_consistent(dev->pdev, dev->max_fib_size * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB), dev->hw_fib_va, dev->hw_fib_pa);
 }
 
 /**
@@ -84,17 +91,22 @@ int fib_setup(struct aac_dev * dev)
        struct hw_fib *hw_fib_va;
        dma_addr_t hw_fib_pa;
        int i;
-       
-       if(fib_map_alloc(dev)<0)
+
+       while (((i = fib_map_alloc(dev)) == -ENOMEM)
+        && (dev->scsi_host_ptr->can_queue > (64 - AAC_NUM_MGT_FIB))) {
+               dev->init->MaxIoCommands = cpu_to_le32((dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB) >> 1);
+               dev->scsi_host_ptr->can_queue = le32_to_cpu(dev->init->MaxIoCommands) - AAC_NUM_MGT_FIB;
+       }
+       if (i<0)
                return -ENOMEM;
                
        hw_fib_va = dev->hw_fib_va;
        hw_fib_pa = dev->hw_fib_pa;
-       memset(hw_fib_va, 0, sizeof(struct hw_fib) * AAC_NUM_FIB);
+       memset(hw_fib_va, 0, dev->max_fib_size * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB));
        /*
         *      Initialise the fibs
         */
-       for (i = 0, fibptr = &dev->fibs[i]; i < AAC_NUM_FIB; i++, fibptr++) 
+       for (i = 0, fibptr = &dev->fibs[i]; i < (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB); i++, fibptr++) 
        {
                fibptr->dev = dev;
                fibptr->hw_fib = hw_fib_va;
@@ -103,15 +115,15 @@ int fib_setup(struct aac_dev * dev)
                init_MUTEX_LOCKED(&fibptr->event_wait);
                spin_lock_init(&fibptr->event_lock);
                hw_fib_va->header.XferState = cpu_to_le32(0xffffffff);
-               hw_fib_va->header.SenderSize = cpu_to_le16(sizeof(struct hw_fib));
+               hw_fib_va->header.SenderSize = cpu_to_le16(dev->max_fib_size);
                fibptr->hw_fib_pa = hw_fib_pa;
-               hw_fib_va = (struct hw_fib *)((unsigned char *)hw_fib_va + sizeof(struct hw_fib));
-               hw_fib_pa = hw_fib_pa + sizeof(struct hw_fib); 
+               hw_fib_va = (struct hw_fib *)((unsigned char *)hw_fib_va + dev->max_fib_size);
+               hw_fib_pa = hw_fib_pa + dev->max_fib_size;
        }
        /*
         *      Add the fib chain to the free list
         */
-       dev->fibs[AAC_NUM_FIB-1].next = NULL;
+       dev->fibs[dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB - 1].next = NULL;
        /*
         *      Enable this to debug out of queue space
         */
@@ -124,7 +136,7 @@ int fib_setup(struct aac_dev * dev)
  *     @dev: Adapter to allocate the fib for
  *
  *     Allocate a fib from the adapter fib pool. If the pool is empty we
- *     wait for fibs to become free.
+ *     return NULL.
  */
  
 struct fib * fib_alloc(struct aac_dev *dev)
@@ -133,10 +145,10 @@ struct fib * fib_alloc(struct aac_dev *dev)
        unsigned long flags;
        spin_lock_irqsave(&dev->fib_lock, flags);
        fibptr = dev->free_fib; 
-       /* Cannot sleep here or you get hangs. Instead we did the
-          maths at compile time. */
-       if(!fibptr)
-               BUG();
+       if(!fibptr){
+               spin_unlock_irqrestore(&dev->fib_lock, flags);
+               return fibptr;
+       }
        dev->free_fib = fibptr->next;
        spin_unlock_irqrestore(&dev->fib_lock, flags);
        /*
@@ -196,11 +208,11 @@ void fib_init(struct fib *fibptr)
        struct hw_fib *hw_fib = fibptr->hw_fib;
 
        hw_fib->header.StructType = FIB_MAGIC;
-       hw_fib->header.Size = cpu_to_le16(sizeof(struct hw_fib));
-        hw_fib->header.XferState = cpu_to_le32(HostOwned | FibInitialized | FibEmpty | FastResponseCapable);
+       hw_fib->header.Size = cpu_to_le16(fibptr->dev->max_fib_size);
+       hw_fib->header.XferState = cpu_to_le32(HostOwned | FibInitialized | FibEmpty | FastResponseCapable);
        hw_fib->header.SenderFibAddress = cpu_to_le32(fibptr->hw_fib_pa);
        hw_fib->header.ReceiverFibAddress = cpu_to_le32(fibptr->hw_fib_pa);
-       hw_fib->header.SenderSize = cpu_to_le16(sizeof(struct hw_fib));
+       hw_fib->header.SenderSize = cpu_to_le16(fibptr->dev->max_fib_size);
 }
 
 /**
@@ -242,6 +254,7 @@ static void fib_dealloc(struct fib * fibptr)
 static int aac_get_entry (struct aac_dev * dev, u32 qid, struct aac_entry **entry, u32 * index, unsigned long *nonotify)
 {
        struct aac_queue * q;
+       unsigned long idx;
 
        /*
         *      All of the queues wrap when they reach the end, so we check
@@ -251,10 +264,23 @@ static int aac_get_entry (struct aac_dev * dev, u32 qid, struct aac_entry **entr
         */
 
        q = &dev->queues->queue[qid];
-       
-       *index = le32_to_cpu(*(q->headers.producer));
-       if ((*index - 2) == le32_to_cpu(*(q->headers.consumer)))
+
+       idx = *index = le32_to_cpu(*(q->headers.producer));
+       /* Interrupt Moderation, only interrupt for first two entries */
+       if (idx != le32_to_cpu(*(q->headers.consumer))) {
+               if (--idx == 0) {
+                       if (qid == AdapHighCmdQueue)
+                               idx = ADAP_HIGH_CMD_ENTRIES;
+                       else if (qid == AdapNormCmdQueue)
+                               idx = ADAP_NORM_CMD_ENTRIES;
+                       else if (qid == AdapHighRespQueue) 
+                               idx = ADAP_HIGH_RESP_ENTRIES;
+                       else if (qid == AdapNormRespQueue) 
+                               idx = ADAP_NORM_RESP_ENTRIES;
+               }
+               if (idx != le32_to_cpu(*(q->headers.consumer)))
                        *nonotify = 1; 
+       }
 
        if (qid == AdapHighCmdQueue) {
                if (*index >= ADAP_HIGH_CMD_ENTRIES)
@@ -279,7 +305,7 @@ static int aac_get_entry (struct aac_dev * dev, u32 qid, struct aac_entry **entr
        }
 
         if ((*index + 1) == le32_to_cpu(*(q->headers.consumer))) { /* Queue is full */
-               printk(KERN_WARNING "Queue %d full, %d outstanding.\n",
+               printk(KERN_WARNING "Queue %d full, %u outstanding.\n",
                                qid, q->numpending);
                return 0;
        } else {
@@ -743,22 +769,25 @@ int fib_complete(struct fib * fibptr)
 
 void aac_printf(struct aac_dev *dev, u32 val)
 {
-       int length = val & 0xffff;
-       int level = (val >> 16) & 0xffff;
        char *cp = dev->printfbuf;
-       
-       /*
-        *      The size of the printfbuf is set in port.c
-        *      There is no variable or define for it
-        */
-       if (length > 255)
-               length = 255;
-       if (cp[length] != 0)
-               cp[length] = 0;
-       if (level == LOG_AAC_HIGH_ERROR)
-               printk(KERN_WARNING "aacraid:%s", cp);
-       else
-               printk(KERN_INFO "aacraid:%s", cp);
+       if (dev->printf_enabled)
+       {
+               int length = val & 0xffff;
+               int level = (val >> 16) & 0xffff;
+               
+               /*
+                *      The size of the printfbuf is set in port.c
+                *      There is no variable or define for it
+                */
+               if (length > 255)
+                       length = 255;
+               if (cp[length] != 0)
+                       cp[length] = 0;
+               if (level == LOG_AAC_HIGH_ERROR)
+                       printk(KERN_WARNING "aacraid:%s", cp);
+               else
+                       printk(KERN_INFO "aacraid:%s", cp);
+       }
        memset(cp, 0,  256);
 }
 
@@ -776,7 +805,6 @@ int aac_command_thread(struct aac_dev * dev)
 {
        struct hw_fib *hw_fib, *hw_newfib;
        struct fib *fib, *newfib;
-       struct aac_queue_block *queues = dev->queues;
        struct aac_fib_context *fibctx;
        unsigned long flags;
        DECLARE_WAITQUEUE(wait, current);
@@ -796,21 +824,22 @@ int aac_command_thread(struct aac_dev * dev)
         *      Let the DPC know it has a place to send the AIF's to.
         */
        dev->aif_thread = 1;
-       add_wait_queue(&queues->queue[HostNormCmdQueue].cmdready, &wait);
+       add_wait_queue(&dev->queues->queue[HostNormCmdQueue].cmdready, &wait);
        set_current_state(TASK_INTERRUPTIBLE);
+       dprintk ((KERN_INFO "aac_command_thread start\n"));
        while(1) 
        {
-               spin_lock_irqsave(queues->queue[HostNormCmdQueue].lock, flags);
-               while(!list_empty(&(queues->queue[HostNormCmdQueue].cmdq))) {
+               spin_lock_irqsave(dev->queues->queue[HostNormCmdQueue].lock, flags);
+               while(!list_empty(&(dev->queues->queue[HostNormCmdQueue].cmdq))) {
                        struct list_head *entry;
                        struct aac_aifcmd * aifcmd;
 
                        set_current_state(TASK_RUNNING);
-               
-                       entry = queues->queue[HostNormCmdQueue].cmdq.next;
+       
+                       entry = dev->queues->queue[HostNormCmdQueue].cmdq.next;
                        list_del(entry);
-                       
-                       spin_unlock_irqrestore(queues->queue[HostNormCmdQueue].lock, flags);
+               
+                       spin_unlock_irqrestore(dev->queues->queue[HostNormCmdQueue].lock, flags);
                        fib = list_entry(entry, struct fib, fiblink);
                        /*
                         *      We will process the FIB here or pass it to a 
@@ -840,9 +869,54 @@ int aac_command_thread(struct aac_dev * dev)
                                   
                                u32 time_now, time_last;
                                unsigned long flagv;
+                               unsigned num;
+                               struct hw_fib ** hw_fib_pool, ** hw_fib_p;
+                               struct fib ** fib_pool, ** fib_p;
                                
                                time_now = jiffies/HZ;
 
+                               /*
+                                * Warning: no sleep allowed while
+                                * holding spinlock. We take the estimate
+                                * and pre-allocate a set of fibs outside the
+                                * lock.
+                                */
+                               num = le32_to_cpu(dev->init->AdapterFibsSize)
+                                   / sizeof(struct hw_fib); /* some extra */
+                               spin_lock_irqsave(&dev->fib_lock, flagv);
+                               entry = dev->fib_list.next;
+                               while (entry != &dev->fib_list) {
+                                       entry = entry->next;
+                                       ++num;
+                               }
+                               spin_unlock_irqrestore(&dev->fib_lock, flagv);
+                               hw_fib_pool = NULL;
+                               fib_pool = NULL;
+                               if (num
+                                && ((hw_fib_pool = kmalloc(sizeof(struct hw_fib *) * num, GFP_KERNEL)))
+                                && ((fib_pool = kmalloc(sizeof(struct fib *) * num, GFP_KERNEL)))) {
+                                       hw_fib_p = hw_fib_pool;
+                                       fib_p = fib_pool;
+                                       while (hw_fib_p < &hw_fib_pool[num]) {
+                                               if (!(*(hw_fib_p++) = kmalloc(sizeof(struct hw_fib), GFP_KERNEL))) {
+                                                       --hw_fib_p;
+                                                       break;
+                                               }
+                                               if (!(*(fib_p++) = kmalloc(sizeof(struct fib), GFP_KERNEL))) {
+                                                       kfree(*(--hw_fib_p));
+                                                       break;
+                                               }
+                                       }
+                                       if ((num = hw_fib_p - hw_fib_pool) == 0) {
+                                               kfree(fib_pool);
+                                               fib_pool = NULL;
+                                               kfree(hw_fib_pool);
+                                               hw_fib_pool = NULL;
+                                       }
+                               } else if (hw_fib_pool) {
+                                       kfree(hw_fib_pool);
+                                       hw_fib_pool = NULL;
+                               }
                                spin_lock_irqsave(&dev->fib_lock, flagv);
                                entry = dev->fib_list.next;
                                /*
@@ -851,6 +925,8 @@ int aac_command_thread(struct aac_dev * dev)
                                 * fib, and then set the event to wake up the
                                 * thread that is waiting for it.
                                 */
+                               hw_fib_p = hw_fib_pool;
+                               fib_p = fib_pool;
                                while (entry != &dev->fib_list) {
                                        /*
                                         * Extract the fibctx
@@ -883,9 +959,11 @@ int aac_command_thread(struct aac_dev * dev)
                                         * Warning: no sleep allowed while
                                         * holding spinlock
                                         */
-                                       hw_newfib = kmalloc(sizeof(struct hw_fib), GFP_ATOMIC);
-                                       newfib = kmalloc(sizeof(struct fib), GFP_ATOMIC);
-                                       if (newfib && hw_newfib) {
+                                       if (hw_fib_p < &hw_fib_pool[num]) {
+                                               hw_newfib = *hw_fib_p;
+                                               *(hw_fib_p++) = NULL;
+                                               newfib = *fib_p;
+                                               *(fib_p++) = NULL;
                                                /*
                                                 * Make the copy of the FIB
                                                 */
@@ -900,15 +978,11 @@ int aac_command_thread(struct aac_dev * dev)
                                                fibctx->count++;
                                                /* 
                                                 * Set the event to wake up the
-                                                * thread that will waiting.
+                                                * thread that is waiting.
                                                 */
                                                up(&fibctx->wait_sem);
                                        } else {
                                                printk(KERN_WARNING "aifd: didn't allocate NewFib.\n");
-                                               if(newfib)
-                                                       kfree(newfib);
-                                               if(hw_newfib)
-                                                       kfree(hw_newfib);
                                        }
                                        entry = entry->next;
                                }
@@ -918,21 +992,38 @@ int aac_command_thread(struct aac_dev * dev)
                                *(__le32 *)hw_fib->data = cpu_to_le32(ST_OK);
                                fib_adapter_complete(fib, sizeof(u32));
                                spin_unlock_irqrestore(&dev->fib_lock, flagv);
+                               /* Free up the remaining resources */
+                               hw_fib_p = hw_fib_pool;
+                               fib_p = fib_pool;
+                               while (hw_fib_p < &hw_fib_pool[num]) {
+                                       if (*hw_fib_p)
+                                               kfree(*hw_fib_p);
+                                       if (*fib_p)
+                                               kfree(*fib_p);
+                                       ++fib_p;
+                                       ++hw_fib_p;
+                               }
+                               if (hw_fib_pool)
+                                       kfree(hw_fib_pool);
+                               if (fib_pool)
+                                       kfree(fib_pool);
                        }
-                       spin_lock_irqsave(queues->queue[HostNormCmdQueue].lock, flags);
                        kfree(fib);
+                       spin_lock_irqsave(dev->queues->queue[HostNormCmdQueue].lock, flags);
                }
                /*
                 *      There are no more AIF's
                 */
-               spin_unlock_irqrestore(queues->queue[HostNormCmdQueue].lock, flags);
+               spin_unlock_irqrestore(dev->queues->queue[HostNormCmdQueue].lock, flags);
                schedule();
 
                if(signal_pending(current))
                        break;
                set_current_state(TASK_INTERRUPTIBLE);
        }
-       remove_wait_queue(&queues->queue[HostNormCmdQueue].cmdready, &wait);
+       if (dev->queues)
+               remove_wait_queue(&dev->queues->queue[HostNormCmdQueue].cmdready, &wait);
        dev->aif_thread = 0;
        complete_and_exit(&dev->aif_completion, 0);
+       return 0;
 }