2005-04-18 01:05:31 +04:00
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
2005-06-25 18:34:39 +04:00
* Fibre Channel Host Bus Adapters . *
2007-06-18 04:56:36 +04:00
* Copyright ( C ) 2004 - 2006 Emulex . All rights reserved . *
2005-06-25 18:34:39 +04:00
* EMULEX and SLI are trademarks of Emulex . *
2005-04-18 01:05:31 +04:00
* www . emulex . com *
2005-06-25 18:34:39 +04:00
* Portions Copyright ( C ) 2004 - 2005 Christoph Hellwig *
2005-04-18 01:05:31 +04:00
* *
* This program is free software ; you can redistribute it and / or *
2005-06-25 18:34:39 +04:00
* modify it under the terms of version 2 of the GNU General *
* Public License as published by the Free Software Foundation . *
* This program is distributed in the hope that it will be useful . *
* ALL EXPRESS OR IMPLIED CONDITIONS , REPRESENTATIONS AND *
* WARRANTIES , INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY , *
* FITNESS FOR A PARTICULAR PURPOSE , OR NON - INFRINGEMENT , ARE *
* DISCLAIMED , EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
* TO BE LEGALLY INVALID . See the GNU General Public License for *
* more details , a copy of which can be found in the file COPYING *
* included with this package . *
2005-04-18 01:05:31 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/mempool.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
2005-08-10 23:03:01 +04:00
# include <scsi/scsi_device.h>
# include <scsi/scsi_transport_fc.h>
2005-08-10 23:03:09 +04:00
# include <scsi/scsi.h>
2005-04-18 01:05:31 +04:00
# include "lpfc_hw.h"
# include "lpfc_sli.h"
# include "lpfc_disc.h"
# include "lpfc_scsi.h"
# include "lpfc.h"
# include "lpfc_crtn.h"
# define LPFC_MBUF_POOL_SIZE 64 /* max elements in MBUF safety pool */
# define LPFC_MEM_POOL_SIZE 64 /* max elem in non-DMA safety pool */
2007-06-18 04:56:36 +04:00
2005-04-18 01:05:31 +04:00
int
lpfc_mem_alloc ( struct lpfc_hba * phba )
{
struct lpfc_dma_pool * pool = & phba - > lpfc_mbuf_safety_pool ;
int i ;
phba - > lpfc_scsi_dma_buf_pool = pci_pool_create ( " lpfc_scsi_dma_buf_pool " ,
phba - > pcidev , phba - > cfg_sg_dma_buf_size , 8 , 0 ) ;
if ( ! phba - > lpfc_scsi_dma_buf_pool )
goto fail ;
phba - > lpfc_mbuf_pool = pci_pool_create ( " lpfc_mbuf_pool " , phba - > pcidev ,
LPFC_BPL_SIZE , 8 , 0 ) ;
if ( ! phba - > lpfc_mbuf_pool )
goto fail_free_dma_buf_pool ;
pool - > elements = kmalloc ( sizeof ( struct lpfc_dmabuf ) *
LPFC_MBUF_POOL_SIZE , GFP_KERNEL ) ;
2007-01-02 03:07:32 +03:00
if ( ! pool - > elements )
goto fail_free_lpfc_mbuf_pool ;
2005-04-18 01:05:31 +04:00
pool - > max_count = 0 ;
pool - > current_count = 0 ;
for ( i = 0 ; i < LPFC_MBUF_POOL_SIZE ; i + + ) {
pool - > elements [ i ] . virt = pci_pool_alloc ( phba - > lpfc_mbuf_pool ,
GFP_KERNEL , & pool - > elements [ i ] . phys ) ;
if ( ! pool - > elements [ i ] . virt )
goto fail_free_mbuf_pool ;
pool - > max_count + + ;
pool - > current_count + + ;
}
2006-03-26 13:37:47 +04:00
phba - > mbox_mem_pool = mempool_create_kmalloc_pool ( LPFC_MEM_POOL_SIZE ,
sizeof ( LPFC_MBOXQ_t ) ) ;
2005-04-18 01:05:31 +04:00
if ( ! phba - > mbox_mem_pool )
goto fail_free_mbuf_pool ;
2006-03-26 13:37:47 +04:00
phba - > nlp_mem_pool = mempool_create_kmalloc_pool ( LPFC_MEM_POOL_SIZE ,
sizeof ( struct lpfc_nodelist ) ) ;
2005-04-18 01:05:31 +04:00
if ( ! phba - > nlp_mem_pool )
goto fail_free_mbox_pool ;
2007-06-18 04:56:37 +04:00
phba - > lpfc_hbq_pool = pci_pool_create ( " lpfc_hbq_pool " , phba - > pcidev ,
LPFC_BPL_SIZE , 8 , 0 ) ;
if ( ! phba - > lpfc_hbq_pool )
goto fail_free_nlp_mem_pool ;
2005-04-18 01:05:31 +04:00
return 0 ;
2007-06-18 04:56:37 +04:00
fail_free_nlp_mem_pool :
mempool_destroy ( phba - > nlp_mem_pool ) ;
phba - > nlp_mem_pool = NULL ;
2005-04-18 01:05:31 +04:00
fail_free_mbox_pool :
mempool_destroy ( phba - > mbox_mem_pool ) ;
2007-06-18 04:56:36 +04:00
phba - > mbox_mem_pool = NULL ;
2005-04-18 01:05:31 +04:00
fail_free_mbuf_pool :
2007-01-02 03:07:32 +03:00
while ( i - - )
2005-04-18 01:05:31 +04:00
pci_pool_free ( phba - > lpfc_mbuf_pool , pool - > elements [ i ] . virt ,
pool - > elements [ i ] . phys ) ;
kfree ( pool - > elements ) ;
2007-01-02 03:07:32 +03:00
fail_free_lpfc_mbuf_pool :
2005-04-18 01:05:31 +04:00
pci_pool_destroy ( phba - > lpfc_mbuf_pool ) ;
2007-06-18 04:56:36 +04:00
phba - > lpfc_mbuf_pool = NULL ;
2005-04-18 01:05:31 +04:00
fail_free_dma_buf_pool :
pci_pool_destroy ( phba - > lpfc_scsi_dma_buf_pool ) ;
2007-06-18 04:56:36 +04:00
phba - > lpfc_scsi_dma_buf_pool = NULL ;
2005-04-18 01:05:31 +04:00
fail :
return - ENOMEM ;
}
void
lpfc_mem_free ( struct lpfc_hba * phba )
{
struct lpfc_sli * psli = & phba - > sli ;
struct lpfc_dma_pool * pool = & phba - > lpfc_mbuf_safety_pool ;
LPFC_MBOXQ_t * mbox , * next_mbox ;
struct lpfc_dmabuf * mp ;
int i ;
2007-06-18 04:56:37 +04:00
lpfc_sli_hbqbuf_free_all ( phba ) ;
2007-06-18 04:56:36 +04:00
spin_lock_irq ( & phba - > hbalock ) ;
2005-04-18 01:05:31 +04:00
list_for_each_entry_safe ( mbox , next_mbox , & psli - > mboxq , list ) {
mp = ( struct lpfc_dmabuf * ) ( mbox - > context1 ) ;
if ( mp ) {
lpfc_mbuf_free ( phba , mp - > virt , mp - > phys ) ;
kfree ( mp ) ;
}
list_del ( & mbox - > list ) ;
mempool_free ( mbox , phba - > mbox_mem_pool ) ;
}
psli - > sli_flag & = ~ LPFC_SLI_MBOX_ACTIVE ;
2007-06-18 04:56:36 +04:00
spin_unlock_irq ( & phba - > hbalock ) ;
2005-04-18 01:05:31 +04:00
if ( psli - > mbox_active ) {
mbox = psli - > mbox_active ;
mp = ( struct lpfc_dmabuf * ) ( mbox - > context1 ) ;
if ( mp ) {
lpfc_mbuf_free ( phba , mp - > virt , mp - > phys ) ;
kfree ( mp ) ;
}
mempool_free ( mbox , phba - > mbox_mem_pool ) ;
psli - > mbox_active = NULL ;
}
for ( i = 0 ; i < pool - > current_count ; i + + )
pci_pool_free ( phba - > lpfc_mbuf_pool , pool - > elements [ i ] . virt ,
pool - > elements [ i ] . phys ) ;
kfree ( pool - > elements ) ;
2007-06-18 04:56:36 +04:00
2007-06-18 04:56:37 +04:00
pci_pool_destroy ( phba - > lpfc_hbq_pool ) ;
2005-04-18 01:05:31 +04:00
mempool_destroy ( phba - > nlp_mem_pool ) ;
mempool_destroy ( phba - > mbox_mem_pool ) ;
pci_pool_destroy ( phba - > lpfc_scsi_dma_buf_pool ) ;
pci_pool_destroy ( phba - > lpfc_mbuf_pool ) ;
2006-07-06 23:49:34 +04:00
2007-06-18 04:56:37 +04:00
phba - > lpfc_hbq_pool = NULL ;
2007-06-18 04:56:36 +04:00
phba - > nlp_mem_pool = NULL ;
phba - > mbox_mem_pool = NULL ;
phba - > lpfc_scsi_dma_buf_pool = NULL ;
phba - > lpfc_mbuf_pool = NULL ;
2006-07-06 23:49:34 +04:00
/* Free the iocb lookup array */
kfree ( psli - > iocbq_lookup ) ;
psli - > iocbq_lookup = NULL ;
2005-04-18 01:05:31 +04:00
}
void *
lpfc_mbuf_alloc ( struct lpfc_hba * phba , int mem_flags , dma_addr_t * handle )
{
struct lpfc_dma_pool * pool = & phba - > lpfc_mbuf_safety_pool ;
2007-06-18 04:56:36 +04:00
unsigned long iflags ;
2005-04-18 01:05:31 +04:00
void * ret ;
ret = pci_pool_alloc ( phba - > lpfc_mbuf_pool , GFP_KERNEL , handle ) ;
2007-06-18 04:56:36 +04:00
spin_lock_irqsave ( & phba - > hbalock , iflags ) ;
2005-04-18 01:05:31 +04:00
if ( ! ret & & ( mem_flags & MEM_PRI ) & & pool - > current_count ) {
pool - > current_count - - ;
ret = pool - > elements [ pool - > current_count ] . virt ;
* handle = pool - > elements [ pool - > current_count ] . phys ;
}
2007-06-18 04:56:36 +04:00
spin_unlock_irqrestore ( & phba - > hbalock , iflags ) ;
2005-04-18 01:05:31 +04:00
return ret ;
}
void
2007-06-18 04:56:36 +04:00
__lpfc_mbuf_free ( struct lpfc_hba * phba , void * virt , dma_addr_t dma )
2005-04-18 01:05:31 +04:00
{
struct lpfc_dma_pool * pool = & phba - > lpfc_mbuf_safety_pool ;
if ( pool - > current_count < pool - > max_count ) {
pool - > elements [ pool - > current_count ] . virt = virt ;
pool - > elements [ pool - > current_count ] . phys = dma ;
pool - > current_count + + ;
} else {
pci_pool_free ( phba - > lpfc_mbuf_pool , virt , dma ) ;
}
return ;
}
2007-06-18 04:56:36 +04:00
void
lpfc_mbuf_free ( struct lpfc_hba * phba , void * virt , dma_addr_t dma )
{
unsigned long iflags ;
spin_lock_irqsave ( & phba - > hbalock , iflags ) ;
__lpfc_mbuf_free ( phba , virt , dma ) ;
spin_unlock_irqrestore ( & phba - > hbalock , iflags ) ;
return ;
}
2007-06-18 04:56:37 +04:00
void *
lpfc_hbq_alloc ( struct lpfc_hba * phba , int mem_flags , dma_addr_t * handle )
{
void * ret ;
ret = pci_pool_alloc ( phba - > lpfc_hbq_pool , GFP_ATOMIC , handle ) ;
return ret ;
}
void
lpfc_hbq_free ( struct lpfc_hba * phba , void * virt , dma_addr_t dma )
{
pci_pool_free ( phba - > lpfc_hbq_pool , virt , dma ) ;
return ;
}