/* * Internal Functions * This function will initialize the src_mac address to be * used in outgoing frames
*/ staticinlinevoid fnic_fdls_set_fcoe_srcmac(struct fnic *fnic,
uint8_t *src_mac)
{
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Setting src mac: %02x:%02x:%02x:%02x:%02x:%02x",
src_mac[0], src_mac[1], src_mac[2], src_mac[3],
src_mac[4], src_mac[5]);
memcpy(fnic->iport.fpma, src_mac, 6);
}
/* * This function will initialize the dst_mac address to be * used in outgoing frames
*/ staticinlinevoid fnic_fdls_set_fcoe_dstmac(struct fnic *fnic,
uint8_t *dst_mac)
{
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Setting dst mac: %02x:%02x:%02x:%02x:%02x:%02x",
dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3],
dst_mac[4], dst_mac[5]);
/* * FPMA can be either taken from ethhdr(dst_mac) or flogi resp * or derive from FC_MAP and FCID combination. While it should be * same, revisit this if there is any possibility of not-correct.
*/ void fnic_fdls_learn_fcoe_macs(struct fnic_iport_s *iport, void *rx_frame,
uint8_t *fcid)
{ struct fnic *fnic = iport->fnic; struct ethhdr *ethhdr = (struct ethhdr *) rx_frame;
uint8_t fcmac[6] = { 0x0E, 0xFC, 0x00, 0x00, 0x00, 0x00 };
if (fnic->stop_rx_link_events) {
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Stop link rx events\n"); return;
}
/* Do not process if the fnic is already in transitional state */ if ((fnic->state != FNIC_IN_ETH_MODE)
&& (fnic->state != FNIC_IN_FC_MODE)) {
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "fnic in transitional state: %d. link up: %d ignored",
fnic->state, vnic_dev_link_status(fnic->vdev));
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Current link status: %d iport state: %d\n",
fnic->link_status, fnic->iport.state); return;
}
while (fnic->reset_in_progress == IN_PROGRESS) {
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "fnic reset in progress. Link event needs to wait\n");
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "waiting for reset completion\n");
wait_for_completion_timeout(&fnic->reset_completion_wait,
msecs_to_jiffies(5000));
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "woken up from reset completion wait\n");
spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
max_count++; if (max_count >= MAX_RESET_WAIT_COUNT) {
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Rstth waited for too long. Skipping handle link event\n");
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); return;
}
}
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Marking fnic reset in progress\n");
fnic->reset_in_progress = IN_PROGRESS;
if ((vnic_dev_get_intr_mode(fnic->vdev) != VNIC_DEV_INTR_MODE_MSI) ||
(fnic->link_status != old_link_status)) {
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "old link status: %d link status: %d\n",
old_link_status, (int) fnic->link_status);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "old down count %d down count: %d\n",
old_link_down_cnt, (int) fnic->link_down_cnt);
}
if (old_link_status == fnic->link_status) { if (!fnic->link_status) { /* DOWN -> DOWN */
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "down->down\n");
} else { if (old_link_down_cnt != fnic->link_down_cnt) { /* UP -> DOWN -> UP */
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "up->down. Link down\n");
fnic_fdls_link_status_change(fnic, 0);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "down->up. Link up\n");
fnic_fdls_link_status_change(fnic, 1);
} else { /* UP -> UP */
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "up->up\n");
}
}
} elseif (fnic->link_status) { /* DOWN -> UP */
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "down->up. Link up\n");
fnic_fdls_link_status_change(fnic, 1);
} else { /* UP -> DOWN */
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "up->down. Link down\n");
fnic_fdls_link_status_change(fnic, 0);
}
/* * If we're in a transitional state, just re-queue and return. * The queue will be serviced when we get to a stable state.
*/ if (fnic->state != FNIC_IN_FC_MODE &&
fnic->state != FNIC_IN_ETH_MODE) {
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Cannot process frame in transitional state\n");
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); return;
}
list_del(&cur_frame->links);
/* Frames from FCP_RQ will have ethhdrs stripped off */
fchdr_offset = (cur_frame->rx_ethhdr_stripped) ?
0 : FNIC_ETH_FCOE_HDRS_OFFSET;
/* * If we're in a transitional state, just re-queue and return. * The queue will be serviced when we get to a stable state.
*/ if (fnic->state != FNIC_IN_FC_MODE &&
fnic->state != FNIC_IN_ETH_MODE) {
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); return;
}
int fnic_rq_cmpl_handler(struct fnic *fnic, int rq_work_to_do)
{ unsignedint tot_rq_work_done = 0, cur_work_done; unsignedint i; int err;
for (i = 0; i < fnic->rq_count; i++) {
cur_work_done = vnic_cq_service(&fnic->cq[i], rq_work_to_do,
fnic_rq_cmpl_handler_cont,
NULL); if (cur_work_done && fnic->stop_rx_link_events != 1) {
err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame); if (err)
shost_printk(KERN_ERR, fnic->host, "fnic_alloc_rq_frame can't alloc" " frame\n");
}
tot_rq_work_done += cur_work_done;
}
return tot_rq_work_done;
}
/* * This function is called once at init time to allocate and fill RQ * buffers. Subsequently, it is called in the interrupt context after RQ * buffer processing to replenish the buffers in the RQ
*/ int fnic_alloc_rq_frame(struct vnic_rq *rq)
{ struct fnic *fnic = vnic_dev_priv(rq->vdev); void *buf;
u16 len;
dma_addr_t pa; int ret;
len = FNIC_FRAME_HT_ROOM;
buf = kmalloc(len, GFP_ATOMIC); if (!buf) {
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Unable to allocate RQ buffer of size: %d\n", len); return -ENOMEM;
}
pa = dma_map_single(&fnic->pdev->dev, buf, len, DMA_FROM_DEVICE); if (dma_mapping_error(&fnic->pdev->dev, pa)) {
ret = -ENOMEM;
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "PCI mapping failed with error %d\n", ret); goto free_buf;
}
if (!vnic_wq_desc_avail(wq)) {
dma_unmap_single(&fnic->pdev->dev, pa, frame_len, DMA_TO_DEVICE);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "vnic work queue descriptor is not available");
ret = -1; goto fnic_send_frame_end;
}
/* hw inserts cos value */
fnic_queue_wq_desc(wq, frame, pa, frame_len, FC_EOF_T,
0, fnic->vlan_id, 1, 1, 1);
/** * fdls_send_fcoe_frame - send a filled-in FC frame, filling in eth and FCoE * info. This interface is used only in the non fast path. (login, fabric * registrations etc.) * * @fnic: fnic instance * @frame: frame structure with FC payload filled in * @frame_size: length of the frame to be sent * @srcmac: source mac address * @dstmac: destination mac address * * Called with the fnic lock held.
*/ staticint
fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size,
uint8_t *srcmac, uint8_t *dstmac)
{ struct ethhdr *pethhdr; struct fcoe_hdr *pfcoe_hdr; struct fnic_frame_list *frame_elem; int len = frame_size; int ret; struct fc_frame_header *fchdr = (struct fc_frame_header *) (frame +
FNIC_ETH_FCOE_HDRS_OFFSET);
/* * Queue frame if in a transitional state. * This occurs while registering the Port_ID / MAC address after FLOGI.
*/ if ((fnic->state != FNIC_IN_FC_MODE)
&& (fnic->state != FNIC_IN_ETH_MODE)) {
frame_elem = mempool_alloc(fnic->frame_elem_pool,
GFP_ATOMIC | __GFP_ZERO); if (!frame_elem) {
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Failed to allocate memory for frame elem"); return -ENOMEM;
}
/** * fnic_flush_tx() - send queued frames. * @work: pointer to work element * * Send frames that were waiting to go out in FC or Ethernet mode. * Whenever changing modes we purge queued frames, so these frames should * be queued for the stable mode that we're in, either FC or Ethernet. * * Called without fnic_lock held.
*/ void fnic_flush_tx(struct work_struct *work)
{ struct fnic *fnic = container_of(work, struct fnic, flush_work); struct fc_frame *fp; struct fnic_frame_list *cur_frame, *next;
/* Change state to reflect transition to FC mode */ if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE)
fnic->state = FNIC_IN_ETH_TRANS_FC_MODE; else {
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Unexpected fnic state while processing FLOGI response\n"); return -1;
}
/* * Send FLOGI registration to firmware to set up FC mode. * The new address will be set up when registration completes.
*/
ret = fnic_flogi_reg_handler(fnic, port_id); if (ret < 0) {
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "FLOGI registration error ret: %d fnic state: %d\n",
ret, fnic->state); if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE)
fnic->state = FNIC_IN_ETH_MODE;
if (rport) { /* tport resource release will be done * after fnic_terminate_rport_io()
*/
tport->flags |= FNIC_FDLS_TPORT_DELETED;
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
/* Interface to scsi_fc_transport */
fc_remote_port_delete(rport);
spin_lock_irqsave(&fnic->fnic_lock, flags);
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, "Deregistered and freed tport fcid: 0x%x from scsi transport fc",
tport->fcid);
/* * the dd_data is allocated by fc transport * of size dd_fcrport_size
*/
rdd_data = rport->dd_data;
rdd_data->tport = NULL;
rdd_data->iport = NULL;
list_del(&tport->links);
kfree(tport);
} else {
fnic_del_tport_timer_sync(fnic, tport);
list_del(&tport->links);
kfree(tport);
}
}
/* * This is a single thread. It is per fnic module, not per fnic * All the fnics that need to be reset * have been serialized via the reset fnic list.
*/
spin_lock_irqsave(&reset_fnic_list_lock, reset_fnic_list_lock_flags);
list_for_each_entry_safe(cur_fnic, next_fnic, &reset_fnic_list, links) {
list_del(&cur_fnic->links);
spin_unlock_irqrestore(&reset_fnic_list_lock,
reset_fnic_list_lock_flags);
dev_err(&cur_fnic->pdev->dev, "fnic: <%d>: issuing a host reset\n",
cur_fnic->fnic_num);
host_reset_ret_code = fnic_host_reset(cur_fnic->host);
dev_err(&cur_fnic->pdev->dev, "fnic: <%d>: returned from host reset with status: %d\n",
cur_fnic->fnic_num, host_reset_ret_code);
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.