2005-04-17 16:05:31 -05:00
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
2005-06-25 10:34:39 -04:00
* Fibre Channel Host Bus Adapters . *
2007-06-17 19:56:36 -05:00
* Copyright ( C ) 2004 - 2006 Emulex . All rights reserved . *
2005-06-25 10:34:39 -04:00
* EMULEX and SLI are trademarks of Emulex . *
2005-04-17 16:05:31 -05:00
* www . emulex . com *
2005-06-25 10:34:39 -04:00
* Portions Copyright ( C ) 2004 - 2005 Christoph Hellwig *
2005-04-17 16:05:31 -05:00
* *
* This program is free software ; you can redistribute it and / or *
2005-06-25 10: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-17 16:05:31 -05:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/mempool.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
2005-08-10 15:03:01 -04:00
# include <scsi/scsi_device.h>
# include <scsi/scsi_transport_fc.h>
2005-08-10 15:03:09 -04:00
# include <scsi/scsi.h>
2005-04-17 16:05:31 -05: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-17 19:56:36 -05:00
2005-04-17 16:05:31 -05:00
int
lpfc_mem_alloc ( struct lpfc_hba * phba )
{
struct lpfc_dma_pool * pool = & phba - > lpfc_mbuf_safety_pool ;
2007-06-17 19:56:38 -05:00
int longs ;
2005-04-17 16:05:31 -05:00
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 01:07:32 +01:00
if ( ! pool - > elements )
goto fail_free_lpfc_mbuf_pool ;
2005-04-17 16:05:31 -05: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 01:37:47 -08:00
phba - > mbox_mem_pool = mempool_create_kmalloc_pool ( LPFC_MEM_POOL_SIZE ,
sizeof ( LPFC_MBOXQ_t ) ) ;
2005-04-17 16:05:31 -05:00
if ( ! phba - > mbox_mem_pool )
goto fail_free_mbuf_pool ;
2006-03-26 01:37:47 -08:00
phba - > nlp_mem_pool = mempool_create_kmalloc_pool ( LPFC_MEM_POOL_SIZE ,
sizeof ( struct lpfc_nodelist ) ) ;
2005-04-17 16:05:31 -05:00
if ( ! phba - > nlp_mem_pool )
goto fail_free_mbox_pool ;
2007-06-17 19:56:37 -05: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 ;
2007-06-17 19:56:39 -05:00
/* vpi zero is reserved for the physical port so add 1 to max */
longs = ( ( phba - > max_vpi + 1 ) + BITS_PER_LONG - 1 ) / BITS_PER_LONG ;
2007-06-17 19:56:38 -05:00
phba - > vpi_bmask = kzalloc ( longs * sizeof ( unsigned long ) , GFP_KERNEL ) ;
if ( ! phba - > vpi_bmask )
goto fail_free_hbq_pool ;
2005-04-17 16:05:31 -05:00
return 0 ;
2007-06-17 19:56:38 -05:00
fail_free_hbq_pool :
lpfc_sli_hbqbuf_free_all ( phba ) ;
2008-01-11 01:52:54 -05:00
pci_pool_destroy ( phba - > lpfc_hbq_pool ) ;
2007-06-17 19:56:37 -05:00
fail_free_nlp_mem_pool :
mempool_destroy ( phba - > nlp_mem_pool ) ;
phba - > nlp_mem_pool = NULL ;
2005-04-17 16:05:31 -05:00
fail_free_mbox_pool :
mempool_destroy ( phba - > mbox_mem_pool ) ;
2007-06-17 19:56:36 -05:00
phba - > mbox_mem_pool = NULL ;
2005-04-17 16:05:31 -05:00
fail_free_mbuf_pool :
2007-01-02 01:07:32 +01:00
while ( i - - )
2005-04-17 16:05:31 -05:00
pci_pool_free ( phba - > lpfc_mbuf_pool , pool - > elements [ i ] . virt ,
pool - > elements [ i ] . phys ) ;
kfree ( pool - > elements ) ;
2007-01-02 01:07:32 +01:00
fail_free_lpfc_mbuf_pool :
2005-04-17 16:05:31 -05:00
pci_pool_destroy ( phba - > lpfc_mbuf_pool ) ;
2007-06-17 19:56:36 -05:00
phba - > lpfc_mbuf_pool = NULL ;
2005-04-17 16:05:31 -05:00
fail_free_dma_buf_pool :
pci_pool_destroy ( phba - > lpfc_scsi_dma_buf_pool ) ;
2007-06-17 19:56:36 -05:00
phba - > lpfc_scsi_dma_buf_pool = NULL ;
2005-04-17 16:05:31 -05: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-17 19:56:38 -05:00
kfree ( phba - > vpi_bmask ) ;
2007-06-17 19:56:37 -05:00
lpfc_sli_hbqbuf_free_all ( phba ) ;
2005-04-17 16:05:31 -05: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 ) ;
}
2007-06-17 19:56:38 -05:00
list_for_each_entry_safe ( mbox , next_mbox , & psli - > mboxq_cmpl , 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 ) ;
}
2005-04-17 16:05:31 -05:00
psli - > sli_flag & = ~ LPFC_SLI_MBOX_ACTIVE ;
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-17 19:56:36 -05:00
2007-06-17 19:56:37 -05:00
pci_pool_destroy ( phba - > lpfc_hbq_pool ) ;
2005-04-17 16:05:31 -05: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 15:49:34 -04:00
2007-06-17 19:56:37 -05:00
phba - > lpfc_hbq_pool = NULL ;
2007-06-17 19:56:36 -05:00
phba - > nlp_mem_pool = NULL ;
phba - > mbox_mem_pool = NULL ;
phba - > lpfc_scsi_dma_buf_pool = NULL ;
phba - > lpfc_mbuf_pool = NULL ;
2007-06-17 19:56:38 -05:00
/* Free the iocb lookup array */
2006-07-06 15:49:34 -04:00
kfree ( psli - > iocbq_lookup ) ;
psli - > iocbq_lookup = NULL ;
2005-04-17 16:05:31 -05: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-17 19:56:36 -05:00
unsigned long iflags ;
2005-04-17 16:05:31 -05:00
void * ret ;
ret = pci_pool_alloc ( phba - > lpfc_mbuf_pool , GFP_KERNEL , handle ) ;
2007-06-17 19:56:36 -05:00
spin_lock_irqsave ( & phba - > hbalock , iflags ) ;
2007-06-17 19:56:38 -05:00
if ( ! ret & & ( mem_flags & MEM_PRI ) & & pool - > current_count ) {
2005-04-17 16:05:31 -05:00
pool - > current_count - - ;
ret = pool - > elements [ pool - > current_count ] . virt ;
* handle = pool - > elements [ pool - > current_count ] . phys ;
}
2007-06-17 19:56:36 -05:00
spin_unlock_irqrestore ( & phba - > hbalock , iflags ) ;
2005-04-17 16:05:31 -05:00
return ret ;
}
void
2007-06-17 19:56:36 -05:00
__lpfc_mbuf_free ( struct lpfc_hba * phba , void * virt , dma_addr_t dma )
2005-04-17 16:05:31 -05: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-17 19:56:36 -05: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-17 19:56:37 -05:00
2007-08-02 11:10:31 -04:00
struct hbq_dmabuf *
lpfc_els_hbq_alloc ( struct lpfc_hba * phba )
2007-06-17 19:56:37 -05:00
{
2007-08-02 11:10:31 -04:00
struct hbq_dmabuf * hbqbp ;
hbqbp = kmalloc ( sizeof ( struct hbq_dmabuf ) , GFP_KERNEL ) ;
if ( ! hbqbp )
return NULL ;
hbqbp - > dbuf . virt = pci_pool_alloc ( phba - > lpfc_hbq_pool , GFP_KERNEL ,
& hbqbp - > dbuf . phys ) ;
if ( ! hbqbp - > dbuf . virt ) {
kfree ( hbqbp ) ;
return NULL ;
}
hbqbp - > size = LPFC_BPL_SIZE ;
return hbqbp ;
2007-06-17 19:56:37 -05:00
}
void
2007-08-02 11:10:31 -04:00
lpfc_els_hbq_free ( struct lpfc_hba * phba , struct hbq_dmabuf * hbqbp )
2007-06-17 19:56:37 -05:00
{
2007-08-02 11:10:31 -04:00
pci_pool_free ( phba - > lpfc_hbq_pool , hbqbp - > dbuf . virt , hbqbp - > dbuf . phys ) ;
kfree ( hbqbp ) ;
2007-06-17 19:56:37 -05:00
return ;
}
2007-08-02 11:10:31 -04:00
/* This is ONLY called for the LPFC_ELS_HBQ */
2007-06-17 19:56:38 -05:00
void
lpfc_in_buf_free ( struct lpfc_hba * phba , struct lpfc_dmabuf * mp )
{
struct hbq_dmabuf * hbq_entry ;
2008-02-08 18:50:25 -05:00
unsigned long flags ;
2007-06-17 19:56:38 -05:00
2008-02-08 18:50:14 -05:00
if ( ! mp )
return ;
2007-06-17 19:56:38 -05:00
if ( phba - > sli3_options & LPFC_SLI3_HBQ_ENABLED ) {
2008-02-08 18:50:25 -05:00
/* Check whether HBQ is still in use */
spin_lock_irqsave ( & phba - > hbalock , flags ) ;
if ( ! phba - > hbq_in_use ) {
spin_unlock_irqrestore ( & phba - > hbalock , flags ) ;
return ;
}
2007-06-17 19:56:38 -05:00
hbq_entry = container_of ( mp , struct hbq_dmabuf , dbuf ) ;
2008-02-08 18:50:25 -05:00
list_del ( & hbq_entry - > dbuf . list ) ;
2007-06-17 19:56:38 -05:00
if ( hbq_entry - > tag = = - 1 ) {
2007-08-02 11:10:31 -04:00
( phba - > hbqs [ LPFC_ELS_HBQ ] . hbq_free_buffer )
( phba , hbq_entry ) ;
2007-06-17 19:56:38 -05:00
} else {
lpfc_sli_free_hbq ( phba , hbq_entry ) ;
}
2008-02-08 18:50:25 -05:00
spin_unlock_irqrestore ( & phba - > hbalock , flags ) ;
2007-06-17 19:56:38 -05:00
} else {
lpfc_mbuf_free ( phba , mp - > virt , mp - > phys ) ;
kfree ( mp ) ;
}
return ;
}