/* * Copyright (C) 2015 Mellanox Technologies Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include #include #include #include #include #include #include #include #include "flexboot_nodnic.h" #include "mlx_utils/include/public/mlx_types.h" #include "mlx_utils/include/public/mlx_utils.h" #include "mlx_utils/include/public/mlx_bail.h" #include "mlx_nodnic/include/mlx_cmd.h" #include "mlx_utils/include/public/mlx_memory.h" #include "mlx_utils/include/public/mlx_pci.h" #include "mlx_nodnic/include/mlx_device.h" #include "mlx_nodnic/include/mlx_port.h" #include #include #include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig.h" #include "mlx_utils/mlx_lib/mlx_nvconfig/mlx_nvconfig_defaults.h" #include "mlx_utils/include/public/mlx_pci_gw.h" #include "mlx_utils/mlx_lib/mlx_vmac/mlx_vmac.h" #include "mlx_utils/mlx_lib/mlx_mtu/mlx_mtu.h" /*************************************************************************** * * Completion queue operations * *************************************************************************** */ static int flexboot_nodnic_arm_cq ( struct flexboot_nodnic_port *port ) { #ifndef DEVICE_CX3 mlx_uint32 val32 = 0; union arm_cq_uar cq_uar; #define ARM_CQ_UAR_CQ_CI_MASK 0xffffff #define ARM_CQ_UAR_CMDSN_MASK 3 #define ARM_CQ_UAR_CMDSN_OFFSET 28 #define ARM_CQ_UAR_CQ_CI_OFFSET 0x20 if ( port->port_priv.device->device_cap.support_bar_cq_ctrl ) { cq_uar.dword[0] = cpu_to_be32((port->eth_cq->next_idx & ARM_CQ_UAR_CQ_CI_MASK) | ((port->cmdsn++ & ARM_CQ_UAR_CMDSN_MASK) << ARM_CQ_UAR_CMDSN_OFFSET)); cq_uar.dword[1] = cpu_to_be32(port->eth_cq->cqn); wmb(); writeq(cq_uar.qword, port->port_priv.device->uar.virt + ARM_CQ_UAR_CQ_CI_OFFSET); port->port_priv.arm_cq_doorbell_record->dword[0] = cq_uar.dword[1]; port->port_priv.arm_cq_doorbell_record->dword[1] = cq_uar.dword[0]; } else { val32 = ( port->eth_cq->next_idx & 0xffffff ); if ( nodnic_port_set ( & port->port_priv, nodnic_port_option_arm_cq, val32 ) ) { MLX_DEBUG_ERROR( port->port_priv.device, "Failed to arm the CQ\n" ); return MLX_FAILED; } } #else mlx_utils *utils = port->port_priv.device->utils; nodnic_port_data_flow_gw *ptr = port->port_priv.data_flow_gw; mlx_uint32 data = 0; mlx_uint32 val = 0; if ( port->port_priv.device->device_cap.crspace_doorbells == 0 ) { val = ( port->eth_cq->next_idx & 0xffff ); if ( nodnic_port_set ( & port->port_priv, nodnic_port_option_arm_cq, val ) ) { MLX_DEBUG_ERROR( port->port_priv.device, "Failed to arm the CQ\n" ); return MLX_FAILED; } } else { /* Arming the CQ with CQ CI should be with this format - * 16 bit - CQ CI - same endianness as the FW (don't swap bytes) * 15 bit - reserved * 1 bit - arm CQ - must correct the endianness with the reserved above */ data = ( ( ( port->eth_cq->next_idx & 0xffff ) << 16 ) | 0x0080 ); /* Write the new index and update FW that new data was submitted */ mlx_pci_mem_write ( utils, MlxPciWidthUint32, 0, ( mlx_uintn ) & ( ptr->armcq_cq_ci_dword ), 1, &data ); } #endif return 0; } /** * Create completion queue * * @v ibdev Infiniband device * @v cq Completion queue * @ret rc Return status code */ static int flexboot_nodnic_create_cq ( struct ib_device *ibdev , struct ib_completion_queue *cq ) { struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq; mlx_status status = MLX_SUCCESS; mlx_uint32 cqn; flexboot_nodnic_cq = (struct flexboot_nodnic_completion_queue *) zalloc(sizeof(*flexboot_nodnic_cq)); if ( flexboot_nodnic_cq == NULL ) { status = MLX_OUT_OF_RESOURCES; goto qp_alloc_err; } status = nodnic_port_create_cq(&port->port_priv, cq->num_cqes * flexboot_nodnic->callbacks->get_cqe_size(), &flexboot_nodnic_cq->nodnic_completion_queue ); MLX_FATAL_CHECK_STATUS(status, create_err, "nodnic_port_create_cq failed"); flexboot_nodnic->callbacks->cqe_set_owner( flexboot_nodnic_cq->nodnic_completion_queue->cq_virt, cq->num_cqes); if ( flexboot_nodnic->device_priv.device_cap.support_bar_cq_ctrl ) { status = nodnic_port_query(&port->port_priv, nodnic_port_option_cq_n_index, (mlx_uint32 *)&cqn ); MLX_FATAL_CHECK_STATUS(status, read_cqn_err, "failed to query cqn"); cq->cqn = cqn; } ib_cq_set_drvdata ( cq, flexboot_nodnic_cq ); return status; read_cqn_err: create_err: free(flexboot_nodnic_cq); qp_alloc_err: return status; } /** * Destroy completion queue * * @v ibdev Infiniband device * @v cq Completion queue */ static void flexboot_nodnic_destroy_cq ( struct ib_device *ibdev , struct ib_completion_queue *cq ) { struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq = ib_cq_get_drvdata ( cq ); nodnic_port_destroy_cq(&port->port_priv, flexboot_nodnic_cq->nodnic_completion_queue); free(flexboot_nodnic_cq); } static struct ib_work_queue * flexboot_nodnic_find_wq ( struct ib_device *ibdev , struct ib_completion_queue *cq, unsigned long qpn, int is_send ) { struct ib_work_queue *wq; struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp; struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; struct nodnic_ring *ring; mlx_uint32 out_qpn; list_for_each_entry ( wq, &cq->work_queues, list ) { flexboot_nodnic_qp = ib_qp_get_drvdata ( wq->qp ); if( wq->is_send == is_send && wq->is_send == TRUE ) { ring = &flexboot_nodnic_qp->nodnic_queue_pair->send.nodnic_ring; } else if( wq->is_send == is_send && wq->is_send == FALSE ) { ring = &flexboot_nodnic_qp->nodnic_queue_pair->receive.nodnic_ring; } else { continue; } nodnic_port_get_qpn(&port->port_priv, ring, &out_qpn); if ( out_qpn == qpn ) return wq; } return NULL; } /** * Handle completion * * @v ibdev Infiniband device * @v cq Completion queue * @v cqe Hardware completion queue entry * @ret rc Return status code */ static int flexboot_nodnic_complete ( struct ib_device *ibdev, struct ib_completion_queue *cq, struct cqe_data *cqe_data ) { struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); struct ib_work_queue *wq; struct ib_queue_pair *qp; struct io_buffer *iobuf; struct ib_address_vector recv_dest; struct ib_address_vector recv_source; unsigned long qpn; unsigned long wqe_idx; unsigned long wqe_idx_mask; size_t len; int rc = 0; /* Parse completion */ qpn = cqe_data->qpn; if ( cqe_data->is_error == TRUE ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p CQN %#lx syndrome %x vendor %x\n", flexboot_nodnic, cq->cqn, cqe_data->syndrome, cqe_data->vendor_err_syndrome ); rc = -EIO; /* Don't return immediately; propagate error to completer */ } /* Identify work queue */ wq = flexboot_nodnic_find_wq( ibdev, cq, qpn, cqe_data->is_send ); if ( wq == NULL ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p CQN %#lx unknown %s QPN %#lx\n", flexboot_nodnic, cq->cqn, ( cqe_data->is_send ? "send" : "recv" ), qpn ); return -EIO; } qp = wq->qp; /* Identify work queue entry */ wqe_idx = cqe_data->wqe_counter; wqe_idx_mask = ( wq->num_wqes - 1 ); DBGCP ( flexboot_nodnic, "NODNIC %p CQN %#lx QPN %#lx %s WQE %#lx completed:\n", flexboot_nodnic, cq->cqn, qp->qpn, ( cqe_data->is_send ? "send" : "recv" ), wqe_idx ); /* Identify I/O buffer */ iobuf = wq->iobufs[wqe_idx & wqe_idx_mask]; if ( iobuf == NULL ) { DBGC ( flexboot_nodnic, "NODNIC %p CQN %#lx QPN %#lx empty %s WQE %#lx\n", flexboot_nodnic, cq->cqn, qp->qpn, ( cqe_data->is_send ? "send" : "recv" ), wqe_idx ); return -EIO; } wq->iobufs[wqe_idx & wqe_idx_mask] = NULL; if ( cqe_data->is_send == TRUE ) { /* Hand off to completion handler */ ib_complete_send ( ibdev, qp, iobuf, rc ); } else if ( rc != 0 ) { /* Propagate error to receive completion handler */ ib_complete_recv ( ibdev, qp, NULL, NULL, iobuf, rc ); } else { /* Set received length */ len = cqe_data->byte_cnt; assert ( len <= iob_tailroom ( iobuf ) ); iob_put ( iobuf, len ); memset ( &recv_dest, 0, sizeof ( recv_dest ) ); recv_dest.qpn = qpn; memset ( &recv_source, 0, sizeof ( recv_source ) ); switch ( qp->type ) { case IB_QPT_SMI: case IB_QPT_GSI: case IB_QPT_UD: case IB_QPT_RC: break; case IB_QPT_ETH: break; default: assert ( 0 ); return -EINVAL; } /* Hand off to completion handler */ ib_complete_recv ( ibdev, qp, &recv_dest, &recv_source, iobuf, rc ); } return rc; } /** * Poll completion queue * * @v ibdev Infiniband device * @v cq Completion queues */ static void flexboot_nodnic_poll_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq) { struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); struct flexboot_nodnic_completion_queue *flexboot_nodnic_cq = ib_cq_get_drvdata ( cq ); void *cqe; mlx_size cqe_size; struct cqe_data cqe_data; unsigned int cqe_idx_mask; int rc; cqe_size = flexboot_nodnic->callbacks->get_cqe_size(); while ( TRUE ) { /* Look for completion entry */ cqe_idx_mask = ( cq->num_cqes - 1 ); cqe = ((uint8_t *)flexboot_nodnic_cq->nodnic_completion_queue->cq_virt) + cqe_size * (cq->next_idx & cqe_idx_mask); /* TODO: check fill_completion */ flexboot_nodnic->callbacks->fill_completion(cqe, &cqe_data); if ( cqe_data.owner ^ ( ( cq->next_idx & cq->num_cqes ) ? 1 : 0 ) ) { /* Entry still owned by hardware; end of poll */ break; } /* Handle completion */ rc = flexboot_nodnic_complete ( ibdev, cq, &cqe_data ); if ( rc != 0 ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p CQN %#lx failed to complete: %s\n", flexboot_nodnic, cq->cqn, strerror ( rc ) ); DBGC_HDA ( flexboot_nodnic, virt_to_phys ( cqe ), cqe, sizeof ( *cqe ) ); } /* Update completion queue's index */ cq->next_idx++; } } /*************************************************************************** * * Queue pair operations * *************************************************************************** */ /** * Create queue pair * * @v ibdev Infiniband device * @v qp Queue pair * @ret rc Return status code */ static int flexboot_nodnic_create_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp; mlx_status status = MLX_SUCCESS; flexboot_nodnic_qp = (struct flexboot_nodnic_queue_pair *)zalloc(sizeof(*flexboot_nodnic_qp)); if ( flexboot_nodnic_qp == NULL ) { status = MLX_OUT_OF_RESOURCES; goto qp_alloc_err; } status = nodnic_port_create_qp(&port->port_priv, qp->type, qp->send.num_wqes * sizeof(struct nodnic_send_wqbb), qp->send.num_wqes, qp->recv.num_wqes * sizeof(struct nodnic_recv_wqe), qp->recv.num_wqes, &flexboot_nodnic_qp->nodnic_queue_pair); MLX_FATAL_CHECK_STATUS(status, create_err, "nodnic_port_create_qp failed"); ib_qp_set_drvdata ( qp, flexboot_nodnic_qp ); return status; create_err: free(flexboot_nodnic_qp); qp_alloc_err: return status; } /** * Modify queue pair * * @v ibdev Infiniband device * @v qp Queue pair * @ret rc Return status code */ static int flexboot_nodnic_modify_qp ( struct ib_device *ibdev __unused, struct ib_queue_pair *qp __unused) { /*not needed*/ return 0; } /** * Destroy queue pair * * @v ibdev Infiniband device * @v qp Queue pair */ static void flexboot_nodnic_destroy_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = ib_qp_get_drvdata ( qp ); nodnic_port_destroy_qp(&port->port_priv, qp->type, flexboot_nodnic_qp->nodnic_queue_pair); free(flexboot_nodnic_qp); } /*************************************************************************** * * Work request operations * *************************************************************************** */ /** * Post send work queue entry * * @v ibdev Infiniband device * @v qp Queue pair * @v av Address vector * @v iobuf I/O buffer * @ret rc Return status code */ static int flexboot_nodnic_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_address_vector *av, struct io_buffer *iobuf) { struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = ib_qp_get_drvdata ( qp ); struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; struct ib_work_queue *wq = &qp->send; struct nodnic_send_wqbb *wqbb; nodnic_qp *nodnic_qp = flexboot_nodnic_qp->nodnic_queue_pair; struct nodnic_send_ring *send_ring = &nodnic_qp->send; mlx_status status = MLX_SUCCESS; unsigned int wqe_idx_mask; unsigned long wqe_idx; if ( ( port->port_priv.dma_state == FALSE ) || ( port->port_priv.port_state & NODNIC_PORT_DISABLING_DMA ) ) { DBGC ( flexboot_nodnic, "flexboot_nodnic DMA disabled\n"); status = -ENETDOWN; goto post_send_done; } /* Allocate work queue entry */ wqe_idx = wq->next_idx; wqe_idx_mask = ( wq->num_wqes - 1 ); if ( wq->iobufs[wqe_idx & wqe_idx_mask] ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p QPN %#lx send queue full\n", flexboot_nodnic, qp->qpn ); status = -ENOBUFS; goto post_send_done; } wqbb = &send_ring->wqe_virt[wqe_idx & wqe_idx_mask]; wq->iobufs[wqe_idx & wqe_idx_mask] = iobuf; assert ( flexboot_nodnic->callbacks-> fill_send_wqe[qp->type] != NULL ); status = flexboot_nodnic->callbacks-> fill_send_wqe[qp->type] ( ibdev, qp, av, iobuf, wqbb, wqe_idx ); if ( status != 0 ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p QPN %#lx fill send wqe failed\n", flexboot_nodnic, qp->qpn ); goto post_send_done; } wq->next_idx++; status = port->port_priv.send_doorbell ( &port->port_priv, &send_ring->nodnic_ring, ( mlx_uint16 ) wq->next_idx ); if ( flexboot_nodnic->callbacks->tx_uar_send_doorbell_fn ) { flexboot_nodnic->callbacks->tx_uar_send_doorbell_fn ( ibdev, wqbb ); } if ( status != 0 ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p ring send doorbell failed\n", flexboot_nodnic ); } post_send_done: return status; } /** * Post receive work queue entry * * @v ibdev Infiniband device * @v qp Queue pair * @v iobuf I/O buffer * @ret rc Return status code */ static int flexboot_nodnic_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct io_buffer *iobuf ) { struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = ib_qp_get_drvdata ( qp ); struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; struct ib_work_queue *wq = &qp->recv; nodnic_qp *nodnic_qp = flexboot_nodnic_qp->nodnic_queue_pair; struct nodnic_recv_ring *recv_ring = &nodnic_qp->receive; struct nodnic_recv_wqe *wqe; unsigned int wqe_idx_mask; mlx_status status = MLX_SUCCESS; /* Allocate work queue entry */ wqe_idx_mask = ( wq->num_wqes - 1 ); if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p QPN %#lx receive queue full\n", flexboot_nodnic, qp->qpn ); status = -ENOBUFS; goto post_recv_done; } wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf; wqe = &((struct nodnic_recv_wqe*)recv_ring->wqe_virt)[wq->next_idx & wqe_idx_mask]; MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) ); MLX_FILL_1 ( &wqe->data[0], 1, l_key, flexboot_nodnic->device_priv.lkey ); MLX_FILL_H ( &wqe->data[0], 2, local_address_h, virt_to_bus ( iobuf->data ) ); MLX_FILL_1 ( &wqe->data[0], 3, local_address_l, virt_to_bus ( iobuf->data ) ); wq->next_idx++; status = port->port_priv.recv_doorbell ( &port->port_priv, &recv_ring->nodnic_ring, ( mlx_uint16 ) wq->next_idx ); if ( status != 0 ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p ring receive doorbell failed\n", flexboot_nodnic ); } post_recv_done: return status; } /*************************************************************************** * * Event queues * *************************************************************************** */ static void flexboot_nodnic_poll_eq ( struct ib_device *ibdev ) { struct flexboot_nodnic *flexboot_nodnic; struct flexboot_nodnic_port *port; struct net_device *netdev; nodnic_port_state state = 0; mlx_status status; if ( ! ibdev ) { DBG ( "%s: ibdev = NULL!!!\n", __FUNCTION__ ); return; } flexboot_nodnic = ib_get_drvdata ( ibdev ); port = &flexboot_nodnic->port[ibdev->port - 1]; netdev = port->netdev; if ( ! netdev_is_open ( netdev ) ) { DBG2( "%s: port %d is closed\n", __FUNCTION__, port->ibdev->port ); return; } /* we don't poll EQ. Just poll link status if it's not active */ if ( ! netdev_link_ok ( netdev ) ) { status = nodnic_port_get_state ( &port->port_priv, &state ); MLX_FATAL_CHECK_STATUS(status, state_err, "nodnic_port_get_state failed"); if ( state == nodnic_port_state_active ) { DBG( "%s: port %d physical link is up\n", __FUNCTION__, port->ibdev->port ); port->type->state_change ( flexboot_nodnic, port, 1 ); } } state_err: return; } /*************************************************************************** * * Multicast group operations * *************************************************************************** */ static int flexboot_nodnic_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp, union ib_gid *gid) { struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; mlx_mac_address mac; mlx_status status = MLX_SUCCESS; switch (qp->type) { case IB_QPT_ETH: memcpy(&mac, &gid, sizeof(mac)); status = nodnic_port_add_mac_filter(&port->port_priv, mac); MLX_CHECK_STATUS(flexboot_nodnic->device_priv, status, mac_err, "nodnic_port_add_mac_filter failed"); break; default: break; } mac_err: return status; } static void flexboot_nodnic_mcast_detach ( struct ib_device *ibdev, struct ib_queue_pair *qp, union ib_gid *gid ) { struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); struct flexboot_nodnic_port *port = &flexboot_nodnic->port[ibdev->port - 1]; mlx_mac_address mac; mlx_status status = MLX_SUCCESS; switch (qp->type) { case IB_QPT_ETH: memcpy(&mac, &gid, sizeof(mac)); status = nodnic_port_remove_mac_filter(&port->port_priv, mac); MLX_CHECK_STATUS(flexboot_nodnic->device_priv, status, mac_err, "nodnic_port_remove_mac_filter failed"); break; default: break; } mac_err: return; } /*************************************************************************** * * Infiniband link-layer operations * *************************************************************************** */ /** * Initialise Infiniband link * * @v ibdev Infiniband device * @ret rc Return status code */ static int flexboot_nodnic_ib_open ( struct ib_device *ibdev __unused) { int rc = 0; /*TODO: add implementation*/ return rc; } /** * Close Infiniband link * * @v ibdev Infiniband device */ static void flexboot_nodnic_ib_close ( struct ib_device *ibdev __unused) { /*TODO: add implementation*/ } /** * Inform embedded subnet management agent of a received MAD * * @v ibdev Infiniband device * @v mad MAD * @ret rc Return status code */ static int flexboot_nodnic_inform_sma ( struct ib_device *ibdev __unused, union ib_mad *mad __unused) { /*TODO: add implementation*/ return 0; } /** flexboot_nodnic Infiniband operations */ static struct ib_device_operations flexboot_nodnic_ib_operations = { .create_cq = flexboot_nodnic_create_cq, .destroy_cq = flexboot_nodnic_destroy_cq, .create_qp = flexboot_nodnic_create_qp, .modify_qp = flexboot_nodnic_modify_qp, .destroy_qp = flexboot_nodnic_destroy_qp, .post_send = flexboot_nodnic_post_send, .post_recv = flexboot_nodnic_post_recv, .poll_cq = flexboot_nodnic_poll_cq, .poll_eq = flexboot_nodnic_poll_eq, .open = flexboot_nodnic_ib_open, .close = flexboot_nodnic_ib_close, .mcast_attach = flexboot_nodnic_mcast_attach, .mcast_detach = flexboot_nodnic_mcast_detach, .set_port_info = flexboot_nodnic_inform_sma, .set_pkey_table = flexboot_nodnic_inform_sma, }; /*************************************************************************** * * * *************************************************************************** */ #define FLEX_NODNIC_TX_POLL_TOUT 500000 #define FLEX_NODNIC_TX_POLL_USLEEP 10 static void flexboot_nodnic_complete_all_tx ( struct flexboot_nodnic_port *port ) { struct ib_device *ibdev = port->ibdev; struct ib_completion_queue *cq; struct ib_work_queue *wq; int keep_polling = 0; int timeout = FLEX_NODNIC_TX_POLL_TOUT; list_for_each_entry ( cq, &ibdev->cqs, list ) { do { ib_poll_cq ( ibdev, cq ); keep_polling = 0; list_for_each_entry ( wq, &cq->work_queues, list ) { if ( wq->is_send ) keep_polling += ( wq->fill > 0 ); } udelay ( FLEX_NODNIC_TX_POLL_USLEEP ); } while ( keep_polling && ( timeout-- > 0 ) ); } } static void flexboot_nodnic_port_disable_dma ( struct flexboot_nodnic_port *port ) { nodnic_port_priv *port_priv = & ( port->port_priv ); mlx_status status; if ( ! ( port_priv->port_state & NODNIC_PORT_OPENED ) ) return; port_priv->port_state |= NODNIC_PORT_DISABLING_DMA; flexboot_nodnic_complete_all_tx ( port ); if ( ( status = nodnic_port_disable_dma ( port_priv ) ) ) { MLX_DEBUG_WARN ( port, "Failed to disable DMA %d\n", status ); } port_priv->port_state &= ~NODNIC_PORT_DISABLING_DMA; } /*************************************************************************** * * Ethernet operation * *************************************************************************** */ /** Number of flexboot_nodnic Ethernet send work queue entries */ #define FLEXBOOT_NODNIC_ETH_NUM_SEND_WQES 64 /** Number of flexboot_nodnic Ethernet receive work queue entries */ #define FLEXBOOT_NODNIC_ETH_NUM_RECV_WQES 64 /** flexboot nodnic Ethernet queue pair operations */ static struct ib_queue_pair_operations flexboot_nodnic_eth_qp_op = { .alloc_iob = alloc_iob, }; /** * Transmit packet via flexboot_nodnic Ethernet device * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ static int flexboot_nodnic_eth_transmit ( struct net_device *netdev, struct io_buffer *iobuf) { struct flexboot_nodnic_port *port = netdev->priv; struct ib_device *ibdev = port->ibdev; struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); int rc; rc = ib_post_send ( ibdev, port->eth_qp, NULL, iobuf); /* Transmit packet */ if ( rc != 0) { DBGC ( flexboot_nodnic, "NODNIC %p port %d could not transmit: %s\n", flexboot_nodnic, ibdev->port, strerror ( rc ) ); return rc; } return 0; } /** * Handle flexboot_nodnic Ethernet device send completion * * @v ibdev Infiniband device * @v qp Queue pair * @v iobuf I/O buffer * @v rc Completion status code */ static void flexboot_nodnic_eth_complete_send ( struct ib_device *ibdev __unused, struct ib_queue_pair *qp, struct io_buffer *iobuf, int rc) { struct net_device *netdev = ib_qp_get_ownerdata ( qp ); netdev_tx_complete_err ( netdev, iobuf, rc ); } /** * Handle flexboot_nodnic Ethernet device receive completion * * @v ibdev Infiniband device * @v qp Queue pair * @v av Address vector, or NULL * @v iobuf I/O buffer * @v rc Completion status code */ static void flexboot_nodnic_eth_complete_recv ( struct ib_device *ibdev __unused, struct ib_queue_pair *qp, struct ib_address_vector *dest __unused, struct ib_address_vector *source, struct io_buffer *iobuf, int rc) { struct net_device *netdev = ib_qp_get_ownerdata ( qp ); if ( rc != 0 ) { DBG ( "Received packet with error\n" ); netdev_rx_err ( netdev, iobuf, rc ); return; } if ( source == NULL ) { DBG ( "Received packet without address vector\n" ); netdev_rx_err ( netdev, iobuf, -ENOTTY ); return; } netdev_rx ( netdev, iobuf ); } /** flexboot_nodnic Ethernet device completion operations */ static struct ib_completion_queue_operations flexboot_nodnic_eth_cq_op = { .complete_send = flexboot_nodnic_eth_complete_send, .complete_recv = flexboot_nodnic_eth_complete_recv, }; /** * Poll flexboot_nodnic Ethernet device * * @v netdev Network device */ static void flexboot_nodnic_eth_poll ( struct net_device *netdev) { struct flexboot_nodnic_port *port = netdev->priv; struct ib_device *ibdev = port->ibdev; ib_poll_eq ( ibdev ); } /** * Open flexboot_nodnic Ethernet device * * @v netdev Network device * @ret rc Return status code */ static int flexboot_nodnic_eth_open ( struct net_device *netdev ) { struct flexboot_nodnic_port *port = netdev->priv; struct ib_device *ibdev = port->ibdev; struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); mlx_status status = MLX_SUCCESS; struct ib_completion_queue *dummy_cq = NULL; struct flexboot_nodnic_queue_pair *flexboot_nodnic_qp = NULL; mlx_uint64 cq_size = 0; mlx_uint32 qpn = 0; nodnic_port_state state = nodnic_port_state_down; int rc; if ( port->port_priv.port_state & NODNIC_PORT_OPENED ) { DBGC ( flexboot_nodnic, "%s: port %d is already opened\n", __FUNCTION__, port->ibdev->port ); return 0; } port->port_priv.port_state |= NODNIC_PORT_OPENED; dummy_cq = zalloc ( sizeof ( struct ib_completion_queue ) ); if ( dummy_cq == NULL ) { DBGC ( flexboot_nodnic, "%s: Failed to allocate dummy CQ\n", __FUNCTION__ ); status = MLX_OUT_OF_RESOURCES; goto err_create_dummy_cq; } INIT_LIST_HEAD ( &dummy_cq->work_queues ); if ( ( rc = ib_create_qp ( ibdev, IB_QPT_ETH, FLEXBOOT_NODNIC_ETH_NUM_SEND_WQES, dummy_cq, FLEXBOOT_NODNIC_ETH_NUM_RECV_WQES, dummy_cq, &flexboot_nodnic_eth_qp_op, netdev->name, &port->eth_qp ) ) != 0 ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not create queue pair\n", flexboot_nodnic, ibdev->port ); status = MLX_OUT_OF_RESOURCES; goto err_create_qp; } ib_qp_set_ownerdata ( port->eth_qp, netdev ); status = nodnic_port_get_cq_size(&port->port_priv, &cq_size); MLX_FATAL_CHECK_STATUS(status, get_cq_size_err, "nodnic_port_get_cq_size failed"); if ( ( rc = ib_create_cq ( ibdev, cq_size, &flexboot_nodnic_eth_cq_op, &port->eth_cq ) ) != 0 ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not create completion queue\n", flexboot_nodnic, ibdev->port ); status = MLX_OUT_OF_RESOURCES; goto err_create_cq; } port->eth_qp->send.cq = port->eth_cq; list_del(&port->eth_qp->send.list); list_add ( &port->eth_qp->send.list, &port->eth_cq->work_queues ); port->eth_qp->recv.cq = port->eth_cq; port->cmdsn = 0; list_del(&port->eth_qp->recv.list); list_add ( &port->eth_qp->recv.list, &port->eth_cq->work_queues ); status = nodnic_port_allocate_eq(&port->port_priv, flexboot_nodnic->device_priv.device_cap.log_working_buffer_size); MLX_FATAL_CHECK_STATUS(status, eq_alloc_err, "nodnic_port_allocate_eq failed"); status = nodnic_port_init(&port->port_priv); MLX_FATAL_CHECK_STATUS(status, init_err, "nodnic_port_init failed"); /* update qp - qpn */ flexboot_nodnic_qp = ib_qp_get_drvdata ( port->eth_qp ); status = nodnic_port_get_qpn(&port->port_priv, &flexboot_nodnic_qp->nodnic_queue_pair->send.nodnic_ring, &qpn); MLX_FATAL_CHECK_STATUS(status, qpn_err, "nodnic_port_get_qpn failed"); port->eth_qp->qpn = qpn; /* Fill receive rings */ ib_refill_recv ( ibdev, port->eth_qp ); status = nodnic_port_enable_dma(&port->port_priv); MLX_FATAL_CHECK_STATUS(status, dma_err, "nodnic_port_enable_dma failed"); if (flexboot_nodnic->device_priv.device_cap.support_promisc_filter) { status = nodnic_port_set_promisc(&port->port_priv, TRUE); MLX_FATAL_CHECK_STATUS(status, promisc_err, "nodnic_port_set_promisc failed"); } status = nodnic_port_get_state(&port->port_priv, &state); MLX_FATAL_CHECK_STATUS(status, state_err, "nodnic_port_get_state failed"); port->type->state_change ( flexboot_nodnic, port, state == nodnic_port_state_active ); DBGC ( flexboot_nodnic, "%s: port %d opened (link is %s)\n", __FUNCTION__, port->ibdev->port, ( ( state == nodnic_port_state_active ) ? "Up" : "Down" ) ); free(dummy_cq); return 0; state_err: promisc_err: dma_err: qpn_err: nodnic_port_close(&port->port_priv); init_err: nodnic_port_free_eq(&port->port_priv); eq_alloc_err: err_create_cq: get_cq_size_err: ib_destroy_qp(ibdev, port->eth_qp ); err_create_qp: free(dummy_cq); err_create_dummy_cq: port->port_priv.port_state &= ~NODNIC_PORT_OPENED; return status; } /** * Close flexboot_nodnic Ethernet device * * @v netdev Network device */ static void flexboot_nodnic_eth_close ( struct net_device *netdev) { struct flexboot_nodnic_port *port = netdev->priv; struct ib_device *ibdev = port->ibdev; struct flexboot_nodnic *flexboot_nodnic = ib_get_drvdata ( ibdev ); mlx_status status = MLX_SUCCESS; if ( ! ( port->port_priv.port_state & NODNIC_PORT_OPENED ) ) { DBGC ( flexboot_nodnic, "%s: port %d is already closed\n", __FUNCTION__, port->ibdev->port ); return; } if (flexboot_nodnic->device_priv.device_cap.support_promisc_filter) { if ( ( status = nodnic_port_set_promisc( &port->port_priv, FALSE ) ) ) { DBGC ( flexboot_nodnic, "nodnic_port_set_promisc failed (status = %d)\n", status ); } } flexboot_nodnic_port_disable_dma ( port ); port->port_priv.port_state &= ~NODNIC_PORT_OPENED; port->type->state_change ( flexboot_nodnic, port, FALSE ); /* Close port */ status = nodnic_port_close(&port->port_priv); if ( status != MLX_SUCCESS ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not close port: %s\n", flexboot_nodnic, ibdev->port, strerror ( status ) ); /* Nothing we can do about this */ } ib_destroy_qp ( ibdev, port->eth_qp ); port->eth_qp = NULL; ib_destroy_cq ( ibdev, port->eth_cq ); port->eth_cq = NULL; nodnic_port_free_eq(&port->port_priv); DBGC ( flexboot_nodnic, "%s: port %d closed\n", __FUNCTION__, port->ibdev->port ); } void flexboot_nodnic_eth_irq ( struct net_device *netdev, int enable ) { struct flexboot_nodnic_port *port = netdev->priv; if ( enable ) { if ( ( port->port_priv.port_state & NODNIC_PORT_OPENED ) && ! ( port->port_priv.port_state & NODNIC_PORT_DISABLING_DMA ) ) { flexboot_nodnic_arm_cq ( port ); } else { /* do nothing */ } } else { nodnic_device_clear_int( port->port_priv.device ); } } /** flexboot_nodnic Ethernet network device operations */ static struct net_device_operations flexboot_nodnic_eth_operations = { .open = flexboot_nodnic_eth_open, .close = flexboot_nodnic_eth_close, .transmit = flexboot_nodnic_eth_transmit, .poll = flexboot_nodnic_eth_poll, }; /** * Register flexboot_nodnic Ethernet device */ static int flexboot_nodnic_register_netdev ( struct flexboot_nodnic *flexboot_nodnic, struct flexboot_nodnic_port *port) { mlx_status status = MLX_SUCCESS; struct net_device *netdev; struct ib_device *ibdev = port->ibdev; union { uint8_t bytes[8]; uint32_t dwords[2]; } mac; /* Allocate network devices */ netdev = alloc_etherdev ( 0 ); if ( netdev == NULL ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not allocate net device\n", flexboot_nodnic, ibdev->port ); status = MLX_OUT_OF_RESOURCES; goto alloc_err; } port->netdev = netdev; netdev_init ( netdev, &flexboot_nodnic_eth_operations ); netdev->dev = ibdev->dev; netdev->priv = port; status = nodnic_port_query(&port->port_priv, nodnic_port_option_mac_high, &mac.dwords[0]); MLX_FATAL_CHECK_STATUS(status, mac_err, "failed to query mac high"); status = nodnic_port_query(&port->port_priv, nodnic_port_option_mac_low, &mac.dwords[1]); MLX_FATAL_CHECK_STATUS(status, mac_err, "failed to query mac low"); mac.dwords[0] = htonl(mac.dwords[0]); mac.dwords[1] = htonl(mac.dwords[1]); memcpy ( netdev->hw_addr, &mac.bytes[2], ETH_ALEN); /* Register network device */ status = register_netdev ( netdev ); if ( status != MLX_SUCCESS ) { DBGC ( flexboot_nodnic, "flexboot_nodnic %p port %d could not register network device: %s\n", flexboot_nodnic, ibdev->port, strerror ( status ) ); goto reg_err; } return status; reg_err: mac_err: netdev_put ( netdev ); alloc_err: return status; } /** * Handle flexboot_nodnic Ethernet device port state change */ static void flexboot_nodnic_state_change_netdev ( struct flexboot_nodnic *flexboot_nodnic __unused, struct flexboot_nodnic_port *port, int link_up ) { struct net_device *netdev = port->netdev; if ( link_up ) netdev_link_up ( netdev ); else netdev_link_down ( netdev ); } /** * Unregister flexboot_nodnic Ethernet device */ static void flexboot_nodnic_unregister_netdev ( struct flexboot_nodnic *flexboot_nodnic __unused, struct flexboot_nodnic_port *port ) { struct net_device *netdev = port->netdev; unregister_netdev ( netdev ); netdev_nullify ( netdev ); netdev_put ( netdev ); } /** flexboot_nodnic Ethernet port type */ static struct flexboot_nodnic_port_type flexboot_nodnic_port_type_eth = { .register_dev = flexboot_nodnic_register_netdev, .state_change = flexboot_nodnic_state_change_netdev, .unregister_dev = flexboot_nodnic_unregister_netdev, }; /*************************************************************************** * * PCI interface helper functions * *************************************************************************** */ static mlx_status flexboot_nodnic_allocate_infiniband_devices( struct flexboot_nodnic *flexboot_nodnic_priv ) { mlx_status status = MLX_SUCCESS; nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; struct pci_device *pci = flexboot_nodnic_priv->pci; struct ib_device *ibdev = NULL; unsigned int i = 0; /* Allocate Infiniband devices */ for (; i < device_priv->device_cap.num_ports; i++) { if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) continue; ibdev = alloc_ibdev(0); if (ibdev == NULL) { status = MLX_OUT_OF_RESOURCES; goto err_alloc_ibdev; } flexboot_nodnic_priv->port[i].ibdev = ibdev; ibdev->op = &flexboot_nodnic_ib_operations; ibdev->dev = &pci->dev; ibdev->port = ( FLEXBOOT_NODNIC_PORT_BASE + i); ib_set_drvdata(ibdev, flexboot_nodnic_priv); } return status; err_alloc_ibdev: for ( i-- ; ( signed int ) i >= 0 ; i-- ) ibdev_put ( flexboot_nodnic_priv->port[i].ibdev ); return status; } static mlx_status flexboot_nodnic_thin_init_ports( struct flexboot_nodnic *flexboot_nodnic_priv ) { mlx_status status = MLX_SUCCESS; nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; nodnic_port_priv *port_priv = NULL; unsigned int i = 0; for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) { if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) continue; port_priv = &flexboot_nodnic_priv->port[i].port_priv; status = nodnic_port_thin_init( device_priv, port_priv, i ); MLX_FATAL_CHECK_STATUS(status, thin_init_err, "flexboot_nodnic_thin_init_ports failed"); } thin_init_err: return status; } static mlx_status flexboot_nodnic_set_ports_type ( struct flexboot_nodnic *flexboot_nodnic_priv ) { mlx_status status = MLX_SUCCESS; nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; nodnic_port_priv *port_priv = NULL; nodnic_port_type type = NODNIC_PORT_TYPE_UNKNOWN; unsigned int i = 0; for ( i = 0 ; i < device_priv->device_cap.num_ports ; i++ ) { if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) continue; port_priv = &flexboot_nodnic_priv->port[i].port_priv; status = nodnic_port_get_type(port_priv, &type); MLX_FATAL_CHECK_STATUS(status, type_err, "nodnic_port_get_type failed"); switch ( type ) { case NODNIC_PORT_TYPE_ETH: DBGC ( flexboot_nodnic_priv, "Port %d type is Ethernet\n", i ); flexboot_nodnic_priv->port[i].type = &flexboot_nodnic_port_type_eth; break; case NODNIC_PORT_TYPE_IB: DBGC ( flexboot_nodnic_priv, "Port %d type is Infiniband\n", i ); status = MLX_UNSUPPORTED; goto type_err; default: DBGC ( flexboot_nodnic_priv, "Port %d type is unknown\n", i ); status = MLX_UNSUPPORTED; goto type_err; } } type_err: return status; } static mlx_status flexboot_nodnic_ports_register_dev( struct flexboot_nodnic *flexboot_nodnic_priv ) { mlx_status status = MLX_SUCCESS; nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; struct flexboot_nodnic_port *port = NULL; unsigned int i = 0; for (; i < device_priv->device_cap.num_ports; i++) { if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) continue; port = &flexboot_nodnic_priv->port[i]; status = port->type->register_dev ( flexboot_nodnic_priv, port ); MLX_FATAL_CHECK_STATUS(status, reg_err, "port register_dev failed"); } reg_err: return status; } static mlx_status flexboot_nodnic_ports_unregister_dev ( struct flexboot_nodnic *flexboot_nodnic_priv ) { struct flexboot_nodnic_port *port; nodnic_device_priv *device_priv = &flexboot_nodnic_priv->device_priv; int i = (device_priv->device_cap.num_ports - 1); for (; i >= 0; i--) { if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) continue; port = &flexboot_nodnic_priv->port[i]; port->type->unregister_dev(flexboot_nodnic_priv, port); ibdev_put(flexboot_nodnic_priv->port[i].ibdev); } return MLX_SUCCESS; } /*************************************************************************** * * flexboot nodnic interface * *************************************************************************** */ __unused static void flexboot_nodnic_enable_dma ( struct flexboot_nodnic *nodnic ) { nodnic_port_priv *port_priv; mlx_status status; int i; for ( i = 0; i < nodnic->device_priv.device_cap.num_ports; i++ ) { if ( ! ( nodnic->port_mask & ( i + 1 ) ) ) continue; port_priv = & ( nodnic->port[i].port_priv ); if ( ! ( port_priv->port_state & NODNIC_PORT_OPENED ) ) continue; if ( ( status = nodnic_port_enable_dma ( port_priv ) ) ) { MLX_DEBUG_WARN ( nodnic, "Failed to enable DMA %d\n", status ); } } } __unused static void flexboot_nodnic_disable_dma ( struct flexboot_nodnic *nodnic ) { int i; for ( i = 0; i < nodnic->device_priv.device_cap.num_ports; i++ ) { if ( ! ( nodnic->port_mask & ( i + 1 ) ) ) continue; flexboot_nodnic_port_disable_dma ( & ( nodnic->port[i] ) ); } } int flexboot_nodnic_is_supported ( struct pci_device *pci ) { mlx_utils utils; mlx_pci_gw_buffer buffer; mlx_status status; int is_supported = 0; DBG ( "%s: start\n", __FUNCTION__ ); memset ( &utils, 0, sizeof ( utils ) ); status = mlx_utils_init ( &utils, pci ); MLX_CHECK_STATUS ( pci, status, utils_init_err, "mlx_utils_init failed" ); status = mlx_pci_gw_init ( &utils ); MLX_CHECK_STATUS ( pci, status, pci_gw_init_err, "mlx_pci_gw_init failed" ); status = mlx_pci_gw_read ( &utils, PCI_GW_SPACE_NODNIC, NODNIC_NIC_INTERFACE_SUPPORTED_OFFSET, &buffer ); if ( status == MLX_SUCCESS ) { buffer >>= NODNIC_NIC_INTERFACE_SUPPORTED_BIT; is_supported = ( buffer & 0x1 ); } mlx_pci_gw_teardown( &utils ); pci_gw_init_err: mlx_utils_teardown(&utils); utils_init_err: DBG ( "%s: NODNIC is %s supported (status = %d)\n", __FUNCTION__, ( is_supported ? "": "not" ), status ); return is_supported; } void flexboot_nodnic_copy_mac ( uint8_t mac_addr[], uint32_t low_byte, uint16_t high_byte ) { union mac_addr { struct { uint32_t low_byte; uint16_t high_byte; }; uint8_t mac_addr[ETH_ALEN]; } mac_addr_aux; mac_addr_aux.high_byte = high_byte; mac_addr_aux.low_byte = low_byte; mac_addr[0] = mac_addr_aux.mac_addr[5]; mac_addr[1] = mac_addr_aux.mac_addr[4]; mac_addr[2] = mac_addr_aux.mac_addr[3]; mac_addr[3] = mac_addr_aux.mac_addr[2]; mac_addr[4] = mac_addr_aux.mac_addr[1]; mac_addr[5] = mac_addr_aux.mac_addr[0]; } static mlx_status flexboot_nodnic_get_factory_mac ( struct flexboot_nodnic *flexboot_nodnic_priv, uint8_t port __unused ) { struct mlx_vmac_query_virt_mac virt_mac; mlx_status status; memset ( & virt_mac, 0, sizeof ( virt_mac ) ); status = mlx_vmac_query_virt_mac ( flexboot_nodnic_priv->device_priv.utils, &virt_mac ); if ( ! status ) { DBGC ( flexboot_nodnic_priv, "NODNIC %p Failed to set the virtual MAC\n" ,flexboot_nodnic_priv ); } return status; } /** * Set port masking * * @v flexboot_nodnic nodnic device * @ret rc Return status code */ static int flexboot_nodnic_set_port_masking ( struct flexboot_nodnic *flexboot_nodnic ) { unsigned int i; nodnic_device_priv *device_priv = &flexboot_nodnic->device_priv; flexboot_nodnic->port_mask = 0; for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) { flexboot_nodnic->port_mask |= (i + 1); } if ( ! flexboot_nodnic->port_mask ) { /* No port was enabled */ DBGC ( flexboot_nodnic, "NODNIC %p No port was enabled for " "booting\n", flexboot_nodnic ); return -ENETUNREACH; } return 0; } int init_mlx_utils ( mlx_utils **utils, struct pci_device *pci ) { int rc = 0; *utils = ( mlx_utils * ) zalloc ( sizeof ( mlx_utils ) ); if ( *utils == NULL ) { DBGC ( utils, "%s: Failed to allocate utils\n", __FUNCTION__ ); rc = -1; goto err_utils_alloc; } if ( mlx_utils_init ( *utils, pci ) ) { DBGC ( utils, "%s: mlx_utils_init failed\n", __FUNCTION__ ); rc = -1; goto err_utils_init; } if ( mlx_pci_gw_init ( *utils ) ){ DBGC ( utils, "%s: mlx_pci_gw_init failed\n", __FUNCTION__ ); rc = -1; goto err_cmd_init; } return 0; mlx_pci_gw_teardown ( *utils ); err_cmd_init: mlx_utils_teardown ( *utils ); err_utils_init: free ( *utils ); err_utils_alloc: *utils = NULL; return rc; } void free_mlx_utils ( mlx_utils **utils ) { mlx_pci_gw_teardown ( *utils ); mlx_utils_teardown ( *utils ); free ( *utils ); *utils = NULL; } /** * Initialise Nodnic PCI parameters * * @v hermon Nodnic device */ static int flexboot_nodnic_alloc_uar ( struct flexboot_nodnic *flexboot_nodnic ) { mlx_status status = MLX_SUCCESS; struct pci_device *pci = flexboot_nodnic->pci; nodnic_uar *uar = &flexboot_nodnic->port[0].port_priv.device->uar; if ( ! flexboot_nodnic->device_priv.device_cap.support_uar_tx_db ) { DBGC ( flexboot_nodnic, "%s: tx db using uar is not supported \n", __FUNCTION__ ); return -ENOTSUP; } /* read uar offset then allocate */ if ( ( status = nodnic_port_set_send_uar_offset ( &flexboot_nodnic->port[0].port_priv ) ) ) { DBGC ( flexboot_nodnic, "%s: nodnic_port_set_send_uar_offset failed," "status = %d\n", __FUNCTION__, status ); return -EINVAL; } uar->phys = ( pci_bar_start ( pci, FLEXBOOT_NODNIC_HCA_BAR ) + (mlx_uint32)uar->offset ); uar->virt = ( void * )( ioremap ( uar->phys, FLEXBOOT_NODNIC_PAGE_SIZE ) ); return status; } static int flexboot_nodnic_dealloc_uar ( struct flexboot_nodnic *flexboot_nodnic ) { nodnic_uar *uar = &flexboot_nodnic->port[0].port_priv.device->uar; if ( uar->virt ) { iounmap( uar->virt ); uar->virt = NULL; } return MLX_SUCCESS; } int flexboot_nodnic_probe ( struct pci_device *pci, struct flexboot_nodnic_callbacks *callbacks, void *drv_priv __unused ) { mlx_status status = MLX_SUCCESS; struct flexboot_nodnic *flexboot_nodnic_priv = NULL; nodnic_device_priv *device_priv = NULL; int i = 0; if ( ( pci == NULL ) || ( callbacks == NULL ) ) { DBGC ( flexboot_nodnic_priv, "%s: Bad Parameter\n", __FUNCTION__ ); return -EINVAL; } flexboot_nodnic_priv = zalloc( sizeof ( *flexboot_nodnic_priv ) ); if ( flexboot_nodnic_priv == NULL ) { DBGC ( flexboot_nodnic_priv, "%s: Failed to allocate priv data\n", __FUNCTION__ ); status = MLX_OUT_OF_RESOURCES; goto device_err_alloc; } /* Register settings * Note that pci->priv will be the device private data */ flexboot_nodnic_priv->pci = pci; flexboot_nodnic_priv->callbacks = callbacks; pci_set_drvdata ( pci, flexboot_nodnic_priv ); device_priv = &flexboot_nodnic_priv->device_priv; /* init mlx utils */ status = init_mlx_utils ( & device_priv->utils, pci ); MLX_FATAL_CHECK_STATUS(status, err_utils_init, "init_mlx_utils failed"); /* init device */ status = nodnic_device_init( device_priv ); MLX_FATAL_CHECK_STATUS(status, device_init_err, "nodnic_device_init failed"); status = nodnic_device_get_cap( device_priv ); MLX_FATAL_CHECK_STATUS(status, get_cap_err, "nodnic_device_get_cap failed"); if ( mlx_set_admin_mtu ( device_priv->utils, 1, EN_DEFAULT_ADMIN_MTU ) ) { MLX_DEBUG_ERROR( device_priv->utils, "Failed to set admin mtu\n" ); } status = flexboot_nodnic_set_port_masking ( flexboot_nodnic_priv ); MLX_FATAL_CHECK_STATUS(status, err_set_masking, "flexboot_nodnic_set_port_masking failed"); status = flexboot_nodnic_allocate_infiniband_devices( flexboot_nodnic_priv ); MLX_FATAL_CHECK_STATUS(status, err_alloc_ibdev, "flexboot_nodnic_allocate_infiniband_devices failed"); /* port init */ status = flexboot_nodnic_thin_init_ports( flexboot_nodnic_priv ); MLX_FATAL_CHECK_STATUS(status, err_thin_init_ports, "flexboot_nodnic_thin_init_ports failed"); if ( ( status = flexboot_nodnic_alloc_uar ( flexboot_nodnic_priv ) ) ) { DBGC(flexboot_nodnic_priv, "%s: flexboot_nodnic_alloc_uar failed" " ( status = %d )\n",__FUNCTION__, status ); } /* device reg */ status = flexboot_nodnic_set_ports_type( flexboot_nodnic_priv ); MLX_CHECK_STATUS( flexboot_nodnic_priv, status, err_set_ports_types, "flexboot_nodnic_set_ports_type failed"); status = flexboot_nodnic_ports_register_dev( flexboot_nodnic_priv ); MLX_FATAL_CHECK_STATUS(status, reg_err, "flexboot_nodnic_ports_register_dev failed"); for ( i = 0; i < device_priv->device_cap.num_ports; i++ ) { if ( ! ( flexboot_nodnic_priv->port_mask & ( i + 1 ) ) ) continue; flexboot_nodnic_get_factory_mac ( flexboot_nodnic_priv, i ); } /* Update ETH operations with IRQ function if supported */ DBGC ( flexboot_nodnic_priv, "%s: %s IRQ function\n", __FUNCTION__, ( callbacks->irq ? "Valid" : "No" ) ); flexboot_nodnic_eth_operations.irq = callbacks->irq; return 0; flexboot_nodnic_ports_unregister_dev ( flexboot_nodnic_priv ); reg_err: err_set_ports_types: flexboot_nodnic_dealloc_uar ( flexboot_nodnic_priv ); err_thin_init_ports: err_alloc_ibdev: err_set_masking: get_cap_err: nodnic_device_teardown ( device_priv ); device_init_err: free_mlx_utils ( & device_priv->utils ); err_utils_init: free ( flexboot_nodnic_priv ); device_err_alloc: return status; } void flexboot_nodnic_remove ( struct pci_device *pci ) { struct flexboot_nodnic *flexboot_nodnic_priv = pci_get_drvdata ( pci ); nodnic_device_priv *device_priv = & ( flexboot_nodnic_priv->device_priv ); flexboot_nodnic_dealloc_uar ( flexboot_nodnic_priv ); flexboot_nodnic_ports_unregister_dev ( flexboot_nodnic_priv ); nodnic_device_teardown( device_priv ); free_mlx_utils ( & device_priv->utils ); free( flexboot_nodnic_priv ); }