aboutsummaryrefslogtreecommitdiffstats
path: root/gpxe/src/drivers/infiniband/hermon.c
diff options
context:
space:
mode:
Diffstat (limited to 'gpxe/src/drivers/infiniband/hermon.c')
-rw-r--r--gpxe/src/drivers/infiniband/hermon.c702
1 files changed, 565 insertions, 137 deletions
diff --git a/gpxe/src/drivers/infiniband/hermon.c b/gpxe/src/drivers/infiniband/hermon.c
index 40add28a..b9c97f94 100644
--- a/gpxe/src/drivers/infiniband/hermon.c
+++ b/gpxe/src/drivers/infiniband/hermon.c
@@ -17,6 +17,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+FILE_LICENCE ( GPL2_OR_LATER );
+
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
@@ -27,6 +29,7 @@
#include <byteswap.h>
#include <gpxe/io.h>
#include <gpxe/pci.h>
+#include <gpxe/pcibackup.h>
#include <gpxe/malloc.h>
#include <gpxe/umalloc.h>
#include <gpxe/iobuf.h>
@@ -199,9 +202,10 @@ static int hermon_cmd ( struct hermon *hermon, unsigned long command,
opcode_modifier, op_mod,
go, 1,
t, hermon->toggle );
- DBGC ( hermon, "Hermon %p issuing command:\n", hermon );
- DBGC_HDA ( hermon, virt_to_phys ( hermon->config + HERMON_HCR_BASE ),
- &hcr, sizeof ( hcr ) );
+ DBGC ( hermon, "Hermon %p issuing command %04x\n",
+ hermon, opcode );
+ DBGC2_HDA ( hermon, virt_to_phys ( hermon->config + HERMON_HCR_BASE ),
+ &hcr, sizeof ( hcr ) );
if ( in_len && ( command & HERMON_HCR_IN_MBOX ) ) {
DBGC2 ( hermon, "Input mailbox:\n" );
DBGC2_HDA ( hermon, virt_to_phys ( in_buffer ), in_buffer,
@@ -416,6 +420,23 @@ hermon_cmd_2rst_qp ( struct hermon *hermon, unsigned long qpn ) {
}
static inline int
+hermon_cmd_query_qp ( struct hermon *hermon, unsigned long qpn,
+ struct hermonprm_qp_ee_state_transitions *ctx ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_OUT_CMD ( HERMON_HCR_QUERY_QP,
+ 1, sizeof ( *ctx ) ),
+ 0, NULL, qpn, ctx );
+}
+
+static inline int
+hermon_cmd_conf_special_qp ( struct hermon *hermon, unsigned int internal_qps,
+ unsigned long base_qpn ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_VOID_CMD ( HERMON_HCR_CONF_SPECIAL_QP ),
+ internal_qps, NULL, base_qpn, NULL );
+}
+
+static inline int
hermon_cmd_mad_ifc ( struct hermon *hermon, unsigned int port,
union hermonprm_mad *mad ) {
return hermon_cmd ( hermon,
@@ -521,6 +542,16 @@ hermon_cmd_map_fa ( struct hermon *hermon,
0, map, 1, NULL );
}
+static inline int
+hermon_cmd_sense_port ( struct hermon *hermon, unsigned int port,
+ struct hermonprm_sense_port *port_type ) {
+ return hermon_cmd ( hermon,
+ HERMON_HCR_OUT_CMD ( HERMON_HCR_SENSE_PORT,
+ 1, sizeof ( *port_type ) ),
+ 0, NULL, port, port_type );
+}
+
+
/***************************************************************************
*
* Memory translation table operations
@@ -795,6 +826,124 @@ static void hermon_destroy_cq ( struct ib_device *ibdev,
*/
/**
+ * Assign queue pair number
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @ret rc Return status code
+ */
+static int hermon_alloc_qpn ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ unsigned int port_offset;
+ int qpn_offset;
+
+ /* Calculate queue pair number */
+ port_offset = ( ibdev->port - HERMON_PORT_BASE );
+
+ switch ( qp->type ) {
+ case IB_QPT_SMI:
+ qp->qpn = ( hermon->special_qpn_base + port_offset );
+ return 0;
+ case IB_QPT_GSI:
+ qp->qpn = ( hermon->special_qpn_base + 2 + port_offset );
+ return 0;
+ case IB_QPT_UD:
+ case IB_QPT_RC:
+ /* Find a free queue pair number */
+ qpn_offset = hermon_bitmask_alloc ( hermon->qp_inuse,
+ HERMON_MAX_QPS, 1 );
+ if ( qpn_offset < 0 ) {
+ DBGC ( hermon, "Hermon %p out of queue pairs\n",
+ hermon );
+ return qpn_offset;
+ }
+ qp->qpn = ( ( random() & HERMON_QPN_RANDOM_MASK ) |
+ ( hermon->qpn_base + qpn_offset ) );
+ return 0;
+ default:
+ DBGC ( hermon, "Hermon %p unsupported QP type %d\n",
+ hermon, qp->type );
+ return -ENOTSUP;
+ }
+}
+
+/**
+ * Free queue pair number
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ */
+static void hermon_free_qpn ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ int qpn_offset;
+
+ qpn_offset = ( ( qp->qpn & ~HERMON_QPN_RANDOM_MASK )
+ - hermon->qpn_base );
+ if ( qpn_offset >= 0 )
+ hermon_bitmask_free ( hermon->qp_inuse, qpn_offset, 1 );
+}
+
+/**
+ * Calculate transmission rate
+ *
+ * @v av Address vector
+ * @ret hermon_rate Hermon rate
+ */
+static unsigned int hermon_rate ( struct ib_address_vector *av ) {
+ return ( ( ( av->rate >= IB_RATE_2_5 ) && ( av->rate <= IB_RATE_120 ) )
+ ? ( av->rate + 5 ) : 0 );
+}
+
+/**
+ * Calculate schedule queue
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @ret sched_queue Schedule queue
+ */
+static unsigned int hermon_sched_queue ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp ) {
+ return ( ( ( qp->type == IB_QPT_SMI ) ?
+ HERMON_SCHED_QP0 : HERMON_SCHED_DEFAULT ) |
+ ( ( ibdev->port - 1 ) << 6 ) );
+}
+
+/** Queue pair transport service type map */
+static uint8_t hermon_qp_st[] = {
+ [IB_QPT_SMI] = HERMON_ST_MLX,
+ [IB_QPT_GSI] = HERMON_ST_MLX,
+ [IB_QPT_UD] = HERMON_ST_UD,
+ [IB_QPT_RC] = HERMON_ST_RC,
+};
+
+/**
+ * Dump queue pair context (for debugging only)
+ *
+ * @v hermon Hermon device
+ * @v qp Queue pair
+ * @ret rc Return status code
+ */
+static inline int hermon_dump_qpctx ( struct hermon *hermon,
+ struct ib_queue_pair *qp ) {
+ struct hermonprm_qp_ee_state_transitions qpctx;
+ int rc;
+
+ memset ( &qpctx, 0, sizeof ( qpctx ) );
+ if ( ( rc = hermon_cmd_query_qp ( hermon, qp->qpn, &qpctx ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p QUERY_QP failed: %s\n",
+ hermon, strerror ( rc ) );
+ return rc;
+ }
+ DBGC ( hermon, "Hermon %p QPN %lx context:\n", hermon, qp->qpn );
+ DBGC_HDA ( hermon, 0, &qpctx.u.dwords[2],
+ ( sizeof ( qpctx ) - 8 ) );
+
+ return 0;
+}
+
+/**
* Create queue pair
*
* @v ibdev Infiniband device
@@ -806,19 +955,11 @@ static int hermon_create_qp ( struct ib_device *ibdev,
struct hermon *hermon = ib_get_drvdata ( ibdev );
struct hermon_queue_pair *hermon_qp;
struct hermonprm_qp_ee_state_transitions qpctx;
- int qpn_offset;
int rc;
- /* Find a free queue pair number */
- qpn_offset = hermon_bitmask_alloc ( hermon->qp_inuse,
- HERMON_MAX_QPS, 1 );
- if ( qpn_offset < 0 ) {
- DBGC ( hermon, "Hermon %p out of queue pairs\n", hermon );
- rc = qpn_offset;
- goto err_qpn_offset;
- }
- qp->qpn = ( HERMON_QPN_BASE + hermon->cap.reserved_qps +
- qpn_offset );
+ /* Calculate queue pair number */
+ if ( ( rc = hermon_alloc_qpn ( ibdev, qp ) ) != 0 )
+ goto err_alloc_qpn;
/* Allocate control structures */
hermon_qp = zalloc ( sizeof ( *hermon_qp ) );
@@ -864,8 +1005,8 @@ static int hermon_create_qp ( struct ib_device *ibdev,
/* Transition queue to INIT state */
memset ( &qpctx, 0, sizeof ( qpctx ) );
MLX_FILL_2 ( &qpctx, 2,
- qpc_eec_data.pm_state, 0x03 /* Always 0x03 for UD */,
- qpc_eec_data.st, HERMON_ST_UD );
+ qpc_eec_data.pm_state, HERMON_PM_STATE_MIGRATED,
+ qpc_eec_data.st, hermon_qp_st[qp->type] );
MLX_FILL_1 ( &qpctx, 3, qpc_eec_data.pd, HERMON_GLOBAL_PD );
MLX_FILL_4 ( &qpctx, 4,
qpc_eec_data.log_rq_size, fls ( qp->recv.num_wqes - 1 ),
@@ -878,12 +1019,15 @@ static int hermon_create_qp ( struct ib_device *ibdev,
MLX_FILL_1 ( &qpctx, 5,
qpc_eec_data.usr_page, HERMON_UAR_NON_EQ_PAGE );
MLX_FILL_1 ( &qpctx, 33, qpc_eec_data.cqn_snd, qp->send.cq->cqn );
- MLX_FILL_1 ( &qpctx, 38, qpc_eec_data.page_offset,
+ MLX_FILL_4 ( &qpctx, 38,
+ qpc_eec_data.rre, 1,
+ qpc_eec_data.rwe, 1,
+ qpc_eec_data.rae, 1,
+ qpc_eec_data.page_offset,
( hermon_qp->mtt.page_offset >> 6 ) );
MLX_FILL_1 ( &qpctx, 41, qpc_eec_data.cqn_rcv, qp->recv.cq->cqn );
MLX_FILL_1 ( &qpctx, 43, qpc_eec_data.db_record_addr_l,
( virt_to_phys ( &hermon_qp->recv.doorbell ) >> 2 ) );
- MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey );
MLX_FILL_1 ( &qpctx, 53, qpc_eec_data.mtt_base_addr_l,
( hermon_qp->mtt.mtt_base_addr >> 3 ) );
if ( ( rc = hermon_cmd_rst2init_qp ( hermon, qp->qpn,
@@ -892,28 +1036,7 @@ static int hermon_create_qp ( struct ib_device *ibdev,
hermon, strerror ( rc ) );
goto err_rst2init_qp;
}
-
- /* Transition queue to RTR state */
- memset ( &qpctx, 0, sizeof ( qpctx ) );
- MLX_FILL_2 ( &qpctx, 4,
- qpc_eec_data.mtu, HERMON_MTU_2048,
- qpc_eec_data.msg_max, 11 /* 2^11 = 2048 */ );
- MLX_FILL_1 ( &qpctx, 16,
- qpc_eec_data.primary_address_path.sched_queue,
- ( 0x83 /* default policy */ |
- ( ( ibdev->port - 1 ) << 6 ) ) );
- if ( ( rc = hermon_cmd_init2rtr_qp ( hermon, qp->qpn,
- &qpctx ) ) != 0 ) {
- DBGC ( hermon, "Hermon %p INIT2RTR_QP failed: %s\n",
- hermon, strerror ( rc ) );
- goto err_init2rtr_qp;
- }
- memset ( &qpctx, 0, sizeof ( qpctx ) );
- if ( ( rc = hermon_cmd_rtr2rts_qp ( hermon, qp->qpn, &qpctx ) ) != 0 ){
- DBGC ( hermon, "Hermon %p RTR2RTS_QP failed: %s\n",
- hermon, strerror ( rc ) );
- goto err_rtr2rts_qp;
- }
+ hermon_qp->state = HERMON_QP_ST_INIT;
DBGC ( hermon, "Hermon %p QPN %#lx send ring at [%p,%p)\n",
hermon, qp->qpn, hermon_qp->send.wqe,
@@ -924,8 +1047,6 @@ static int hermon_create_qp ( struct ib_device *ibdev,
ib_qp_set_drvdata ( qp, hermon_qp );
return 0;
- err_rtr2rts_qp:
- err_init2rtr_qp:
hermon_cmd_2rst_qp ( hermon, qp->qpn );
err_rst2init_qp:
hermon_free_mtt ( hermon, &hermon_qp->mtt );
@@ -934,8 +1055,8 @@ static int hermon_create_qp ( struct ib_device *ibdev,
err_alloc_wqe:
free ( hermon_qp );
err_hermon_qp:
- hermon_bitmask_free ( hermon->qp_inuse, qpn_offset, 1 );
- err_qpn_offset:
+ hermon_free_qpn ( ibdev, qp );
+ err_alloc_qpn:
return rc;
}
@@ -944,24 +1065,68 @@ static int hermon_create_qp ( struct ib_device *ibdev,
*
* @v ibdev Infiniband device
* @v qp Queue pair
- * @v mod_list Modification list
* @ret rc Return status code
*/
static int hermon_modify_qp ( struct ib_device *ibdev,
- struct ib_queue_pair *qp,
- unsigned long mod_list ) {
+ struct ib_queue_pair *qp ) {
struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp );
struct hermonprm_qp_ee_state_transitions qpctx;
- unsigned long optparammask = 0;
int rc;
- /* Construct optparammask */
- if ( mod_list & IB_MODIFY_QKEY )
- optparammask |= HERMON_QP_OPT_PARAM_QKEY;
+ /* Transition queue to RTR state, if applicable */
+ if ( hermon_qp->state < HERMON_QP_ST_RTR ) {
+ memset ( &qpctx, 0, sizeof ( qpctx ) );
+ MLX_FILL_2 ( &qpctx, 4,
+ qpc_eec_data.mtu, HERMON_MTU_2048,
+ qpc_eec_data.msg_max, 31 );
+ MLX_FILL_1 ( &qpctx, 7,
+ qpc_eec_data.remote_qpn_een, qp->av.qpn );
+ MLX_FILL_1 ( &qpctx, 9,
+ qpc_eec_data.primary_address_path.rlid,
+ qp->av.lid );
+ MLX_FILL_1 ( &qpctx, 10,
+ qpc_eec_data.primary_address_path.max_stat_rate,
+ hermon_rate ( &qp->av ) );
+ memcpy ( &qpctx.u.dwords[12], &qp->av.gid,
+ sizeof ( qp->av.gid ) );
+ MLX_FILL_1 ( &qpctx, 16,
+ qpc_eec_data.primary_address_path.sched_queue,
+ hermon_sched_queue ( ibdev, qp ) );
+ MLX_FILL_1 ( &qpctx, 39,
+ qpc_eec_data.next_rcv_psn, qp->recv.psn );
+ if ( ( rc = hermon_cmd_init2rtr_qp ( hermon, qp->qpn,
+ &qpctx ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p INIT2RTR_QP failed: %s\n",
+ hermon, strerror ( rc ) );
+ return rc;
+ }
+ hermon_qp->state = HERMON_QP_ST_RTR;
+ }
+
+ /* Transition queue to RTS state */
+ if ( hermon_qp->state < HERMON_QP_ST_RTS ) {
+ memset ( &qpctx, 0, sizeof ( qpctx ) );
+ MLX_FILL_1 ( &qpctx, 10,
+ qpc_eec_data.primary_address_path.ack_timeout,
+ 14 /* 4.096us * 2^(14) = 67ms */ );
+ MLX_FILL_2 ( &qpctx, 30,
+ qpc_eec_data.retry_count, HERMON_RETRY_MAX,
+ qpc_eec_data.rnr_retry, HERMON_RETRY_MAX );
+ MLX_FILL_1 ( &qpctx, 32,
+ qpc_eec_data.next_send_psn, qp->send.psn );
+ if ( ( rc = hermon_cmd_rtr2rts_qp ( hermon, qp->qpn,
+ &qpctx ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p RTR2RTS_QP failed: %s\n",
+ hermon, strerror ( rc ) );
+ return rc;
+ }
+ hermon_qp->state = HERMON_QP_ST_RTS;
+ }
- /* Issue RTS2RTS_QP */
+ /* Update parameters in RTS state */
memset ( &qpctx, 0, sizeof ( qpctx ) );
- MLX_FILL_1 ( &qpctx, 0, opt_param_mask, optparammask );
+ MLX_FILL_1 ( &qpctx, 0, opt_param_mask, HERMON_QP_OPT_PARAM_QKEY );
MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey );
if ( ( rc = hermon_cmd_rts2rts_qp ( hermon, qp->qpn, &qpctx ) ) != 0 ){
DBGC ( hermon, "Hermon %p RTS2RTS_QP failed: %s\n",
@@ -982,7 +1147,6 @@ static void hermon_destroy_qp ( struct ib_device *ibdev,
struct ib_queue_pair *qp ) {
struct hermon *hermon = ib_get_drvdata ( ibdev );
struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp );
- int qpn_offset;
int rc;
/* Take ownership back from hardware */
@@ -1001,9 +1165,7 @@ static void hermon_destroy_qp ( struct ib_device *ibdev,
free ( hermon_qp );
/* Mark queue number as free */
- qpn_offset = ( qp->qpn - HERMON_QPN_BASE -
- hermon->cap.reserved_qps );
- hermon_bitmask_free ( hermon->qp_inuse, qpn_offset, 1 );
+ hermon_free_qpn ( ibdev, qp );
ib_qp_set_drvdata ( qp, NULL );
}
@@ -1015,9 +1177,133 @@ static void hermon_destroy_qp ( struct ib_device *ibdev,
***************************************************************************
*/
-/** GID used for GID-less send work queue entries */
-static const struct ib_gid hermon_no_gid = {
- { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
+/**
+ * Construct UD send work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v av Address vector
+ * @v iobuf I/O buffer
+ * @v wqe Send work queue entry
+ * @ret opcode Control opcode
+ */
+static unsigned int
+hermon_fill_ud_send_wqe ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp __unused,
+ struct ib_address_vector *av,
+ struct io_buffer *iobuf,
+ union hermon_send_wqe *wqe ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+
+ MLX_FILL_1 ( &wqe->ud.ctrl, 1, ds,
+ ( ( offsetof ( typeof ( wqe->ud ), data[1] ) / 16 ) ) );
+ MLX_FILL_1 ( &wqe->ud.ctrl, 2, c, 0x03 /* generate completion */ );
+ MLX_FILL_2 ( &wqe->ud.ud, 0,
+ ud_address_vector.pd, HERMON_GLOBAL_PD,
+ ud_address_vector.port_number, ibdev->port );
+ MLX_FILL_2 ( &wqe->ud.ud, 1,
+ ud_address_vector.rlid, av->lid,
+ ud_address_vector.g, av->gid_present );
+ MLX_FILL_1 ( &wqe->ud.ud, 2,
+ ud_address_vector.max_stat_rate, hermon_rate ( av ) );
+ MLX_FILL_1 ( &wqe->ud.ud, 3, ud_address_vector.sl, av->sl );
+ memcpy ( &wqe->ud.ud.u.dwords[4], &av->gid, sizeof ( av->gid ) );
+ MLX_FILL_1 ( &wqe->ud.ud, 8, destination_qp, av->qpn );
+ MLX_FILL_1 ( &wqe->ud.ud, 9, q_key, av->qkey );
+ MLX_FILL_1 ( &wqe->ud.data[0], 0, byte_count, iob_len ( iobuf ) );
+ MLX_FILL_1 ( &wqe->ud.data[0], 1, l_key, hermon->lkey );
+ MLX_FILL_1 ( &wqe->ud.data[0], 3,
+ local_address_l, virt_to_bus ( iobuf->data ) );
+ return HERMON_OPCODE_SEND;
+}
+
+/**
+ * Construct MLX send work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v av Address vector
+ * @v iobuf I/O buffer
+ * @v wqe Send work queue entry
+ * @ret opcode Control opcode
+ */
+static unsigned int
+hermon_fill_mlx_send_wqe ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct ib_address_vector *av,
+ struct io_buffer *iobuf,
+ union hermon_send_wqe *wqe ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct io_buffer headers;
+
+ /* Construct IB headers */
+ iob_populate ( &headers, &wqe->mlx.headers, 0,
+ sizeof ( wqe->mlx.headers ) );
+ iob_reserve ( &headers, sizeof ( wqe->mlx.headers ) );
+ ib_push ( ibdev, &headers, qp, iob_len ( iobuf ), av );
+
+ /* Fill work queue entry */
+ MLX_FILL_1 ( &wqe->mlx.ctrl, 1, ds,
+ ( ( offsetof ( typeof ( wqe->mlx ), data[2] ) / 16 ) ) );
+ MLX_FILL_5 ( &wqe->mlx.ctrl, 2,
+ c, 0x03 /* generate completion */,
+ icrc, 0 /* generate ICRC */,
+ max_statrate, hermon_rate ( av ),
+ slr, 0,
+ v15, ( ( qp->ext_qpn == IB_QPN_SMI ) ? 1 : 0 ) );
+ MLX_FILL_1 ( &wqe->mlx.ctrl, 3, rlid, av->lid );
+ MLX_FILL_1 ( &wqe->mlx.data[0], 0,
+ byte_count, iob_len ( &headers ) );
+ MLX_FILL_1 ( &wqe->mlx.data[0], 1, l_key, hermon->lkey );
+ MLX_FILL_1 ( &wqe->mlx.data[0], 3,
+ local_address_l, virt_to_bus ( headers.data ) );
+ MLX_FILL_1 ( &wqe->mlx.data[1], 0,
+ byte_count, ( iob_len ( iobuf ) + 4 /* ICRC */ ) );
+ MLX_FILL_1 ( &wqe->mlx.data[1], 1, l_key, hermon->lkey );
+ MLX_FILL_1 ( &wqe->mlx.data[1], 3,
+ local_address_l, virt_to_bus ( iobuf->data ) );
+ return HERMON_OPCODE_SEND;
+}
+
+/**
+ * Construct RC send work queue entry
+ *
+ * @v ibdev Infiniband device
+ * @v qp Queue pair
+ * @v av Address vector
+ * @v iobuf I/O buffer
+ * @v wqe Send work queue entry
+ * @ret opcode Control opcode
+ */
+static unsigned int
+hermon_fill_rc_send_wqe ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp __unused,
+ struct ib_address_vector *av __unused,
+ struct io_buffer *iobuf,
+ union hermon_send_wqe *wqe ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+
+ MLX_FILL_1 ( &wqe->rc.ctrl, 1, ds,
+ ( ( offsetof ( typeof ( wqe->rc ), data[1] ) / 16 ) ) );
+ MLX_FILL_1 ( &wqe->rc.ctrl, 2, c, 0x03 /* generate completion */ );
+ MLX_FILL_1 ( &wqe->rc.data[0], 0, byte_count, iob_len ( iobuf ) );
+ MLX_FILL_1 ( &wqe->rc.data[0], 1, l_key, hermon->lkey );
+ MLX_FILL_1 ( &wqe->rc.data[0], 3,
+ local_address_l, virt_to_bus ( iobuf->data ) );
+ return HERMON_OPCODE_SEND;
+}
+
+/** Work queue entry constructors */
+static unsigned int
+( * hermon_fill_send_wqe[] ) ( struct ib_device *ibdev,
+ struct ib_queue_pair *qp,
+ struct ib_address_vector *av,
+ struct io_buffer *iobuf,
+ union hermon_send_wqe *wqe ) = {
+ [IB_QPT_SMI] = hermon_fill_mlx_send_wqe,
+ [IB_QPT_GSI] = hermon_fill_mlx_send_wqe,
+ [IB_QPT_UD] = hermon_fill_ud_send_wqe,
+ [IB_QPT_RC] = hermon_fill_rc_send_wqe,
};
/**
@@ -1037,10 +1323,10 @@ static int hermon_post_send ( struct ib_device *ibdev,
struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp );
struct ib_work_queue *wq = &qp->send;
struct hermon_send_work_queue *hermon_send_wq = &hermon_qp->send;
- struct hermonprm_ud_send_wqe *wqe;
- const struct ib_gid *gid;
+ union hermon_send_wqe *wqe;
union hermonprm_doorbell_register db_reg;
unsigned int wqe_idx_mask;
+ unsigned int opcode;
/* Allocate work queue entry */
wqe_idx_mask = ( wq->num_wqes - 1 );
@@ -1050,35 +1336,18 @@ static int hermon_post_send ( struct ib_device *ibdev,
}
wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf;
wqe = &hermon_send_wq->wqe[ wq->next_idx &
- ( hermon_send_wq->num_wqes - 1 ) ].ud;
+ ( hermon_send_wq->num_wqes - 1 ) ];
/* Construct work queue entry */
memset ( ( ( ( void * ) wqe ) + 4 /* avoid ctrl.owner */ ), 0,
( sizeof ( *wqe ) - 4 ) );
- MLX_FILL_1 ( &wqe->ctrl, 1, ds, ( sizeof ( *wqe ) / 16 ) );
- MLX_FILL_1 ( &wqe->ctrl, 2, c, 0x03 /* generate completion */ );
- MLX_FILL_2 ( &wqe->ud, 0,
- ud_address_vector.pd, HERMON_GLOBAL_PD,
- ud_address_vector.port_number, ibdev->port );
- MLX_FILL_2 ( &wqe->ud, 1,
- ud_address_vector.rlid, av->lid,
- ud_address_vector.g, av->gid_present );
- MLX_FILL_1 ( &wqe->ud, 2,
- ud_address_vector.max_stat_rate,
- ( ( ( av->rate < 2 ) || ( av->rate > 10 ) ) ?
- 8 : ( av->rate + 5 ) ) );
- MLX_FILL_1 ( &wqe->ud, 3, ud_address_vector.sl, av->sl );
- gid = ( av->gid_present ? &av->gid : &hermon_no_gid );
- memcpy ( &wqe->ud.u.dwords[4], gid, sizeof ( *gid ) );
- MLX_FILL_1 ( &wqe->ud, 8, destination_qp, av->qpn );
- MLX_FILL_1 ( &wqe->ud, 9, q_key, av->qkey );
- MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_len ( iobuf ) );
- MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->reserved_lkey );
- MLX_FILL_1 ( &wqe->data[0], 3,
- local_address_l, virt_to_bus ( iobuf->data ) );
+ assert ( qp->type < ( sizeof ( hermon_fill_send_wqe ) /
+ sizeof ( hermon_fill_send_wqe[0] ) ) );
+ assert ( hermon_fill_send_wqe[qp->type] != NULL );
+ opcode = hermon_fill_send_wqe[qp->type] ( ibdev, qp, av, iobuf, wqe );
barrier();
MLX_FILL_2 ( &wqe->ctrl, 0,
- opcode, HERMON_OPCODE_SEND,
+ opcode, opcode,
owner,
( ( wq->next_idx & hermon_send_wq->num_wqes ) ? 1 : 0 ) );
DBGCP ( hermon, "Hermon %p posting send WQE:\n", hermon );
@@ -1126,7 +1395,7 @@ static int hermon_post_recv ( struct ib_device *ibdev,
/* Construct work queue entry */
MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) );
- MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->reserved_lkey );
+ MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->lkey );
MLX_FILL_1 ( &wqe->data[0], 3,
local_address_l, virt_to_bus ( iobuf->data ) );
@@ -1157,8 +1426,9 @@ static int hermon_complete ( struct ib_device *ibdev,
struct ib_queue_pair *qp;
struct hermon_queue_pair *hermon_qp;
struct io_buffer *iobuf;
- struct ib_address_vector av;
+ struct ib_address_vector recv_av;
struct ib_global_route_header *grh;
+ struct ib_address_vector *av;
unsigned int opcode;
unsigned long qpn;
int is_send;
@@ -1196,7 +1466,7 @@ static int hermon_complete ( struct ib_device *ibdev,
iobuf = wq->iobufs[wqe_idx];
if ( ! iobuf ) {
DBGC ( hermon, "Hermon %p CQN %lx QPN %lx empty WQE %x\n",
- hermon, cq->cqn, qpn, wqe_idx );
+ hermon, cq->cqn, qp->qpn, wqe_idx );
return -EIO;
}
wq->iobufs[wqe_idx] = NULL;
@@ -1209,18 +1479,31 @@ static int hermon_complete ( struct ib_device *ibdev,
len = MLX_GET ( &cqe->normal, byte_cnt );
assert ( len <= iob_tailroom ( iobuf ) );
iob_put ( iobuf, len );
- assert ( iob_len ( iobuf ) >= sizeof ( *grh ) );
- grh = iobuf->data;
- iob_pull ( iobuf, sizeof ( *grh ) );
- /* Construct address vector */
- memset ( &av, 0, sizeof ( av ) );
- av.qpn = MLX_GET ( &cqe->normal, srq_rqpn );
- av.lid = MLX_GET ( &cqe->normal, slid_smac47_32 );
- av.sl = MLX_GET ( &cqe->normal, sl );
- av.gid_present = MLX_GET ( &cqe->normal, g );
- memcpy ( &av.gid, &grh->sgid, sizeof ( av.gid ) );
+ switch ( qp->type ) {
+ case IB_QPT_SMI:
+ case IB_QPT_GSI:
+ case IB_QPT_UD:
+ assert ( iob_len ( iobuf ) >= sizeof ( *grh ) );
+ grh = iobuf->data;
+ iob_pull ( iobuf, sizeof ( *grh ) );
+ /* Construct address vector */
+ av = &recv_av;
+ memset ( av, 0, sizeof ( *av ) );
+ av->qpn = MLX_GET ( &cqe->normal, srq_rqpn );
+ av->lid = MLX_GET ( &cqe->normal, slid_smac47_32 );
+ av->sl = MLX_GET ( &cqe->normal, sl );
+ av->gid_present = MLX_GET ( &cqe->normal, g );
+ memcpy ( &av->gid, &grh->sgid, sizeof ( av->gid ) );
+ break;
+ case IB_QPT_RC:
+ av = &qp->av;
+ break;
+ default:
+ assert ( 0 );
+ return -EINVAL;
+ }
/* Hand off to completion handler */
- ib_complete_recv ( ibdev, qp, &av, iobuf, rc );
+ ib_complete_recv ( ibdev, qp, av, iobuf, rc );
}
return rc;
@@ -1417,7 +1700,7 @@ static void hermon_event_port_state_change ( struct hermon *hermon,
( link_up ? "up" : "down" ) );
/* Sanity check */
- if ( port >= HERMON_NUM_PORTS ) {
+ if ( port >= hermon->cap.num_ports ) {
DBGC ( hermon, "Hermon %p port %d does not exist!\n",
hermon, ( port + 1 ) );
return;
@@ -1489,6 +1772,36 @@ static void hermon_poll_eq ( struct ib_device *ibdev ) {
*/
/**
+ * Sense port type
+ *
+ * @v ibdev Infiniband device
+ * @ret port_type Port type, or negative error
+ */
+static int hermon_sense_port_type ( struct ib_device *ibdev ) {
+ struct hermon *hermon = ib_get_drvdata ( ibdev );
+ struct hermonprm_sense_port sense_port;
+ int port_type;
+ int rc;
+
+ /* If DPDP is not supported, always assume Infiniband */
+ if ( ! hermon->cap.dpdp )
+ return HERMON_PORT_TYPE_IB;
+
+ /* Sense the port type */
+ if ( ( rc = hermon_cmd_sense_port ( hermon, ibdev->port,
+ &sense_port ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p port %d sense failed: %s\n",
+ hermon, ibdev->port, strerror ( rc ) );
+ return rc;
+ }
+ port_type = MLX_GET ( &sense_port, port_type );
+
+ DBGC ( hermon, "Hermon %p port %d type %d\n",
+ hermon, ibdev->port, port_type );
+ return port_type;
+}
+
+/**
* Initialise Infiniband link
*
* @v ibdev Infiniband device
@@ -1497,8 +1810,19 @@ static void hermon_poll_eq ( struct ib_device *ibdev ) {
static int hermon_open ( struct ib_device *ibdev ) {
struct hermon *hermon = ib_get_drvdata ( ibdev );
struct hermonprm_init_port init_port;
+ int port_type;
int rc;
+ /* Check we are connected to an Infiniband network */
+ if ( ( rc = port_type = hermon_sense_port_type ( ibdev ) ) < 0 )
+ return rc;
+ if ( port_type != HERMON_PORT_TYPE_IB ) {
+ DBGC ( hermon, "Hermon %p port %d not connected to an "
+ "Infiniband network", hermon, ibdev->port );
+ return -ENOTCONN;
+ }
+
+ /* Init Port */
memset ( &init_port, 0, sizeof ( init_port ) );
MLX_FILL_2 ( &init_port, 0,
port_width_cap, 3,
@@ -1536,6 +1860,27 @@ static void hermon_close ( struct ib_device *ibdev ) {
}
}
+/**
+ * Inform embedded subnet management agent of a received MAD
+ *
+ * @v ibdev Infiniband device
+ * @v mad MAD
+ * @ret rc Return status code
+ */
+static int hermon_inform_sma ( struct ib_device *ibdev,
+ union ib_mad *mad ) {
+ int rc;
+
+ /* Send the MAD to the embedded SMA */
+ if ( ( rc = hermon_mad ( ibdev, mad ) ) != 0 )
+ return rc;
+
+ /* Update parameters held in software */
+ ib_smc_update ( ibdev, hermon_mad );
+
+ return 0;
+}
+
/***************************************************************************
*
* Multicast group operations
@@ -1646,6 +1991,8 @@ static struct ib_device_operations hermon_ib_operations = {
.close = hermon_close,
.mcast_attach = hermon_mcast_attach,
.mcast_detach = hermon_mcast_detach,
+ .set_port_info = hermon_inform_sma,
+ .set_pkey_table = hermon_inform_sma,
};
/***************************************************************************
@@ -1676,6 +2023,12 @@ static int hermon_map_vpm ( struct hermon *hermon,
assert ( ( pa & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
assert ( ( len & ( HERMON_PAGE_SIZE - 1 ) ) == 0 );
+ /* These mappings tend to generate huge volumes of
+ * uninteresting debug data, which basically makes it
+ * impossible to use debugging otherwise.
+ */
+ DBG_DISABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
+
while ( len ) {
memset ( &mapping, 0, sizeof ( mapping ) );
MLX_FILL_1 ( &mapping, 0, va_h, ( va >> 32 ) );
@@ -1684,6 +2037,7 @@ static int hermon_map_vpm ( struct hermon *hermon,
log2size, 0,
pa_l, ( pa >> 12 ) );
if ( ( rc = map ( hermon, &mapping ) ) != 0 ) {
+ DBG_ENABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
DBGC ( hermon, "Hermon %p could not map %llx => %lx: "
"%s\n", hermon, va, pa, strerror ( rc ) );
return rc;
@@ -1693,6 +2047,7 @@ static int hermon_map_vpm ( struct hermon *hermon,
len -= HERMON_PAGE_SIZE;
}
+ DBG_ENABLE ( DBGLVL_LOG | DBGLVL_EXTRA );
return 0;
}
@@ -1821,6 +2176,15 @@ static int hermon_get_cap ( struct hermon *hermon ) {
( 1 << MLX_GET ( &dev_cap, log2_rsvd_mrws ) );
hermon->cap.dmpt_entry_size = MLX_GET ( &dev_cap, d_mpt_entry_sz );
hermon->cap.reserved_uars = MLX_GET ( &dev_cap, num_rsvd_uars );
+ hermon->cap.num_ports = MLX_GET ( &dev_cap, num_ports );
+ hermon->cap.dpdp = MLX_GET ( &dev_cap, dpdp );
+
+ /* Sanity check */
+ if ( hermon->cap.num_ports > HERMON_MAX_PORTS ) {
+ DBGC ( hermon, "Hermon %p has %d ports (only %d supported)\n",
+ hermon, hermon->cap.num_ports, HERMON_MAX_PORTS );
+ hermon->cap.num_ports = HERMON_MAX_PORTS;
+ }
return 0;
}
@@ -1868,7 +2232,8 @@ static int hermon_alloc_icm ( struct hermon *hermon,
*/
/* Calculate number of each object type within ICM */
- log_num_qps = fls ( hermon->cap.reserved_qps + HERMON_MAX_QPS - 1 );
+ log_num_qps = fls ( hermon->cap.reserved_qps +
+ HERMON_RSVD_SPECIAL_QPS + HERMON_MAX_QPS - 1 );
log_num_srqs = fls ( hermon->cap.reserved_srqs - 1 );
log_num_cqs = fls ( hermon->cap.reserved_cqs + HERMON_MAX_CQS - 1 );
log_num_eqs = fls ( hermon->cap.reserved_eqs + HERMON_MAX_EQS - 1 );
@@ -2130,17 +2495,21 @@ static int hermon_setup_mpt ( struct hermon *hermon ) {
/* Derive key */
key = ( hermon->cap.reserved_mrws | HERMON_MKEY_PREFIX );
- hermon->reserved_lkey = ( ( key << 8 ) | ( key >> 24 ) );
+ hermon->lkey = ( ( key << 8 ) | ( key >> 24 ) );
/* Initialise memory protection table */
memset ( &mpt, 0, sizeof ( mpt ) );
- MLX_FILL_4 ( &mpt, 0,
- r_w, 1,
- pa, 1,
+ MLX_FILL_7 ( &mpt, 0,
+ atomic, 1,
+ rw, 1,
+ rr, 1,
+ lw, 1,
lr, 1,
- lw, 1 );
+ pa, 1,
+ r_w, 1 );
MLX_FILL_1 ( &mpt, 2, mem_key, key );
- MLX_FILL_1 ( &mpt, 3, pd, HERMON_GLOBAL_PD );
+ MLX_FILL_1 ( &mpt, 3,
+ pd, HERMON_GLOBAL_PD );
MLX_FILL_1 ( &mpt, 10, len64, 1 );
if ( ( rc = hermon_cmd_sw2hw_mpt ( hermon,
hermon->cap.reserved_mrws,
@@ -2154,6 +2523,54 @@ static int hermon_setup_mpt ( struct hermon *hermon ) {
}
/**
+ * Configure special queue pairs
+ *
+ * @v hermon Hermon device
+ * @ret rc Return status code
+ */
+static int hermon_configure_special_qps ( struct hermon *hermon ) {
+ int rc;
+
+ /* Special QP block must be aligned on its own size */
+ hermon->special_qpn_base = ( ( hermon->cap.reserved_qps +
+ HERMON_NUM_SPECIAL_QPS - 1 )
+ & ~( HERMON_NUM_SPECIAL_QPS - 1 ) );
+ hermon->qpn_base = ( hermon->special_qpn_base +
+ HERMON_NUM_SPECIAL_QPS );
+ DBGC ( hermon, "Hermon %p special QPs at [%lx,%lx]\n", hermon,
+ hermon->special_qpn_base, ( hermon->qpn_base - 1 ) );
+
+ /* Issue command to configure special QPs */
+ if ( ( rc = hermon_cmd_conf_special_qp ( hermon, 0x00,
+ hermon->special_qpn_base ) ) != 0 ) {
+ DBGC ( hermon, "Hermon %p could not configure special QPs: "
+ "%s\n", hermon, strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Reset device
+ *
+ * @v hermon Hermon device
+ * @v pci PCI device
+ */
+static void hermon_reset ( struct hermon *hermon,
+ struct pci_device *pci ) {
+ struct pci_config_backup backup;
+ static const uint8_t backup_exclude[] =
+ PCI_CONFIG_BACKUP_EXCLUDE ( 0x58, 0x5c );
+
+ pci_backup ( pci, &backup, backup_exclude );
+ writel ( HERMON_RESET_MAGIC,
+ ( hermon->config + HERMON_RESET_OFFSET ) );
+ mdelay ( HERMON_RESET_WAIT_TIME_MS );
+ pci_restore ( pci, &backup, backup_exclude );
+}
+
+/**
* Probe PCI device
*
* @v pci PCI device
@@ -2165,7 +2582,7 @@ static int hermon_probe ( struct pci_device *pci,
struct hermon *hermon;
struct ib_device *ibdev;
struct hermonprm_init_hca init_hca;
- int i;
+ unsigned int i;
int rc;
/* Allocate Hermon device */
@@ -2176,20 +2593,6 @@ static int hermon_probe ( struct pci_device *pci,
}
pci_set_drvdata ( pci, hermon );
- /* Allocate Infiniband devices */
- for ( i = 0 ; i < HERMON_NUM_PORTS ; i++ ) {
- ibdev = alloc_ibdev ( 0 );
- if ( ! ibdev ) {
- rc = -ENOMEM;
- goto err_alloc_ibdev;
- }
- hermon->ibdev[i] = ibdev;
- ibdev->op = &hermon_ib_operations;
- ibdev->dev = &pci->dev;
- ibdev->port = ( HERMON_PORT_BASE + i );
- ib_set_drvdata ( ibdev, hermon );
- }
-
/* Fix up PCI device */
adjust_pci_device ( pci );
@@ -2199,6 +2602,9 @@ static int hermon_probe ( struct pci_device *pci,
hermon->uar = ioremap ( pci_bar_start ( pci, HERMON_PCI_UAR_BAR ),
HERMON_UAR_NON_EQ_PAGE * HERMON_PAGE_SIZE );
+ /* Reset device */
+ hermon_reset ( hermon, pci );
+
/* Allocate space for mailboxes */
hermon->mailbox_in = malloc_dma ( HERMON_MBOX_SIZE,
HERMON_MBOX_ALIGN );
@@ -2221,6 +2627,20 @@ static int hermon_probe ( struct pci_device *pci,
if ( ( rc = hermon_get_cap ( hermon ) ) != 0 )
goto err_get_cap;
+ /* Allocate Infiniband devices */
+ for ( i = 0 ; i < hermon->cap.num_ports ; i++ ) {
+ ibdev = alloc_ibdev ( 0 );
+ if ( ! ibdev ) {
+ rc = -ENOMEM;
+ goto err_alloc_ibdev;
+ }
+ hermon->ibdev[i] = ibdev;
+ ibdev->op = &hermon_ib_operations;
+ ibdev->dev = &pci->dev;
+ ibdev->port = ( HERMON_PORT_BASE + i );
+ ib_set_drvdata ( ibdev, hermon );
+ }
+
/* Allocate ICM */
memset ( &init_hca, 0, sizeof ( init_hca ) );
if ( ( rc = hermon_alloc_icm ( hermon, &init_hca ) ) != 0 )
@@ -2239,17 +2659,24 @@ static int hermon_probe ( struct pci_device *pci,
/* Set up memory protection */
if ( ( rc = hermon_setup_mpt ( hermon ) ) != 0 )
goto err_setup_mpt;
+ for ( i = 0 ; i < hermon->cap.num_ports ; i++ )
+ hermon->ibdev[i]->rdma_key = hermon->lkey;
/* Set up event queue */
if ( ( rc = hermon_create_eq ( hermon ) ) != 0 )
goto err_create_eq;
- /* Update MAD parameters */
- for ( i = 0 ; i < HERMON_NUM_PORTS ; i++ )
+ /* Configure special QPs */
+ if ( ( rc = hermon_configure_special_qps ( hermon ) ) != 0 )
+ goto err_conf_special_qps;
+
+ /* Update IPoIB MAC address */
+ for ( i = 0 ; i < hermon->cap.num_ports ; i++ ) {
ib_smc_update ( hermon->ibdev[i], hermon_mad );
+ }
/* Register Infiniband devices */
- for ( i = 0 ; i < HERMON_NUM_PORTS ; i++ ) {
+ for ( i = 0 ; i < hermon->cap.num_ports ; i++ ) {
if ( ( rc = register_ibdev ( hermon->ibdev[i] ) ) != 0 ) {
DBGC ( hermon, "Hermon %p could not register IB "
"device: %s\n", hermon, strerror ( rc ) );
@@ -2259,10 +2686,11 @@ static int hermon_probe ( struct pci_device *pci,
return 0;
- i = HERMON_NUM_PORTS;
+ i = hermon->cap.num_ports;
err_register_ibdev:
- for ( i-- ; i >= 0 ; i-- )
+ for ( i-- ; ( signed int ) i >= 0 ; i-- )
unregister_ibdev ( hermon->ibdev[i] );
+ err_conf_special_qps:
hermon_destroy_eq ( hermon );
err_create_eq:
err_setup_mpt:
@@ -2270,6 +2698,10 @@ static int hermon_probe ( struct pci_device *pci,
err_init_hca:
hermon_free_icm ( hermon );
err_alloc_icm:
+ i = hermon->cap.num_ports;
+ err_alloc_ibdev:
+ for ( i-- ; ( signed int ) i >= 0 ; i-- )
+ ibdev_put ( hermon->ibdev[i] );
err_get_cap:
hermon_stop_firmware ( hermon );
err_start_firmware:
@@ -2277,10 +2709,6 @@ static int hermon_probe ( struct pci_device *pci,
err_mailbox_out:
free_dma ( hermon->mailbox_in, HERMON_MBOX_SIZE );
err_mailbox_in:
- i = HERMON_NUM_PORTS;
- err_alloc_ibdev:
- for ( i-- ; i >= 0 ; i-- )
- ibdev_put ( hermon->ibdev[i] );
free ( hermon );
err_alloc_hermon:
return rc;
@@ -2295,7 +2723,7 @@ static void hermon_remove ( struct pci_device *pci ) {
struct hermon *hermon = pci_get_drvdata ( pci );
int i;
- for ( i = ( HERMON_NUM_PORTS - 1 ) ; i >= 0 ; i-- )
+ for ( i = ( hermon->cap.num_ports - 1 ) ; i >= 0 ; i-- )
unregister_ibdev ( hermon->ibdev[i] );
hermon_destroy_eq ( hermon );
hermon_cmd_close_hca ( hermon );
@@ -2304,16 +2732,16 @@ static void hermon_remove ( struct pci_device *pci ) {
hermon_stop_firmware ( hermon );
free_dma ( hermon->mailbox_out, HERMON_MBOX_SIZE );
free_dma ( hermon->mailbox_in, HERMON_MBOX_SIZE );
- for ( i = ( HERMON_NUM_PORTS - 1 ) ; i >= 0 ; i-- )
+ for ( i = ( hermon->cap.num_ports - 1 ) ; i >= 0 ; i-- )
ibdev_put ( hermon->ibdev[i] );
free ( hermon );
}
static struct pci_device_id hermon_nics[] = {
- PCI_ROM ( 0x15b3, 0x6340, "mt25408", "MT25408 HCA driver" ),
- PCI_ROM ( 0x15b3, 0x634a, "mt25418", "MT25418 HCA driver" ),
- PCI_ROM ( 0x15b3, 0x6732, "mt26418", "MT26418 HCA driver" ),
- PCI_ROM ( 0x15b3, 0x673c, "mt26428", "MT26428 HCA driver" ),
+ PCI_ROM ( 0x15b3, 0x6340, "mt25408", "MT25408 HCA driver", 0 ),
+ PCI_ROM ( 0x15b3, 0x634a, "mt25418", "MT25418 HCA driver", 0 ),
+ PCI_ROM ( 0x15b3, 0x6732, "mt26418", "MT26418 HCA driver", 0 ),
+ PCI_ROM ( 0x15b3, 0x673c, "mt26428", "MT26428 HCA driver", 0 ),
};
struct pci_driver hermon_driver __pci_driver = {