diff options
Diffstat (limited to 'drivers/scsi/aacraid/commsup.c')
| -rw-r--r-- | drivers/scsi/aacraid/commsup.c | 245 |
1 files changed, 196 insertions, 49 deletions
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 47434499e82..cab190af634 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -1,11 +1,12 @@ /* * Adaptec AAC series RAID controller driver - * (c) Copyright 2001 Red Hat Inc. <alan@redhat.com> + * (c) Copyright 2001 Red Hat Inc. * * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * - * Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.com) + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,11 +42,11 @@ #include <linux/delay.h> #include <linux/kthread.h> #include <linux/interrupt.h> +#include <linux/semaphore.h> #include <scsi/scsi.h> #include <scsi/scsi_host.h> #include <scsi/scsi_device.h> #include <scsi/scsi_cmnd.h> -#include <asm/semaphore.h> #include "aacraid.h" @@ -63,9 +64,11 @@ static int fib_map_alloc(struct aac_dev *dev) "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) + dev->hw_fib_va = pci_alloc_consistent(dev->pdev, + (dev->max_fib_size + sizeof(struct aac_fib_xporthdr)) + * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB) + (ALIGN32 - 1), + &dev->hw_fib_pa); + if (dev->hw_fib_va == NULL) return -ENOMEM; return 0; } @@ -91,7 +94,7 @@ void aac_fib_map_free(struct aac_dev *dev) * aac_fib_setup - setup the fibs * @dev: Adapter to set up * - * Allocate the PCI space for the fibs, map it and then intialise the + * Allocate the PCI space for the fibs, map it and then initialise the * fib area, the unmapped fib data and also the free list */ @@ -110,9 +113,22 @@ int aac_fib_setup(struct aac_dev * dev) if (i<0) return -ENOMEM; + /* 32 byte alignment for PMC */ + hw_fib_pa = (dev->hw_fib_pa + (ALIGN32 - 1)) & ~(ALIGN32 - 1); + dev->hw_fib_va = (struct hw_fib *)((unsigned char *)dev->hw_fib_va + + (hw_fib_pa - dev->hw_fib_pa)); + dev->hw_fib_pa = hw_fib_pa; + memset(dev->hw_fib_va, 0, + (dev->max_fib_size + sizeof(struct aac_fib_xporthdr)) * + (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)); + + /* add Xport header */ + dev->hw_fib_va = (struct hw_fib *)((unsigned char *)dev->hw_fib_va + + sizeof(struct aac_fib_xporthdr)); + dev->hw_fib_pa += sizeof(struct aac_fib_xporthdr); + hw_fib = dev->hw_fib_va; hw_fib_pa = dev->hw_fib_pa; - memset(hw_fib, 0, dev->max_fib_size * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)); /* * Initialise the fibs */ @@ -120,17 +136,20 @@ int aac_fib_setup(struct aac_dev * dev) i < (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB); i++, fibptr++) { + fibptr->flags = 0; fibptr->dev = dev; fibptr->hw_fib_va = hw_fib; fibptr->data = (void *) fibptr->hw_fib_va->data; fibptr->next = fibptr+1; /* Forward chain the fibs */ - init_MUTEX_LOCKED(&fibptr->event_wait); + sema_init(&fibptr->event_wait, 0); spin_lock_init(&fibptr->event_lock); hw_fib->header.XferState = cpu_to_le32(0xffffffff); hw_fib->header.SenderSize = cpu_to_le16(dev->max_fib_size); fibptr->hw_fib_pa = hw_fib_pa; - hw_fib = (struct hw_fib *)((unsigned char *)hw_fib + dev->max_fib_size); - hw_fib_pa = hw_fib_pa + dev->max_fib_size; + hw_fib = (struct hw_fib *)((unsigned char *)hw_fib + + dev->max_fib_size + sizeof(struct aac_fib_xporthdr)); + hw_fib_pa = hw_fib_pa + + dev->max_fib_size + sizeof(struct aac_fib_xporthdr); } /* * Add the fib chain to the free list @@ -189,7 +208,14 @@ struct fib *aac_fib_alloc(struct aac_dev *dev) void aac_fib_free(struct fib *fibptr) { - unsigned long flags; + unsigned long flags, flagsv; + + spin_lock_irqsave(&fibptr->event_lock, flagsv); + if (fibptr->done == 2) { + spin_unlock_irqrestore(&fibptr->event_lock, flagsv); + return; + } + spin_unlock_irqrestore(&fibptr->event_lock, flagsv); spin_lock_irqsave(&fibptr->dev->fib_lock, flags); if (unlikely(fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT)) @@ -215,11 +241,11 @@ void aac_fib_init(struct fib *fibptr) { struct hw_fib *hw_fib = fibptr->hw_fib_va; + memset(&hw_fib->header, 0, sizeof(struct aac_fibhdr)); hw_fib->header.StructType = FIB_MAGIC; 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.u.ReceiverFibAddress = cpu_to_le32(fibptr->hw_fib_pa); hw_fib->header.SenderSize = cpu_to_le16(fibptr->dev->max_fib_size); } @@ -234,7 +260,6 @@ void aac_fib_init(struct fib *fibptr) static void fib_dealloc(struct fib * fibptr) { struct hw_fib *hw_fib = fibptr->hw_fib_va; - BUG_ON(hw_fib->header.StructType != FIB_MAGIC); hw_fib->header.XferState = 0; } @@ -345,7 +370,7 @@ int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw entry->size = cpu_to_le32(le16_to_cpu(hw_fib->header.Size)); entry->addr = hw_fib->header.SenderFibAddress; /* Restore adapters pointer to the FIB */ - hw_fib->header.ReceiverFibAddress = hw_fib->header.SenderFibAddress; /* Let the adapter now where to find its data */ + hw_fib->header.u.ReceiverFibAddress = hw_fib->header.SenderFibAddress; /* Let the adapter now where to find its data */ map = 0; } /* @@ -390,11 +415,14 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, struct hw_fib * hw_fib = fibptr->hw_fib_va; unsigned long flags = 0; unsigned long qflags; + unsigned long mflags = 0; + unsigned long sflags = 0; + if (!(hw_fib->header.XferState & cpu_to_le32(HostOwned))) return -EBUSY; /* - * There are 5 cases with the wait and reponse requested flags. + * There are 5 cases with the wait and response 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 Fib is not allocated from pool. If a response @@ -422,7 +450,7 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, */ hw_fib->header.SenderFibAddress = cpu_to_le32(((u32)(fibptr - dev->fibs)) << 2); - hw_fib->header.SenderData = (u32)(fibptr - dev->fibs); + hw_fib->header.Handle = (u32)(fibptr - dev->fibs) + 1; /* * Set FIB state to indicate where it came from and if we want a * response from the adapter. Also load the command from the @@ -432,7 +460,6 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, */ hw_fib->header.Command = cpu_to_le16(command); hw_fib->header.XferState |= cpu_to_le32(SentFromHost); - fibptr->hw_fib_va->header.Flags = 0; /* 0 the flags field - internal only*/ /* * Set the size of the Fib we want to send to the adapter */ @@ -471,9 +498,56 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, if (!dev->queues) return -EBUSY; - if(wait) + if (wait) { + + spin_lock_irqsave(&dev->manage_lock, mflags); + if (dev->management_fib_count >= AAC_NUM_MGT_FIB) { + printk(KERN_INFO "No management Fibs Available:%d\n", + dev->management_fib_count); + spin_unlock_irqrestore(&dev->manage_lock, mflags); + return -EBUSY; + } + dev->management_fib_count++; + spin_unlock_irqrestore(&dev->manage_lock, mflags); spin_lock_irqsave(&fibptr->event_lock, flags); - aac_adapter_deliver(fibptr); + } + + if (dev->sync_mode) { + if (wait) + spin_unlock_irqrestore(&fibptr->event_lock, flags); + spin_lock_irqsave(&dev->sync_lock, sflags); + if (dev->sync_fib) { + list_add_tail(&fibptr->fiblink, &dev->sync_fib_list); + spin_unlock_irqrestore(&dev->sync_lock, sflags); + } else { + dev->sync_fib = fibptr; + spin_unlock_irqrestore(&dev->sync_lock, sflags); + aac_adapter_sync_cmd(dev, SEND_SYNCHRONOUS_FIB, + (u32)fibptr->hw_fib_pa, 0, 0, 0, 0, 0, + NULL, NULL, NULL, NULL, NULL); + } + if (wait) { + fibptr->flags |= FIB_CONTEXT_FLAG_WAIT; + if (down_interruptible(&fibptr->event_wait)) { + fibptr->flags &= ~FIB_CONTEXT_FLAG_WAIT; + return -EFAULT; + } + return 0; + } + return -EINPROGRESS; + } + + if (aac_adapter_deliver(fibptr) != 0) { + printk(KERN_ERR "aac_fib_send: returned -EBUSY\n"); + if (wait) { + spin_unlock_irqrestore(&fibptr->event_lock, flags); + spin_lock_irqsave(&dev->manage_lock, mflags); + dev->management_fib_count--; + spin_unlock_irqrestore(&dev->manage_lock, mflags); + } + return -EBUSY; + } + /* * If the caller wanted us to wait for response wait now. @@ -489,10 +563,10 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, * functioning because an interrupt routing or other * hardware failure has occurred. */ - unsigned long count = 36000000L; /* 3 minutes */ + unsigned long timeout = jiffies + (180 * HZ); /* 3 minutes */ while (down_trylock(&fibptr->event_wait)) { int blink; - if (--count == 0) { + if (time_is_before_eq_jiffies(timeout)) { struct aac_queue * q = &dev->queues->queue[AdapNormCmdQueue]; spin_lock_irqsave(q->lock, qflags); q->numpending--; @@ -513,15 +587,21 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, } return -EFAULT; } - udelay(5); + /* We used to udelay() here but that absorbed + * a CPU when a timeout occured. Not very + * useful. */ + cpu_relax(); } - } else - (void)down_interruptible(&fibptr->event_wait); + } else if (down_interruptible(&fibptr->event_wait)) { + /* Do nothing ... satisfy + * down_interruptible must_check */ + } + 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; + return -ERESTARTSYS; } spin_unlock_irqrestore(&fibptr->event_lock, flags); BUG_ON(fibptr->done == 0); @@ -594,7 +674,7 @@ void aac_consumer_free(struct aac_dev * dev, struct aac_queue *q, u32 qid) if (le32_to_cpu(*q->headers.consumer) >= q->entries) *q->headers.consumer = cpu_to_le32(1); else - *q->headers.consumer = cpu_to_le32(le32_to_cpu(*q->headers.consumer)+1); + le32_add_cpu(q->headers.consumer, 1); if (wasfull) { switch (qid) { @@ -630,17 +710,25 @@ int aac_fib_adapter_complete(struct fib *fibptr, unsigned short size) unsigned long nointr = 0; unsigned long qflags; + if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1 || + dev->comm_interface == AAC_COMM_MESSAGE_TYPE2) { + kfree(hw_fib); + return 0; + } + if (hw_fib->header.XferState == 0) { if (dev->comm_interface == AAC_COMM_MESSAGE) - kfree (hw_fib); + kfree(hw_fib); return 0; } /* * If we plan to do anything check the structure type first. */ - if (hw_fib->header.StructType != FIB_MAGIC) { + if (hw_fib->header.StructType != FIB_MAGIC && + hw_fib->header.StructType != FIB_MAGIC2 && + hw_fib->header.StructType != FIB_MAGIC2_64) { if (dev->comm_interface == AAC_COMM_MESSAGE) - kfree (hw_fib); + kfree(hw_fib); return -EINVAL; } /* @@ -687,6 +775,7 @@ int aac_fib_adapter_complete(struct fib *fibptr, unsigned short size) int aac_fib_complete(struct fib *fibptr) { + unsigned long flags; struct hw_fib * hw_fib = fibptr->hw_fib_va; /* @@ -699,7 +788,9 @@ int aac_fib_complete(struct fib *fibptr) * If we plan to do anything check the structure type first. */ - if (hw_fib->header.StructType != FIB_MAGIC) + if (hw_fib->header.StructType != FIB_MAGIC && + hw_fib->header.StructType != FIB_MAGIC2 && + hw_fib->header.StructType != FIB_MAGIC2_64) return -EINVAL; /* * This block completes a cdb which orginated on the host and we @@ -707,6 +798,13 @@ int aac_fib_complete(struct fib *fibptr) * command is complete that we had sent to the adapter and this * cdb could be reused. */ + spin_lock_irqsave(&fibptr->event_lock, flags); + if (fibptr->done == 2) { + spin_unlock_irqrestore(&fibptr->event_lock, flags); + return 0; + } + spin_unlock_irqrestore(&fibptr->event_lock, flags); + if((hw_fib->header.XferState & cpu_to_le32(SentFromHost)) && (hw_fib->header.XferState & cpu_to_le32(AdapterProcessed))) { @@ -904,19 +1002,36 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) case AifEnAddJBOD: case AifEnDeleteJBOD: container = le32_to_cpu(((__le32 *)aifcmd->data)[1]); - if ((container >> 28)) + if ((container >> 28)) { + container = (u32)-1; break; + } channel = (container >> 24) & 0xF; - if (channel >= dev->maximum_num_channels) + if (channel >= dev->maximum_num_channels) { + container = (u32)-1; break; + } id = container & 0xFFFF; - if (id >= dev->maximum_num_physicals) + if (id >= dev->maximum_num_physicals) { + container = (u32)-1; break; + } lun = (container >> 16) & 0xFF; + container = (u32)-1; channel = aac_phys_to_logical(channel); device_config_needed = (((__le32 *)aifcmd->data)[0] == cpu_to_le32(AifEnAddJBOD)) ? ADD : DELETE; + if (device_config_needed == ADD) { + device = scsi_device_lookup(dev->scsi_host_ptr, + channel, + id, + lun); + if (device) { + scsi_remove_device(device); + scsi_device_put(device); + } + } break; case AifEnEnclosureManagement: @@ -931,13 +1046,18 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) case EM_DRIVE_REMOVAL: container = le32_to_cpu( ((__le32 *)aifcmd->data)[2]); - if ((container >> 28)) + if ((container >> 28)) { + container = (u32)-1; break; + } channel = (container >> 24) & 0xF; - if (channel >= dev->maximum_num_channels) + if (channel >= dev->maximum_num_channels) { + container = (u32)-1; break; + } id = container & 0xFFFF; lun = (container >> 16) & 0xFF; + container = (u32)-1; if (id >= dev->maximum_num_physicals) { /* legacy dev_t ? */ if ((0x2000 <= id) || lun || channel || @@ -1023,9 +1143,10 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) break; } + container = 0; +retry_next: if (device_config_needed == NOTHING) - for (container = 0; container < dev->maximum_num_containers; - ++container) { + for (; 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)) { @@ -1068,6 +1189,9 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) if (device) { switch (device_config_needed) { case DELETE: +#if (defined(AAC_DEBUG_INSTRUMENT_AIF_DELETE)) + scsi_remove_device(device); +#else if (scsi_device_online(device)) { scsi_device_set_state(device, SDEV_OFFLINE); sdev_printk(KERN_INFO, device, @@ -1076,6 +1200,7 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) "array deleted" : "enclosure services event"); } +#endif break; case ADD: if (!scsi_device_online(device)) { @@ -1090,12 +1215,16 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) case CHANGE: if ((channel == CONTAINER_CHANNEL) && (!dev->fsa_dev[container].valid)) { +#if (defined(AAC_DEBUG_INSTRUMENT_AIF_DELETE)) + scsi_remove_device(device); +#else if (!scsi_device_online(device)) break; scsi_device_set_state(device, SDEV_OFFLINE); sdev_printk(KERN_INFO, device, "Device offlined - %s\n", "array failed"); +#endif break; } scsi_rescan_device(&device->sdev_gendev); @@ -1108,6 +1237,11 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) } if (device_config_needed == ADD) scsi_add_device(dev->scsi_host_ptr, channel, id, lun); + if (channel == CONTAINER_CHANNEL) { + container++; + device_config_needed = NOTHING; + goto retry_next; + } } static int _aac_reset_adapter(struct aac_dev *aac, int forced) @@ -1182,25 +1316,28 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced) kfree(aac->queues); aac->queues = NULL; free_irq(aac->pdev->irq, aac); + if (aac->msi) + pci_disable_msi(aac->pdev); kfree(aac->fsa_dev); aac->fsa_dev = NULL; quirks = aac_get_driver_ident(index)->quirks; if (quirks & AAC_QUIRK_31BIT) { - if (((retval = pci_set_dma_mask(aac->pdev, DMA_31BIT_MASK))) || - ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_31BIT_MASK)))) + if (((retval = pci_set_dma_mask(aac->pdev, DMA_BIT_MASK(31)))) || + ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_BIT_MASK(31))))) goto out; } else { - if (((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) || - ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_32BIT_MASK)))) + if (((retval = pci_set_dma_mask(aac->pdev, DMA_BIT_MASK(32)))) || + ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_BIT_MASK(32))))) goto out; } if ((retval = (*(aac_get_driver_ident(index)->init))(aac))) goto out; if (quirks & AAC_QUIRK_31BIT) - if ((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) + if ((retval = pci_set_dma_mask(aac->pdev, DMA_BIT_MASK(32)))) goto out; if (jafo) { - aac->thread = kthread_run(aac_command_thread, aac, aac->name); + aac->thread = kthread_run(aac_command_thread, aac, "%s", + aac->name); if (IS_ERR(aac->thread)) { retval = PTR_ERR(aac->thread); goto out; @@ -1335,7 +1472,10 @@ int aac_reset_adapter(struct aac_dev * aac, int forced) if (status >= 0) aac_fib_complete(fibctx); - aac_fib_free(fibctx); + /* FIB should be freed only after getting + * the response from the F/W */ + if (status != -ERESTARTSYS) + aac_fib_free(fibctx); } } @@ -1739,6 +1879,7 @@ int aac_command_thread(void *data) struct fib *fibptr; if ((fibptr = aac_fib_alloc(dev))) { + int status; __le32 *info; aac_fib_init(fibptr); @@ -1749,15 +1890,21 @@ int aac_command_thread(void *data) *info = cpu_to_le32(now.tv_sec); - (void)aac_fib_send(SendHostTime, + status = aac_fib_send(SendHostTime, fibptr, sizeof(*info), FsaNormal, 1, 1, NULL, NULL); - aac_fib_complete(fibptr); - aac_fib_free(fibptr); + /* Do not set XferState to zero unless + * receives a response from F/W */ + if (status >= 0) + aac_fib_complete(fibptr); + /* FIB should be freed only after + * getting the response from the F/W */ + if (status != -ERESTARTSYS) + aac_fib_free(fibptr); } difference = (long)(unsigned)update_interval*HZ; } else { |
