/* * This file is part of the Chelsio FCoE driver for Linux. * * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE.
*/
ae->type = htons(type);
len += 4; /* includes attribute type and length */
len = (len + 3) & ~3; /* should be multiple of 4 bytes */
ae->len = htons(len);
memcpy(ae->value, val, val_len); if (len > val_len)
memset(ae->value + val_len, 0, len - val_len);
*ptr += len;
}
/* Update WWNs */ /* * This may look like a duplication of what csio_fcoe_enable_link() * does, but is absolutely necessary if the vnpi changes between * a FCOE LINK UP and FCOE LINK DOWN.
*/
memcpy(csio_ln_wwnn(ln), rsp->vnport_wwnn, 8);
memcpy(csio_ln_wwpn(ln), rsp->vnport_wwpn, 8);
if (csio_mb_issue(hw, mbp)) {
csio_err(hw, "failed to issue FCOE LINK cmd on port[%d]\n",
portid);
mempool_free(mbp, hw->mb_mempool); return -EINVAL;
}
retval = csio_mb_fw_retval(mbp); if (retval != FW_SUCCESS) {
csio_err(hw, "FCOE LINK %s cmd on port[%d] failed with " "ret:x%x\n", sub_op ? "UP" : "DOWN", portid, retval);
mempool_free(mbp, hw->mb_mempool); return -EINVAL;
}
/* Get FCoE FCF information */
csio_fcoe_read_fcf_init_mb(ln, mbp, CSIO_MB_DEFAULT_TMO,
ln->portid, ln->fcf_flowid, cbfn);
if (csio_mb_issue(hw, mbp)) {
csio_err(hw, "failed to issue FCOE FCF cmd\n");
mempool_free(mbp, hw->mb_mempool); return -EINVAL;
}
return 0;
}
/* * csio_handle_link_up - Logical Linkup event. * @hw - HW module. * @portid - Physical port number * @fcfi - FCF index. * @vnpi - VNP index. * Returns - none. * * This event is received from FW, when virtual link is established between * Physical port[ENode] and FCF. If its new vnpi, then local node object is * created on this FCF and set to [ONLINE] state. * Lnode waits for FW_RDEV_CMD event to be received indicating that * Fabric login is completed and lnode moves to [READY] state. * * This called with hw lock held
*/ staticvoid
csio_handle_link_up(struct csio_hw *hw, uint8_t portid, uint32_t fcfi,
uint32_t vnpi)
{ struct csio_lnode *ln = NULL;
/* Lookup lnode based on vnpi */
ln = csio_ln_lookup_by_vnpi(hw, vnpi); if (!ln) { /* Pick lnode based on portid */
ln = csio_ln_lookup_by_portid(hw, portid); if (!ln) {
csio_err(hw, "failed to lookup fcoe lnode on port:%d\n",
portid);
CSIO_DB_ASSERT(0); return;
}
/* Check if lnode has valid vnp flowid */ if (ln->vnp_flowid != CSIO_INVALID_IDX) { /* New VN-Port */
spin_unlock_irq(&hw->lock);
csio_lnode_alloc(hw);
spin_lock_irq(&hw->lock); if (!ln) {
csio_err(hw, "failed to allocate fcoe lnode" "for port:%d vnpi:x%x\n",
portid, vnpi);
CSIO_DB_ASSERT(0); return;
}
ln->portid = portid;
}
ln->vnp_flowid = vnpi;
ln->dev_num &= ~0xFFFF;
ln->dev_num |= vnpi;
}
/*Initialize fcfi */
ln->fcf_flowid = fcfi;
csio_info(hw, "Port:%d - FCOE LINK UP\n", portid);
CSIO_INC_STATS(ln, n_link_up);
/* Send LINKUP event to SM */
csio_post_event(&ln->sm, CSIO_LNE_LINKUP);
}
/* * csio_post_event_rns * @ln - FCOE lnode * @evt - Given rnode event * Returns - none * * Posts given rnode event to all FCOE rnodes connected with given Lnode. * This routine is invoked when lnode receives LINK_DOWN/DOWN_LINK/CLOSE * event. * * This called with hw lock held
*/ staticvoid
csio_post_event_rns(struct csio_lnode *ln, enum csio_rn_ev evt)
{ struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead; struct list_head *tmp, *next; struct csio_rnode *rn;
/* * csio_post_event_lns * @ln - FCOE lnode * @evt - Given lnode event * Returns - none * * Posts given lnode event to all FCOE lnodes connected with given Lnode. * This routine is invoked when lnode receives LINK_DOWN/DOWN_LINK/CLOSE * event. * * This called with hw lock held
*/ staticvoid
csio_post_event_lns(struct csio_lnode *ln, enum csio_ln_ev evt)
{ struct list_head *tmp; struct csio_lnode *cln, *sln;
/* If NPIV lnode, send evt only to that and return */ if (csio_is_npiv_ln(ln)) {
csio_post_event(&ln->sm, evt); return;
}
sln = ln; /* Traverse children lnodes list and send evt */
list_for_each(tmp, &sln->cln_head) {
cln = (struct csio_lnode *) tmp;
csio_post_event(&cln->sm, evt);
}
/* Send evt to parent lnode */
csio_post_event(&ln->sm, evt);
}
/* * csio_ln_down - Lcoal nport is down * @ln - FCOE Lnode * Returns - none * * Sends LINK_DOWN events to Lnode and its associated NPIVs lnodes. * * This called with hw lock held
*/ staticvoid
csio_ln_down(struct csio_lnode *ln)
{
csio_post_event_lns(ln, CSIO_LNE_LINK_DOWN);
}
/* * csio_handle_link_down - Logical Linkdown event. * @hw - HW module. * @portid - Physical port number * @fcfi - FCF index. * @vnpi - VNP index. * Returns - none * * This event is received from FW, when virtual link goes down between * Physical port[ENode] and FCF. Lnode and its associated NPIVs lnode hosted on * this vnpi[VN-Port] will be de-instantiated. * * This called with hw lock held
*/ staticvoid
csio_handle_link_down(struct csio_hw *hw, uint8_t portid, uint32_t fcfi,
uint32_t vnpi)
{ struct csio_fcf_info *fp; struct csio_lnode *ln;
/* Lookup lnode based on vnpi */
ln = csio_ln_lookup_by_vnpi(hw, vnpi); if (ln) {
fp = ln->fcfinfo;
CSIO_INC_STATS(ln, n_link_down);
/*Warn if linkdown received if lnode is not in ready state */ if (!csio_is_lnode_ready(ln)) {
csio_ln_warn(ln, "warn: FCOE link is already in offline " "Ignoring Fcoe linkdown event on portid %d\n",
portid);
CSIO_INC_STATS(ln, n_evt_drop); return;
}
/* Verify portid */ if (fp->portid != portid) {
csio_ln_warn(ln, "warn: FCOE linkdown recv with " "invalid port %d\n", portid);
CSIO_INC_STATS(ln, n_evt_drop); return;
}
/* * csio_lns_online - The request in online state. * @ln - FCOE lnode. * @evt - Event to be processed. * * Process the given lnode event which is currently in "online" state. * Invoked with HW lock held. * Return - none.
*/ staticvoid
csio_lns_online(struct csio_lnode *ln, enum csio_ln_ev evt)
{ struct csio_hw *hw = csio_lnode_to_hw(ln);
CSIO_INC_STATS(ln, n_evt_sm[evt]); switch (evt) { case CSIO_LNE_LINKUP:
csio_ln_warn(ln, "warn: FCOE link is up already " "Ignoring linkup on port:%d\n", ln->portid);
CSIO_INC_STATS(ln, n_evt_drop); break;
case CSIO_LNE_FAB_INIT_DONE:
csio_set_state(&ln->sm, csio_lns_ready);
case CSIO_LNE_LINK_DOWN: case CSIO_LNE_DOWN_LINK:
csio_set_state(&ln->sm, csio_lns_uninit); if (csio_is_phys_ln(ln)) { /* Remove FCF entry */
list_del_init(&ln->fcfinfo->list);
} break;
default:
csio_ln_dbg(ln, "unexp ln event %d recv from did:x%x in " "ln state[uninit].\n", evt, ln->nport_id);
CSIO_INC_STATS(ln, n_evt_unexp);
break;
} /* switch event */
}
/* * csio_lns_ready - The request in ready state. * @ln - FCOE lnode. * @evt - Event to be processed. * * Process the given lnode event which is currently in "ready" state. * Invoked with HW lock held. * Return - none.
*/ staticvoid
csio_lns_ready(struct csio_lnode *ln, enum csio_ln_ev evt)
{ struct csio_hw *hw = csio_lnode_to_hw(ln);
CSIO_INC_STATS(ln, n_evt_sm[evt]); switch (evt) { case CSIO_LNE_FAB_INIT_DONE:
csio_ln_dbg(ln, "ignoring event %d recv from did x%x" "in ln state[ready].\n", evt, ln->nport_id);
CSIO_INC_STATS(ln, n_evt_drop); break;
case CSIO_LNE_LINK_DOWN:
csio_set_state(&ln->sm, csio_lns_offline);
csio_post_event_rns(ln, CSIO_RNFE_DOWN);
case CSIO_LNE_DOWN_LINK:
csio_set_state(&ln->sm, csio_lns_offline);
csio_post_event_rns(ln, CSIO_RNFE_DOWN);
/* Host need to issue aborts in case if FW has not returned * WRs with status "ABORTED"
*/
spin_unlock_irq(&hw->lock);
csio_lnode_async_event(ln, CSIO_LN_FC_LINKDOWN);
spin_lock_irq(&hw->lock);
/* * csio_lns_offline - The request in offline state. * @ln - FCOE lnode. * @evt - Event to be processed. * * Process the given lnode event which is currently in "offline" state. * Invoked with HW lock held. * Return - none.
*/ staticvoid
csio_lns_offline(struct csio_lnode *ln, enum csio_ln_ev evt)
{ struct csio_hw *hw = csio_lnode_to_hw(ln); struct csio_lnode *rln = hw->rln; int rv;
CSIO_INC_STATS(ln, n_evt_sm[evt]); switch (evt) { case CSIO_LNE_LINKUP:
csio_set_state(&ln->sm, csio_lns_online); /* Read FCF only for physical lnode */ if (csio_is_phys_ln(ln)) {
rv = csio_ln_read_fcf_entry(ln,
csio_ln_read_fcf_cbfn); if (rv != 0) { /* TODO: Send HW RESET event */
CSIO_INC_STATS(ln, n_err); break;
}
/* Add FCF record */
list_add_tail(&ln->fcfinfo->list, &rln->fcf_lsthead);
}
case CSIO_LNE_LINK_DOWN: case CSIO_LNE_DOWN_LINK: case CSIO_LNE_LOGO:
csio_ln_dbg(ln, "ignoring event %d recv from did x%x" "in ln state[offline].\n", evt, ln->nport_id);
CSIO_INC_STATS(ln, n_evt_drop); break;
case CSIO_LNE_CLOSE:
csio_set_state(&ln->sm, csio_lns_uninit);
csio_post_event_rns(ln, CSIO_RNFE_CLOSE); break;
/* * csio_ln_mgmt_wr_handler -Mgmt Work Request handler. * @wr - WR. * @len - WR len. * This handler is invoked when an outstanding mgmt WR is completed. * Its invoked in the context of FW event worker thread for every * mgmt event received. * Return - none.
*/
/** * csio_lnode_start - Kickstart lnode discovery. * @ln: lnode * * This routine kickstarts the discovery by issuing an FCOE_LINK (up) command.
*/ int
csio_lnode_start(struct csio_lnode *ln)
{ int rv = 0; if (csio_is_phys_ln(ln) && !(ln->flags & CSIO_LNF_LINK_ENABLE)) {
rv = csio_fcoe_enable_link(ln, 1);
ln->flags |= CSIO_LNF_LINK_ENABLE;
}
return rv;
}
/** * csio_lnode_stop - Stop the lnode. * @ln: lnode * * This routine is invoked by HW module to stop lnode and its associated NPIV * lnodes.
*/ void
csio_lnode_stop(struct csio_lnode *ln)
{
csio_post_event_lns(ln, CSIO_LNE_DOWN_LINK); if (csio_is_phys_ln(ln) && (ln->flags & CSIO_LNF_LINK_ENABLE)) {
csio_fcoe_enable_link(ln, 0);
ln->flags &= ~CSIO_LNF_LINK_ENABLE;
}
csio_ln_dbg(ln, "stopping ln :%p\n", ln);
}
/** * csio_lnode_close - Close an lnode. * @ln: lnode * * This routine is invoked by HW module to close an lnode and its * associated NPIV lnodes. Lnode and its associated NPIV lnodes are * set to uninitialized state.
*/ void
csio_lnode_close(struct csio_lnode *ln)
{
csio_post_event_lns(ln, CSIO_LNE_CLOSE); if (csio_is_phys_ln(ln))
ln->vnp_flowid = CSIO_INVALID_IDX;
/* * csio_ln_mgmt_submit_wr - Post elsct work request. * @mgmtm - mgmtm * @io_req - io request. * @sub_op - ELS or CT request type * @pld - Dma Payload buffer * @pld_len - Payload len * Prepares ELSCT Work request and sents it to FW. * Returns: 0 - on success
*/ staticint
csio_ln_mgmt_submit_wr(struct csio_mgmtm *mgmtm, struct csio_ioreq *io_req,
uint8_t sub_op, struct csio_dma_buf *pld,
uint32_t pld_len)
{ struct csio_wr_pair wrp; struct csio_lnode *ln = io_req->lnode; struct csio_rnode *rn = io_req->rnode; struct csio_hw *hw = mgmtm->hw;
uint8_t fw_wr[64]; struct ulptx_sgl dsgl;
uint32_t wr_size = 0;
uint8_t im_len = 0;
uint32_t wr_off = 0;
int ret = 0;
/* Calculate WR Size for this ELS REQ */
wr_size = sizeof(struct fw_fcoe_els_ct_wr);
/* Send as immediate data if pld < 256 */ if (pld_len < 256) {
wr_size += ALIGN(pld_len, 8);
im_len = (uint8_t)pld_len;
} else
wr_size += sizeof(struct ulptx_sgl);
/* Roundup WR size in units of 16 bytes */
wr_size = ALIGN(wr_size, 16);
/* Get WR to send ELS REQ */
ret = csio_wr_get(hw, mgmtm->eq_idx, wr_size, &wrp); if (ret != 0) {
csio_err(hw, "Failed to get WR for ec_req %p ret:%d\n",
io_req, ret); return ret;
}
/* Prepare Generic WR used by all ELS/CT cmd */
csio_ln_prep_ecwr(io_req, wr_size, im_len, sub_op,
ln->nport_id, rn->nport_id,
csio_rn_flowid(rn),
&fw_wr[0]);
/* * csio_notify_lnodes: * @hw: HW module * @note: Notification * * Called from the HW SM to fan out notifications to the * Lnode SM. Since the HW SM is entered with lock held, * there is no need to hold locks here. *
*/ void
csio_notify_lnodes(struct csio_hw *hw, enum csio_ln_notify note)
{ struct list_head *tmp; struct csio_lnode *ln;
csio_dbg(hw, "Notifying all nodes of event %d\n", note);
/* Traverse children lnodes list and send evt */
list_for_each(tmp, &hw->sln_head) {
ln = (struct csio_lnode *) tmp;
switch (note) { case CSIO_LN_NOTIFY_HWREADY:
csio_lnode_start(ln); break;
case CSIO_LN_NOTIFY_HWRESET: case CSIO_LN_NOTIFY_HWREMOVE:
csio_lnode_close(ln); break;
case CSIO_LN_NOTIFY_HWSTOP:
csio_lnode_stop(ln); break;
default: break;
}
}
}
/* * csio_disable_lnodes: * @hw: HW module * @portid:port id * @disable: disable/enable flag. * If disable=1, disables all lnode hosted on given physical port. * otherwise enables all the lnodes on given phsysical port. * This routine need to called with hw lock held.
*/ void
csio_disable_lnodes(struct csio_hw *hw, uint8_t portid, bool disable)
{ struct list_head *tmp; struct csio_lnode *ln;
csio_dbg(hw, "Notifying event to all nodes of port:%d\n", portid);
/* Traverse sibling lnodes list and send evt */
list_for_each(tmp, &hw->sln_head) {
ln = (struct csio_lnode *) tmp; if (ln->portid != portid) continue;
if (disable)
csio_lnode_stop(ln); else
csio_lnode_start(ln);
}
}
/* * csio_lnode_init - Initialize the members of an lnode. * @ln: lnode
*/ int
csio_lnode_init(struct csio_lnode *ln, struct csio_hw *hw, struct csio_lnode *pln)
{ int rv = -EINVAL;
/* Link this lnode to hw */
csio_lnode_to_hw(ln) = hw;
/* Link child to parent if child lnode */ if (pln)
ln->pln = pln; else
ln->pln = NULL;
/* Initialize scsi_tgt and timers to zero */
ln->n_scsi_tgts = 0;
ln->last_scan_ntgts = 0;
ln->tgt_scan_tick = 0;
/* Initialize rnode list */
INIT_LIST_HEAD(&ln->rnhead);
INIT_LIST_HEAD(&ln->cln_head);
/* Initialize log level for debug */
ln->params.log_level = hw->params.log_level;
if (csio_ln_init(ln)) goto err;
/* Add lnode to list of sibling or children lnodes */
spin_lock_irq(&hw->lock);
list_add_tail(&ln->sm.sm_list, pln ? &pln->cln_head : &hw->sln_head); if (pln)
pln->num_vports++;
spin_unlock_irq(&hw->lock);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.