X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fscsi%2Faacraid%2Fcommsup.c;h=4893a6d06a332ac6f32500f3e0ebc3a1a7183838;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=b5ac3c270d080e54bb1922924eaa217e4490c961;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index b5ac3c270..4893a6d06 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -25,7 +25,7 @@ * commsup.c * * Abstract: Contain all routines that are required for FSA host/adapter - * commuication. + * communication. * */ @@ -38,6 +38,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include "aacraid.h" @@ -52,49 +59,60 @@ 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; } /** - * fib_map_free - free the fib objects + * aac_fib_map_free - free the fib objects * @dev: Adapter to free * * Free the PCI mappings and the memory allocated for FIB blocks * on this adapter. */ -void fib_map_free(struct aac_dev *dev) +void aac_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); } /** - * fib_setup - setup the fibs + * aac_fib_setup - setup the fibs * @dev: Adapter to set up * * Allocate the PCI space for the fibs, map it and then intialise the * fib area, the unmapped fib data and also the free list */ -int fib_setup(struct aac_dev * dev) +int aac_fib_setup(struct aac_dev * dev) { struct fib *fibptr; 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 +121,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 */ @@ -120,23 +138,23 @@ int fib_setup(struct aac_dev * dev) } /** - * fib_alloc - allocate a fib + * aac_fib_alloc - allocate a fib * @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) +struct fib *aac_fib_alloc(struct aac_dev *dev) { struct fib * fibptr; 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); /* @@ -148,7 +166,7 @@ struct fib * fib_alloc(struct aac_dev *dev) * Null out fields that depend on being zero at the start of * each I/O */ - fibptr->hw_fib->header.XferState = cpu_to_le32(0); + fibptr->hw_fib->header.XferState = 0; fibptr->callback = NULL; fibptr->callback_data = NULL; @@ -156,14 +174,14 @@ struct fib * fib_alloc(struct aac_dev *dev) } /** - * fib_free - free a fib + * aac_fib_free - free a fib * @fibptr: fib to free up * * Frees up a fib and places it on the appropriate queue * (either free or timed out) */ -void fib_free(struct fib * fibptr) +void aac_fib_free(struct fib *fibptr) { unsigned long flags; @@ -174,8 +192,9 @@ void fib_free(struct fib * fibptr) fibptr->dev->timeout_fib = fibptr; } else { if (fibptr->hw_fib->header.XferState != 0) { - printk(KERN_WARNING "fib_free, XferState != 0, fibptr = 0x%p, XferState = 0x%x\n", - (void*)fibptr, fibptr->hw_fib->header.XferState); + printk(KERN_WARNING "aac_fib_free, XferState != 0, fibptr = 0x%p, XferState = 0x%x\n", + (void*)fibptr, + le32_to_cpu(fibptr->hw_fib->header.XferState)); } fibptr->next = fibptr->dev->free_fib; fibptr->dev->free_fib = fibptr; @@ -184,22 +203,22 @@ void fib_free(struct fib * fibptr) } /** - * fib_init - initialise a fib + * aac_fib_init - initialise a fib * @fibptr: The fib to initialize * * Set up the generic fib fields ready for use */ -void fib_init(struct fib *fibptr) +void aac_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.SenderFibAddress = cpu_to_le32(fibptr->hw_fib_pa); + 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 = 0; /* Filled in later if needed */ 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); } /** @@ -210,12 +229,11 @@ void fib_init(struct fib *fibptr) * caller. */ -void fib_dealloc(struct fib * fibptr) +static void fib_dealloc(struct fib * fibptr) { struct hw_fib *hw_fib = fibptr->hw_fib; - if(hw_fib->header.StructType != FIB_MAGIC) - BUG(); - hw_fib->header.XferState = cpu_to_le32(0); + BUG_ON(hw_fib->header.StructType != FIB_MAGIC); + hw_fib->header.XferState = 0; } /* @@ -241,6 +259,7 @@ 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 @@ -250,35 +269,30 @@ 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 == AdapNormCmdQueue) + idx = ADAP_NORM_CMD_ENTRIES; + else + idx = ADAP_NORM_RESP_ENTRIES; + } + if (idx != le32_to_cpu(*(q->headers.consumer))) *nonotify = 1; + } - if (qid == AdapHighCmdQueue) { - if (*index >= ADAP_HIGH_CMD_ENTRIES) - *index = 0; - } else if (qid == AdapNormCmdQueue) { + if (qid == AdapNormCmdQueue) { if (*index >= ADAP_NORM_CMD_ENTRIES) *index = 0; /* Wrap to front of the Producer Queue. */ - } - else if (qid == AdapHighRespQueue) - { - if (*index >= ADAP_HIGH_RESP_ENTRIES) - *index = 0; - } - else if (qid == AdapNormRespQueue) - { + } else { if (*index >= ADAP_NORM_RESP_ENTRIES) *index = 0; /* Wrap to front of the Producer Queue. */ } - else { - printk("aacraid: invalid qid\n"); - BUG(); - } 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 { @@ -307,12 +321,8 @@ static int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_f { struct aac_entry * entry = NULL; int map = 0; - struct aac_queue * q = &dev->queues->queue[qid]; - - spin_lock_irqsave(q->lock, q->SavedIrql); - if (qid == AdapHighCmdQueue || qid == AdapNormCmdQueue) - { + if (qid == AdapNormCmdQueue) { /* if no entries wait for some if caller wants to */ while (!aac_get_entry(dev, qid, &entry, index, nonotify)) { @@ -323,9 +333,7 @@ static int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_f */ entry->size = cpu_to_le32(le16_to_cpu(hw_fib->header.Size)); map = 1; - } - else if (qid == AdapHighRespQueue || qid == AdapNormRespQueue) - { + } else { while(!aac_get_entry(dev, qid, &entry, index, nonotify)) { /* if no entries wait for some if caller wants to */ @@ -344,43 +352,7 @@ static int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_f * in the queue entry. */ if (map) - entry->addr = fibptr->hw_fib_pa; - return 0; -} - - -/** - * aac_insert_entry - insert a queue entry - * @dev: Adapter - * @index: Index of entry to insert - * @qid: Queue number - * @nonotify: Suppress adapter notification - * - * Gets the next free QE off the requested priorty adapter command - * queue and associates the Fib with the QE. The QE represented by - * index is ready to insert on the queue when this routine returns - * success. - */ - -static int aac_insert_entry(struct aac_dev * dev, u32 index, u32 qid, unsigned long nonotify) -{ - struct aac_queue * q = &dev->queues->queue[qid]; - - if(q == NULL) - BUG(); - *(q->headers.producer) = cpu_to_le32(index + 1); - spin_unlock_irqrestore(q->lock, q->SavedIrql); - - if (qid == AdapHighCmdQueue || - qid == AdapNormCmdQueue || - qid == AdapHighRespQueue || - qid == AdapNormRespQueue) - { - if (!nonotify) - aac_adapter_notify(dev, qid); - } - else - printk("Suprise insert!\n"); + entry->addr = cpu_to_le32(fibptr->hw_fib_pa); return 0; } @@ -393,7 +365,7 @@ static int aac_insert_entry(struct aac_dev * dev, u32 index, u32 qid, unsigned l */ /** - * fib_send - send a fib to the adapter + * aac_fib_send - send a fib to the adapter * @command: Command to send * @fibptr: The fib * @size: Size of fib data area @@ -409,22 +381,23 @@ static int aac_insert_entry(struct aac_dev * dev, u32 index, u32 qid, unsigned l * response FIB is received from the adapter. */ -int fib_send(u16 command, struct fib * fibptr, unsigned long size, int priority, int wait, int reply, fib_callback callback, void * callback_data) +int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, + int priority, int wait, int reply, fib_callback callback, + void *callback_data) { - u32 index; - u32 qid; struct aac_dev * dev = fibptr->dev; - unsigned long nointr = 0; struct hw_fib * hw_fib = fibptr->hw_fib; struct aac_queue * q; unsigned long flags = 0; - if (!(le32_to_cpu(hw_fib->header.XferState) & HostOwned)) + unsigned long qflags; + + if (!(hw_fib->header.XferState & cpu_to_le32(HostOwned))) return -EBUSY; /* * There are 5 cases with the wait and reponse requested flags. * The only invalid cases are if the caller requests to wait and * does not request a response and if the caller does not want a - * response and the Fibis not allocated from pool. If a response + * response and the Fib is not allocated from pool. If a response * is not requesed the Fib will just be deallocaed by the DPC * routine when the response comes back from the adapter. No * further processing will be done besides deleting the Fib. We @@ -447,7 +420,7 @@ int fib_send(u16 command, struct fib * fibptr, unsigned long size, int priority * Map the fib into 32bits by using the fib number */ - hw_fib->header.SenderFibAddress = cpu_to_le32(((u32)(fibptr-dev->fibs)) << 1); + hw_fib->header.SenderFibAddress = cpu_to_le32(((u32)(fibptr - dev->fibs)) << 2); hw_fib->header.SenderData = (u32)(fibptr - dev->fibs); /* * Set FIB state to indicate where it came from and if we want a @@ -470,26 +443,8 @@ int fib_send(u16 command, struct fib * fibptr, unsigned long size, int priority * Get a queue entry connect the FIB to it and send an notify * the adapter a command is ready. */ - if (priority == FsaHigh) { - hw_fib->header.XferState |= cpu_to_le32(HighPriority); - qid = AdapHighCmdQueue; - } else { - hw_fib->header.XferState |= cpu_to_le32(NormalPriority); - qid = AdapNormCmdQueue; - } - q = &dev->queues->queue[qid]; + hw_fib->header.XferState |= cpu_to_le32(NormalPriority); - if(wait) - spin_lock_irqsave(&fibptr->event_lock, flags); - if(aac_queue_get( dev, &index, qid, hw_fib, 1, fibptr, &nointr)<0) - return -EWOULDBLOCK; - dprintk((KERN_DEBUG "fib_send: inserting a queue entry at index %d.\n",index)); - dprintk((KERN_DEBUG "Fib contents:.\n")); - dprintk((KERN_DEBUG " Command = %d.\n", hw_fib->header.Command)); - dprintk((KERN_DEBUG " XferState = %x.\n", hw_fib->header.XferState)); - dprintk((KERN_DEBUG " hw_fib va being sent=%p\n",fibptr->hw_fib)); - dprintk((KERN_DEBUG " hw_fib pa being sent=%lx\n",(ulong)fibptr->hw_fib_pa)); - dprintk((KERN_DEBUG " fib being sent=%p\n",fibptr)); /* * Fill in the Callback and CallbackContext if we are not * going to wait. @@ -498,24 +453,104 @@ int fib_send(u16 command, struct fib * fibptr, unsigned long size, int priority fibptr->callback = callback; fibptr->callback_data = callback_data; } - FIB_COUNTER_INCREMENT(aac_config.FibsSent); - list_add_tail(&fibptr->queue, &q->pendingq); - q->numpending++; fibptr->done = 0; fibptr->flags = 0; - if(aac_insert_entry(dev, index, qid, (nointr & aac_config.irq_mod)) < 0) - return -EWOULDBLOCK; + FIB_COUNTER_INCREMENT(aac_config.FibsSent); + + dprintk((KERN_DEBUG "Fib contents:.\n")); + dprintk((KERN_DEBUG " Command = %d.\n", le32_to_cpu(hw_fib->header.Command))); + dprintk((KERN_DEBUG " SubCommand = %d.\n", le32_to_cpu(((struct aac_query_mount *)fib_data(fibptr))->command))); + dprintk((KERN_DEBUG " XferState = %x.\n", le32_to_cpu(hw_fib->header.XferState))); + dprintk((KERN_DEBUG " hw_fib va being sent=%p\n",fibptr->hw_fib)); + dprintk((KERN_DEBUG " hw_fib pa being sent=%lx\n",(ulong)fibptr->hw_fib_pa)); + dprintk((KERN_DEBUG " fib being sent=%p\n",fibptr)); + + if (!dev->queues) + return -EBUSY; + q = &dev->queues->queue[AdapNormCmdQueue]; + + if(wait) + spin_lock_irqsave(&fibptr->event_lock, flags); + spin_lock_irqsave(q->lock, qflags); + if (dev->new_comm_interface) { + unsigned long count = 10000000L; /* 50 seconds */ + q->numpending++; + spin_unlock_irqrestore(q->lock, qflags); + while (aac_adapter_send(fibptr) != 0) { + if (--count == 0) { + if (wait) + spin_unlock_irqrestore(&fibptr->event_lock, flags); + spin_lock_irqsave(q->lock, qflags); + q->numpending--; + spin_unlock_irqrestore(q->lock, qflags); + return -ETIMEDOUT; + } + udelay(5); + } + } else { + u32 index; + unsigned long nointr = 0; + aac_queue_get( dev, &index, AdapNormCmdQueue, hw_fib, 1, fibptr, &nointr); + + q->numpending++; + *(q->headers.producer) = cpu_to_le32(index + 1); + spin_unlock_irqrestore(q->lock, qflags); + dprintk((KERN_DEBUG "aac_fib_send: inserting a queue entry at index %d.\n",index)); + if (!(nointr & aac_config.irq_mod)) + aac_adapter_notify(dev, AdapNormCmdQueue); + } + /* * If the caller wanted us to wait for response wait now. */ if (wait) { spin_unlock_irqrestore(&fibptr->event_lock, flags); - down(&fibptr->event_wait); - if(fibptr->done == 0) - BUG(); + /* Only set for first known interruptable command */ + if (wait < 0) { + /* + * *VERY* Dangerous to time out a command, the + * assumption is made that we have no hope of + * functioning because an interrupt routing or other + * hardware failure has occurred. + */ + unsigned long count = 36000000L; /* 3 minutes */ + while (down_trylock(&fibptr->event_wait)) { + int blink; + if (--count == 0) { + spin_lock_irqsave(q->lock, qflags); + q->numpending--; + spin_unlock_irqrestore(q->lock, qflags); + if (wait == -1) { + printk(KERN_ERR "aacraid: aac_fib_send: first asynchronous command timed out.\n" + "Usually a result of a PCI interrupt routing problem;\n" + "update mother board BIOS or consider utilizing one of\n" + "the SAFE mode kernel options (acpi, apic etc)\n"); + } + return -ETIMEDOUT; + } + if ((blink = aac_adapter_check_health(dev)) > 0) { + if (wait == -1) { + printk(KERN_ERR "aacraid: aac_fib_send: adapter blinkLED 0x%x.\n" + "Usually a result of a serious unrecoverable hardware problem\n", + blink); + } + return -EFAULT; + } + udelay(5); + } + } else if (down_interruptible(&fibptr->event_wait)) { + spin_lock_irqsave(&fibptr->event_lock, flags); + if (fibptr->done == 0) { + fibptr->done = 2; /* Tell interrupt we aborted */ + spin_unlock_irqrestore(&fibptr->event_lock, flags); + return -EINTR; + } + spin_unlock_irqrestore(&fibptr->event_lock, flags); + } + BUG_ON(fibptr->done == 0); if((fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT)){ return -ETIMEDOUT; @@ -566,12 +601,6 @@ int aac_consumer_get(struct aac_dev * dev, struct aac_queue * q, struct aac_entr return(status); } -int aac_consumer_avail(struct aac_dev *dev, struct aac_queue * q) -{ - return (le32_to_cpu(*q->headers.producer) != le32_to_cpu(*q->headers.consumer)); -} - - /** * aac_consumer_free - free consumer entry * @dev: Adapter @@ -601,15 +630,9 @@ void aac_consumer_free(struct aac_dev * dev, struct aac_queue *q, u32 qid) case HostNormCmdQueue: notify = HostNormCmdNotFull; break; - case HostHighCmdQueue: - notify = HostHighCmdNotFull; - break; case HostNormRespQueue: notify = HostNormRespNotFull; break; - case HostHighRespQueue: - notify = HostHighRespNotFull; - break; default: BUG(); return; @@ -619,7 +642,7 @@ void aac_consumer_free(struct aac_dev * dev, struct aac_queue *q, u32 qid) } /** - * fib_adapter_complete - complete adapter issued fib + * aac_fib_adapter_complete - complete adapter issued fib * @fibptr: fib to complete * @size: size of fib * @@ -627,17 +650,25 @@ void aac_consumer_free(struct aac_dev * dev, struct aac_queue *q, u32 qid) * the adapter. */ -int fib_adapter_complete(struct fib * fibptr, unsigned short size) +int aac_fib_adapter_complete(struct fib *fibptr, unsigned short size) { struct hw_fib * hw_fib = fibptr->hw_fib; struct aac_dev * dev = fibptr->dev; + struct aac_queue * q; unsigned long nointr = 0; - if (le32_to_cpu(hw_fib->header.XferState) == 0) + unsigned long qflags; + + if (hw_fib->header.XferState == 0) { + if (dev->new_comm_interface) + kfree (hw_fib); return 0; + } /* * If we plan to do anything check the structure type first. */ if ( hw_fib->header.StructType != FIB_MAGIC ) { + if (dev->new_comm_interface) + kfree (hw_fib); return -EINVAL; } /* @@ -648,55 +679,42 @@ int fib_adapter_complete(struct fib * fibptr, unsigned short size) * send the completed cdb to the adapter. */ if (hw_fib->header.XferState & cpu_to_le32(SentFromAdapter)) { - hw_fib->header.XferState |= cpu_to_le32(HostProcessed); - if (hw_fib->header.XferState & cpu_to_le32(HighPriority)) { - u32 index; - if (size) - { - size += sizeof(struct aac_fibhdr); - if (size > le16_to_cpu(hw_fib->header.SenderSize)) - return -EMSGSIZE; - hw_fib->header.Size = cpu_to_le16(size); - } - if(aac_queue_get(dev, &index, AdapHighRespQueue, hw_fib, 1, NULL, &nointr) < 0) { - return -EWOULDBLOCK; - } - if (aac_insert_entry(dev, index, AdapHighRespQueue, (nointr & (int)aac_config.irq_mod)) != 0) { - } - } - else if (hw_fib->header.XferState & NormalPriority) - { - u32 index; - + if (dev->new_comm_interface) { + kfree (hw_fib); + } else { + u32 index; + hw_fib->header.XferState |= cpu_to_le32(HostProcessed); if (size) { size += sizeof(struct aac_fibhdr); if (size > le16_to_cpu(hw_fib->header.SenderSize)) return -EMSGSIZE; hw_fib->header.Size = cpu_to_le16(size); } - if (aac_queue_get(dev, &index, AdapNormRespQueue, hw_fib, 1, NULL, &nointr) < 0) - return -EWOULDBLOCK; - if (aac_insert_entry(dev, index, AdapNormRespQueue, (nointr & (int)aac_config.irq_mod)) != 0) - { - } + q = &dev->queues->queue[AdapNormRespQueue]; + spin_lock_irqsave(q->lock, qflags); + aac_queue_get(dev, &index, AdapNormRespQueue, hw_fib, 1, NULL, &nointr); + *(q->headers.producer) = cpu_to_le32(index + 1); + spin_unlock_irqrestore(q->lock, qflags); + if (!(nointr & (int)aac_config.irq_mod)) + aac_adapter_notify(dev, AdapNormRespQueue); } } else { - printk(KERN_WARNING "fib_adapter_complete: Unknown xferstate detected.\n"); + printk(KERN_WARNING "aac_fib_adapter_complete: Unknown xferstate detected.\n"); BUG(); } return 0; } /** - * fib_complete - fib completion handler + * aac_fib_complete - fib completion handler * @fib: FIB to complete * * Will do all necessary work to complete a FIB. */ -int fib_complete(struct fib * fibptr) +int aac_fib_complete(struct fib *fibptr) { struct hw_fib * hw_fib = fibptr->hw_fib; @@ -704,7 +722,7 @@ int fib_complete(struct fib * fibptr) * Check for a fib which has already been completed */ - if (hw_fib->header.XferState == cpu_to_le32(0)) + if (hw_fib->header.XferState == 0) return 0; /* * If we plan to do anything check the structure type first. @@ -749,22 +767,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_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 "%s:%s", dev->name, cp); + else + printk(KERN_INFO "%s:%s", dev->name, cp); + } memset(cp, 0, 256); } @@ -778,18 +799,537 @@ void aac_printf(struct aac_dev *dev, u32 val) * dispatches it to the appropriate routine for handling. */ +#define AIF_SNIFF_TIMEOUT (30*HZ) static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) { struct hw_fib * hw_fib = fibptr->hw_fib; + struct aac_aifcmd * aifcmd = (struct aac_aifcmd *)hw_fib->data; + int busy; + u32 container; + struct scsi_device *device; + enum { + NOTHING, + DELETE, + ADD, + CHANGE + } device_config_needed; + + /* Sniff for container changes */ + + if (!dev || !dev->fsa_dev) + return; + container = (u32)-1; + /* - * Set the status of this FIB to be Invalid parameter. - * - * *(u32 *)fib->data = ST_INVAL; + * We have set this up to try and minimize the number of + * re-configures that take place. As a result of this when + * certain AIF's come in we will set a flag waiting for another + * type of AIF before setting the re-config flag. + */ + switch (le32_to_cpu(aifcmd->command)) { + case AifCmdDriverNotify: + switch (le32_to_cpu(((u32 *)aifcmd->data)[0])) { + /* + * Morph or Expand complete + */ + case AifDenMorphComplete: + case AifDenVolumeExtendComplete: + container = le32_to_cpu(((u32 *)aifcmd->data)[1]); + if (container >= dev->maximum_num_containers) + break; + + /* + * Find the scsi_device associated with the SCSI + * address. Make sure we have the right array, and if + * so set the flag to initiate a new re-config once we + * see an AifEnConfigChange AIF come through. + */ + + if ((dev != NULL) && (dev->scsi_host_ptr != NULL)) { + device = scsi_device_lookup(dev->scsi_host_ptr, + CONTAINER_TO_CHANNEL(container), + CONTAINER_TO_ID(container), + CONTAINER_TO_LUN(container)); + if (device) { + dev->fsa_dev[container].config_needed = CHANGE; + dev->fsa_dev[container].config_waiting_on = AifEnConfigChange; + dev->fsa_dev[container].config_waiting_stamp = jiffies; + scsi_device_put(device); + } + } + } + + /* + * If we are waiting on something and this happens to be + * that thing then set the re-configure flag. + */ + if (container != (u32)-1) { + if (container >= dev->maximum_num_containers) + break; + if ((dev->fsa_dev[container].config_waiting_on == + le32_to_cpu(*(u32 *)aifcmd->data)) && + time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) + dev->fsa_dev[container].config_waiting_on = 0; + } else for (container = 0; + container < dev->maximum_num_containers; ++container) { + if ((dev->fsa_dev[container].config_waiting_on == + le32_to_cpu(*(u32 *)aifcmd->data)) && + time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) + dev->fsa_dev[container].config_waiting_on = 0; + } + break; + + case AifCmdEventNotify: + switch (le32_to_cpu(((u32 *)aifcmd->data)[0])) { + /* + * Add an Array. + */ + case AifEnAddContainer: + container = le32_to_cpu(((u32 *)aifcmd->data)[1]); + if (container >= dev->maximum_num_containers) + break; + dev->fsa_dev[container].config_needed = ADD; + dev->fsa_dev[container].config_waiting_on = + AifEnConfigChange; + dev->fsa_dev[container].config_waiting_stamp = jiffies; + break; + + /* + * Delete an Array. + */ + case AifEnDeleteContainer: + container = le32_to_cpu(((u32 *)aifcmd->data)[1]); + if (container >= dev->maximum_num_containers) + break; + dev->fsa_dev[container].config_needed = DELETE; + dev->fsa_dev[container].config_waiting_on = + AifEnConfigChange; + dev->fsa_dev[container].config_waiting_stamp = jiffies; + break; + + /* + * Container change detected. If we currently are not + * waiting on something else, setup to wait on a Config Change. + */ + case AifEnContainerChange: + container = le32_to_cpu(((u32 *)aifcmd->data)[1]); + if (container >= dev->maximum_num_containers) + break; + if (dev->fsa_dev[container].config_waiting_on && + time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) + break; + dev->fsa_dev[container].config_needed = CHANGE; + dev->fsa_dev[container].config_waiting_on = + AifEnConfigChange; + dev->fsa_dev[container].config_waiting_stamp = jiffies; + break; + + case AifEnConfigChange: + break; + + } + + /* + * If we are waiting on something and this happens to be + * that thing then set the re-configure flag. + */ + if (container != (u32)-1) { + if (container >= dev->maximum_num_containers) + break; + if ((dev->fsa_dev[container].config_waiting_on == + le32_to_cpu(*(u32 *)aifcmd->data)) && + time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) + dev->fsa_dev[container].config_waiting_on = 0; + } else for (container = 0; + container < dev->maximum_num_containers; ++container) { + if ((dev->fsa_dev[container].config_waiting_on == + le32_to_cpu(*(u32 *)aifcmd->data)) && + time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) + dev->fsa_dev[container].config_waiting_on = 0; + } + break; + + case AifCmdJobProgress: + /* + * These are job progress AIF's. When a Clear is being + * done on a container it is initially created then hidden from + * the OS. When the clear completes we don't get a config + * change so we monitor the job status complete on a clear then + * wait for a container change. + */ + + if ((((u32 *)aifcmd->data)[1] == cpu_to_le32(AifJobCtrZero)) + && ((((u32 *)aifcmd->data)[6] == ((u32 *)aifcmd->data)[5]) + || (((u32 *)aifcmd->data)[4] == cpu_to_le32(AifJobStsSuccess)))) { + for (container = 0; + container < dev->maximum_num_containers; + ++container) { + /* + * Stomp on all config sequencing for all + * containers? + */ + dev->fsa_dev[container].config_waiting_on = + AifEnContainerChange; + dev->fsa_dev[container].config_needed = ADD; + dev->fsa_dev[container].config_waiting_stamp = + jiffies; + } + } + if ((((u32 *)aifcmd->data)[1] == cpu_to_le32(AifJobCtrZero)) + && (((u32 *)aifcmd->data)[6] == 0) + && (((u32 *)aifcmd->data)[4] == cpu_to_le32(AifJobStsRunning))) { + for (container = 0; + container < dev->maximum_num_containers; + ++container) { + /* + * Stomp on all config sequencing for all + * containers? + */ + dev->fsa_dev[container].config_waiting_on = + AifEnContainerChange; + dev->fsa_dev[container].config_needed = DELETE; + dev->fsa_dev[container].config_waiting_stamp = + jiffies; + } + } + break; + } + + device_config_needed = NOTHING; + for (container = 0; container < dev->maximum_num_containers; + ++container) { + if ((dev->fsa_dev[container].config_waiting_on == 0) && + (dev->fsa_dev[container].config_needed != NOTHING) && + time_before(jiffies, dev->fsa_dev[container].config_waiting_stamp + AIF_SNIFF_TIMEOUT)) { + device_config_needed = + dev->fsa_dev[container].config_needed; + dev->fsa_dev[container].config_needed = NOTHING; + break; + } + } + if (device_config_needed == NOTHING) + return; + + /* + * If we decided that a re-configuration needs to be done, + * schedule it here on the way out the door, please close the door + * behind you. */ - *(u32 *)hw_fib->data = cpu_to_le32(ST_OK); - fib_adapter_complete(fibptr, sizeof(u32)); + + busy = 0; + + + /* + * Find the scsi_device associated with the SCSI address, + * and mark it as changed, invalidating the cache. This deals + * with changes to existing device IDs. + */ + + if (!dev || !dev->scsi_host_ptr) + return; + /* + * force reload of disk info via aac_probe_container + */ + if ((device_config_needed == CHANGE) + && (dev->fsa_dev[container].valid == 1)) + dev->fsa_dev[container].valid = 2; + if ((device_config_needed == CHANGE) || + (device_config_needed == ADD)) + aac_probe_container(dev, container); + device = scsi_device_lookup(dev->scsi_host_ptr, + CONTAINER_TO_CHANNEL(container), + CONTAINER_TO_ID(container), + CONTAINER_TO_LUN(container)); + if (device) { + switch (device_config_needed) { + case DELETE: + case CHANGE: + scsi_rescan_device(&device->sdev_gendev); + + default: + break; + } + scsi_device_put(device); + } + if (device_config_needed == ADD) { + scsi_add_device(dev->scsi_host_ptr, + CONTAINER_TO_CHANNEL(container), + CONTAINER_TO_ID(container), + CONTAINER_TO_LUN(container)); + } + } +static int _aac_reset_adapter(struct aac_dev *aac) +{ + int index, quirks; + u32 ret; + int retval; + struct Scsi_Host *host; + struct scsi_device *dev; + struct scsi_cmnd *command; + struct scsi_cmnd *command_list; + + /* + * Assumptions: + * - host is locked. + * - in_reset is asserted, so no new i/o is getting to the + * card. + * - The card is dead. + */ + host = aac->scsi_host_ptr; + scsi_block_requests(host); + aac_adapter_disable_int(aac); + spin_unlock_irq(host->host_lock); + kthread_stop(aac->thread); + + /* + * If a positive health, means in a known DEAD PANIC + * state and the adapter could be reset to `try again'. + */ + retval = aac_adapter_check_health(aac); + if (retval == 0) + retval = aac_adapter_sync_cmd(aac, IOP_RESET_ALWAYS, + 0, 0, 0, 0, 0, 0, &ret, NULL, NULL, NULL, NULL); + if (retval) + retval = aac_adapter_sync_cmd(aac, IOP_RESET, + 0, 0, 0, 0, 0, 0, &ret, NULL, NULL, NULL, NULL); + + if (retval) + goto out; + if (ret != 0x00000001) { + retval = -ENODEV; + goto out; + } + + /* + * Loop through the fibs, close the synchronous FIBS + */ + for (index = 0; index < (aac->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB); index++) { + struct fib *fib = &aac->fibs[index]; + if (!(fib->hw_fib->header.XferState & cpu_to_le32(NoResponseExpected | Async)) && + (fib->hw_fib->header.XferState & cpu_to_le32(ResponseExpected))) { + unsigned long flagv; + spin_lock_irqsave(&fib->event_lock, flagv); + up(&fib->event_wait); + spin_unlock_irqrestore(&fib->event_lock, flagv); + schedule(); + } + } + index = aac->cardtype; + + /* + * Re-initialize the adapter, first free resources, then carefully + * apply the initialization sequence to come back again. Only risk + * is a change in Firmware dropping cache, it is assumed the caller + * will ensure that i/o is queisced and the card is flushed in that + * case. + */ + aac_fib_map_free(aac); + aac->hw_fib_va = NULL; + aac->hw_fib_pa = 0; + pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr, aac->comm_phys); + aac->comm_addr = NULL; + aac->comm_phys = 0; + kfree(aac->queues); + aac->queues = NULL; + free_irq(aac->pdev->irq, aac); + kfree(aac->fsa_dev); + aac->fsa_dev = NULL; + if (aac_get_driver_ident(index)->quirks & AAC_QUIRK_31BIT) { + if (((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) || + ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_32BIT_MASK)))) + goto out; + } else { + if (((retval = pci_set_dma_mask(aac->pdev, 0x7FFFFFFFULL))) || + ((retval = pci_set_consistent_dma_mask(aac->pdev, 0x7FFFFFFFULL)))) + goto out; + } + if ((retval = (*(aac_get_driver_ident(index)->init))(aac))) + goto out; + if (aac_get_driver_ident(index)->quirks & AAC_QUIRK_31BIT) + if ((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) + goto out; + aac->thread = kthread_run(aac_command_thread, aac, aac->name); + if (IS_ERR(aac->thread)) { + retval = PTR_ERR(aac->thread); + goto out; + } + (void)aac_get_adapter_info(aac); + quirks = aac_get_driver_ident(index)->quirks; + if ((quirks & AAC_QUIRK_34SG) && (host->sg_tablesize > 34)) { + host->sg_tablesize = 34; + host->max_sectors = (host->sg_tablesize * 8) + 112; + } + if ((quirks & AAC_QUIRK_17SG) && (host->sg_tablesize > 17)) { + host->sg_tablesize = 17; + host->max_sectors = (host->sg_tablesize * 8) + 112; + } + aac_get_config_status(aac, 1); + aac_get_containers(aac); + /* + * This is where the assumption that the Adapter is quiesced + * is important. + */ + command_list = NULL; + __shost_for_each_device(dev, host) { + unsigned long flags; + spin_lock_irqsave(&dev->list_lock, flags); + list_for_each_entry(command, &dev->cmd_list, list) + if (command->SCp.phase == AAC_OWNER_FIRMWARE) { + command->SCp.buffer = (struct scatterlist *)command_list; + command_list = command; + } + spin_unlock_irqrestore(&dev->list_lock, flags); + } + while ((command = command_list)) { + command_list = (struct scsi_cmnd *)command->SCp.buffer; + command->SCp.buffer = NULL; + command->result = DID_OK << 16 + | COMMAND_COMPLETE << 8 + | SAM_STAT_TASK_SET_FULL; + command->SCp.phase = AAC_OWNER_ERROR_HANDLER; + command->scsi_done(command); + } + retval = 0; + +out: + aac->in_reset = 0; + scsi_unblock_requests(host); + spin_lock_irq(host->host_lock); + return retval; +} + +int aac_check_health(struct aac_dev * aac) +{ + int BlinkLED; + unsigned long time_now, flagv = 0; + struct list_head * entry; + struct Scsi_Host * host; + + /* Extending the scope of fib_lock slightly to protect aac->in_reset */ + if (spin_trylock_irqsave(&aac->fib_lock, flagv) == 0) + return 0; + + if (aac->in_reset || !(BlinkLED = aac_adapter_check_health(aac))) { + spin_unlock_irqrestore(&aac->fib_lock, flagv); + return 0; /* OK */ + } + + aac->in_reset = 1; + + /* Fake up an AIF: + * aac_aifcmd.command = AifCmdEventNotify = 1 + * aac_aifcmd.seqnum = 0xFFFFFFFF + * aac_aifcmd.data[0] = AifEnExpEvent = 23 + * aac_aifcmd.data[1] = AifExeFirmwarePanic = 3 + * aac.aifcmd.data[2] = AifHighPriority = 3 + * aac.aifcmd.data[3] = BlinkLED + */ + + time_now = jiffies/HZ; + entry = aac->fib_list.next; + + /* + * For each Context that is on the + * fibctxList, make a copy of the + * fib, and then set the event to wake up the + * thread that is waiting for it. + */ + while (entry != &aac->fib_list) { + /* + * Extract the fibctx + */ + struct aac_fib_context *fibctx = list_entry(entry, struct aac_fib_context, next); + struct hw_fib * hw_fib; + struct fib * fib; + /* + * Check if the queue is getting + * backlogged + */ + if (fibctx->count > 20) { + /* + * It's *not* jiffies folks, + * but jiffies / HZ, so do not + * panic ... + */ + u32 time_last = fibctx->jiffies; + /* + * Has it been > 2 minutes + * since the last read off + * the queue? + */ + if ((time_now - time_last) > aif_timeout) { + entry = entry->next; + aac_close_fib_context(aac, fibctx); + continue; + } + } + /* + * Warning: no sleep allowed while + * holding spinlock + */ + hw_fib = kmalloc(sizeof(struct hw_fib), GFP_ATOMIC); + fib = kmalloc(sizeof(struct fib), GFP_ATOMIC); + if (fib && hw_fib) { + struct aac_aifcmd * aif; + + memset(hw_fib, 0, sizeof(struct hw_fib)); + memset(fib, 0, sizeof(struct fib)); + fib->hw_fib = hw_fib; + fib->dev = aac; + aac_fib_init(fib); + fib->type = FSAFS_NTC_FIB_CONTEXT; + fib->size = sizeof (struct fib); + fib->data = hw_fib->data; + aif = (struct aac_aifcmd *)hw_fib->data; + aif->command = cpu_to_le32(AifCmdEventNotify); + aif->seqnum = cpu_to_le32(0xFFFFFFFF); + aif->data[0] = cpu_to_le32(AifEnExpEvent); + aif->data[1] = cpu_to_le32(AifExeFirmwarePanic); + aif->data[2] = cpu_to_le32(AifHighPriority); + aif->data[3] = cpu_to_le32(BlinkLED); + + /* + * Put the FIB onto the + * fibctx's fibs + */ + list_add_tail(&fib->fiblink, &fibctx->fib_list); + fibctx->count++; + /* + * Set the event to wake up the + * thread that will waiting. + */ + up(&fibctx->wait_sem); + } else { + printk(KERN_WARNING "aifd: didn't allocate NewFib.\n"); + kfree(fib); + kfree(hw_fib); + } + entry = entry->next; + } + + spin_unlock_irqrestore(&aac->fib_lock, flagv); + + if (BlinkLED < 0) { + printk(KERN_ERR "%s: Host adapter dead %d\n", aac->name, BlinkLED); + goto out; + } + + printk(KERN_ERR "%s: Host adapter BLINK LED 0x%x\n", aac->name, BlinkLED); + + host = aac->scsi_host_ptr; + spin_lock_irqsave(host->host_lock, flagv); + BlinkLED = _aac_reset_adapter(aac); + spin_unlock_irqrestore(host->host_lock, flagv); + return BlinkLED; + +out: + aac->in_reset = 0; + return BlinkLED; +} + + /** * aac_command_thread - command processing thread * @dev: Adapter to monitor @@ -800,11 +1340,11 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) * more FIBs. */ -int aac_command_thread(struct aac_dev * dev) +int aac_command_thread(void *data) { + struct aac_dev *dev = data; 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); @@ -814,31 +1354,27 @@ int aac_command_thread(struct aac_dev * dev) */ if (dev->aif_thread) return -EINVAL; - /* - * Set up the name that will appear in 'ps' - * stored in task_struct.comm[16]. - */ - daemonize("aacraid"); - allow_signal(SIGKILL); + /* * 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 @@ -860,8 +1396,8 @@ int aac_command_thread(struct aac_dev * dev) if (aifcmd->command == cpu_to_le32(AifCmdDriverNotify)) { /* Handle Driver Notify Events */ aac_handle_aif(dev, fib); - *(u32 *)hw_fib->data = cpu_to_le32(ST_OK); - fib_adapter_complete(fib, sizeof(u32)); + *(__le32 *)hw_fib->data = cpu_to_le32(ST_OK); + aac_fib_adapter_complete(fib, (u16)sizeof(u32)); } else { struct list_head *entry; /* The u32 here is important and intended. We are using @@ -869,13 +1405,62 @@ 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; + /* Sniff events */ - if (aifcmd->command == cpu_to_le32(AifCmdEventNotify)) + if ((aifcmd->command == + cpu_to_le32(AifCmdEventNotify)) || + (aifcmd->command == + cpu_to_le32(AifCmdJobProgress))) { aac_handle_aif(dev, fib); - + } + 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 { + kfree(hw_fib_pool); + hw_fib_pool = NULL; + } spin_lock_irqsave(&dev->fib_lock, flagv); entry = dev->fib_list.next; /* @@ -884,6 +1469,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 @@ -906,7 +1493,7 @@ int aac_command_thread(struct aac_dev * dev) * since the last read off * the queue? */ - if ((time_now - time_last) > 120) { + if ((time_now - time_last) > aif_timeout) { entry = entry->next; aac_close_fib_context(dev, fibctx); continue; @@ -916,9 +1503,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 */ @@ -933,39 +1522,47 @@ 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; } /* * Set the status of this FIB */ - *(u32 *)hw_fib->data = cpu_to_le32(ST_OK); - fib_adapter_complete(fib, sizeof(u32)); + *(__le32 *)hw_fib->data = cpu_to_le32(ST_OK); + aac_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]) { + kfree(*hw_fib_p); + kfree(*fib_p); + ++fib_p; + ++hw_fib_p; + } + kfree(hw_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)) + if (kthread_should_stop()) 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; }