/* * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. * All rights reserved * www.brocade.com * * Linux driver for Brocade Fibre Channel Host Bus Adapter. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License (GPL) Version 2 as * published by the Free Software Foundation * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ #include BFA_TRC_FILE(HAL, SGPG); BFA_MODULE(sgpg); /** * bfa_sgpg_mod BFA SGPG Mode module */ /** * Compute and return memory needed by FCP(im) module. */ static void bfa_sgpg_meminfo(struct bfa_iocfc_cfg_s *cfg, u32 *km_len, u32 *dm_len) { if (cfg->drvcfg.num_sgpgs < BFA_SGPG_MIN) cfg->drvcfg.num_sgpgs = BFA_SGPG_MIN; *km_len += (cfg->drvcfg.num_sgpgs + 1) * sizeof(struct bfa_sgpg_s); *dm_len += (cfg->drvcfg.num_sgpgs + 1) * sizeof(struct bfi_sgpg_s); } static void bfa_sgpg_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *minfo, struct bfa_pcidev_s *pcidev) { struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); int i; struct bfa_sgpg_s *hsgpg; struct bfi_sgpg_s *sgpg; u64 align_len; union { u64 pa; union bfi_addr_u addr; } sgpg_pa; INIT_LIST_HEAD(&mod->sgpg_q); INIT_LIST_HEAD(&mod->sgpg_wait_q); bfa_trc(bfa, cfg->drvcfg.num_sgpgs); mod->num_sgpgs = cfg->drvcfg.num_sgpgs; mod->sgpg_arr_pa = bfa_meminfo_dma_phys(minfo); align_len = (BFA_SGPG_ROUNDUP(mod->sgpg_arr_pa) - mod->sgpg_arr_pa); mod->sgpg_arr_pa += align_len; mod->hsgpg_arr = (struct bfa_sgpg_s *) (bfa_meminfo_kva(minfo) + align_len); mod->sgpg_arr = (struct bfi_sgpg_s *) (bfa_meminfo_dma_virt(minfo) + align_len); hsgpg = mod->hsgpg_arr; sgpg = mod->sgpg_arr; sgpg_pa.pa = mod->sgpg_arr_pa; mod->free_sgpgs = mod->num_sgpgs; bfa_assert(!(sgpg_pa.pa & (sizeof(struct bfi_sgpg_s) - 1))); for (i = 0; i < mod->num_sgpgs; i++) { bfa_os_memset(hsgpg, 0, sizeof(*hsgpg)); bfa_os_memset(sgpg, 0, sizeof(*sgpg)); hsgpg->sgpg = sgpg; hsgpg->sgpg_pa = sgpg_pa.addr; list_add_tail(&hsgpg->qe, &mod->sgpg_q); hsgpg++; sgpg++; sgpg_pa.pa += sizeof(struct bfi_sgpg_s); } bfa_meminfo_kva(minfo) = (u8 *) hsgpg; bfa_meminfo_dma_virt(minfo) = (u8 *) sgpg; bfa_meminfo_dma_phys(minfo) = sgpg_pa.pa; } static void bfa_sgpg_initdone(struct bfa_s *bfa) { } static void bfa_sgpg_detach(struct bfa_s *bfa) { } static void bfa_sgpg_start(struct bfa_s *bfa) { } static void bfa_sgpg_stop(struct bfa_s *bfa) { } static void bfa_sgpg_iocdisable(struct bfa_s *bfa) { } /** * bfa_sgpg_public BFA SGPG public functions */ bfa_status_t bfa_sgpg_malloc(struct bfa_s *bfa, struct list_head *sgpg_q, int nsgpgs) { struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); struct bfa_sgpg_s *hsgpg; int i; bfa_trc_fp(bfa, nsgpgs); if (mod->free_sgpgs < nsgpgs) return BFA_STATUS_ENOMEM; for (i = 0; i < nsgpgs; i++) { bfa_q_deq(&mod->sgpg_q, &hsgpg); bfa_assert(hsgpg); list_add_tail(&hsgpg->qe, sgpg_q); } mod->free_sgpgs -= nsgpgs; return BFA_STATUS_OK; } void bfa_sgpg_mfree(struct bfa_s *bfa, struct list_head *sgpg_q, int nsgpg) { struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); struct bfa_sgpg_wqe_s *wqe; bfa_trc_fp(bfa, nsgpg); mod->free_sgpgs += nsgpg; bfa_assert(mod->free_sgpgs <= mod->num_sgpgs); list_splice_tail_init(sgpg_q, &mod->sgpg_q); if (list_empty(&mod->sgpg_wait_q)) return; /** * satisfy as many waiting requests as possible */ do { wqe = bfa_q_first(&mod->sgpg_wait_q); if (mod->free_sgpgs < wqe->nsgpg) nsgpg = mod->free_sgpgs; else nsgpg = wqe->nsgpg; bfa_sgpg_malloc(bfa, &wqe->sgpg_q, nsgpg); wqe->nsgpg -= nsgpg; if (wqe->nsgpg == 0) { list_del(&wqe->qe); wqe->cbfn(wqe->cbarg); } } while (mod->free_sgpgs && !list_empty(&mod->sgpg_wait_q)); } void bfa_sgpg_wait(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe, int nsgpg) { struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); bfa_assert(nsgpg > 0); bfa_assert(nsgpg > mod->free_sgpgs); wqe->nsgpg_total = wqe->nsgpg = nsgpg; /** * allocate any left to this one first */ if (mod->free_sgpgs) { /** * no one else is waiting for SGPG */ bfa_assert(list_empty(&mod->sgpg_wait_q)); list_splice_tail_init(&mod->sgpg_q, &wqe->sgpg_q); wqe->nsgpg -= mod->free_sgpgs; mod->free_sgpgs = 0; } list_add_tail(&wqe->qe, &mod->sgpg_wait_q); } void bfa_sgpg_wcancel(struct bfa_s *bfa, struct bfa_sgpg_wqe_s *wqe) { struct bfa_sgpg_mod_s *mod = BFA_SGPG_MOD(bfa); bfa_assert(bfa_q_is_on_q(&mod->sgpg_wait_q, wqe)); list_del(&wqe->qe); if (wqe->nsgpg_total != wqe->nsgpg) bfa_sgpg_mfree(bfa, &wqe->sgpg_q, wqe->nsgpg_total - wqe->nsgpg); } void bfa_sgpg_winit(struct bfa_sgpg_wqe_s *wqe, void (*cbfn) (void *cbarg), void *cbarg) { INIT_LIST_HEAD(&wqe->sgpg_q); wqe->cbfn = cbfn; wqe->cbarg = cbarg; }