aboutsummaryrefslogtreecommitdiffstats
path: root/gpxe/src/drivers/infiniband
diff options
context:
space:
mode:
Diffstat (limited to 'gpxe/src/drivers/infiniband')
-rw-r--r--gpxe/src/drivers/infiniband/MT25218_PRM.h2
-rw-r--r--gpxe/src/drivers/infiniband/MT25408_PRM.h8
-rw-r--r--gpxe/src/drivers/infiniband/arbel.c18
-rw-r--r--gpxe/src/drivers/infiniband/arbel.h2
-rw-r--r--gpxe/src/drivers/infiniband/hermon.c702
-rw-r--r--gpxe/src/drivers/infiniband/hermon.h91
-rw-r--r--gpxe/src/drivers/infiniband/ib_packet.c234
-rw-r--r--gpxe/src/drivers/infiniband/ib_sma.c553
-rw-r--r--gpxe/src/drivers/infiniband/ib_smc.c166
-rw-r--r--gpxe/src/drivers/infiniband/linda.c99
-rw-r--r--gpxe/src/drivers/infiniband/linda.h5
-rw-r--r--gpxe/src/drivers/infiniband/linda_fw.c2
-rw-r--r--gpxe/src/drivers/infiniband/mlx_bitops.h14
-rw-r--r--gpxe/src/drivers/infiniband/qib_7220_regs.h11
-rwxr-xr-x[-rw-r--r--]gpxe/src/drivers/infiniband/qib_genbits.pl29
15 files changed, 784 insertions, 1152 deletions
diff --git a/gpxe/src/drivers/infiniband/MT25218_PRM.h b/gpxe/src/drivers/infiniband/MT25218_PRM.h
index 19ca92cd..f1b7c1ff 100644
--- a/gpxe/src/drivers/infiniband/MT25218_PRM.h
+++ b/gpxe/src/drivers/infiniband/MT25218_PRM.h
@@ -19,6 +19,8 @@
Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
*/
+FILE_LICENCE ( GPL2_ONLY );
+
/***
*** This file was generated at "Tue Nov 22 15:21:23 2005"
*** by:
diff --git a/gpxe/src/drivers/infiniband/MT25408_PRM.h b/gpxe/src/drivers/infiniband/MT25408_PRM.h
index 17882ed7..419e25ac 100644
--- a/gpxe/src/drivers/infiniband/MT25408_PRM.h
+++ b/gpxe/src/drivers/infiniband/MT25408_PRM.h
@@ -19,6 +19,8 @@
Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved.
*/
+FILE_LICENCE ( GPL2_ONLY );
+
/***
*** This file was generated at "Mon Apr 16 23:22:02 2007"
*** by:
@@ -2069,7 +2071,11 @@ struct hermonprm_query_dev_cap_st { /* Little Endian */
pseudo_bit_t pkv[0x00001]; /* PKey Violation Counter Supported */
pseudo_bit_t qkv[0x00001]; /* QKey Violation Coutner Supported */
pseudo_bit_t vmm[0x00001]; /* Hermon New */
- pseudo_bit_t reserved27[0x00005];
+ pseudo_bit_t fcoe[0x00001];
+ pseudo_bit_t dpdp[0x00001]; /* Dual Port Different Protocols */
+ pseudo_bit_t raw_ethertype[0x00001];
+ pseudo_bit_t raw_ipv6[0x00001];
+ pseudo_bit_t blh[0x00001];
pseudo_bit_t mw[0x00001]; /* Memory windows supported */
pseudo_bit_t apm[0x00001]; /* Automatic Path Migration Supported */
pseudo_bit_t atm[0x00001]; /* Atomic operations supported (atomicity is guaranteed between QPs on this HCA) */
diff --git a/gpxe/src/drivers/infiniband/arbel.c b/gpxe/src/drivers/infiniband/arbel.c
index 1756a6e2..5bf35743 100644
--- a/gpxe/src/drivers/infiniband/arbel.c
+++ b/gpxe/src/drivers/infiniband/arbel.c
@@ -19,6 +19,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+FILE_LICENCE ( GPL2_OR_LATER );
+
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
@@ -853,7 +855,6 @@ static int arbel_create_qp ( struct ib_device *ibdev,
( virt_to_bus ( arbel_qp->recv.wqe ) >> 6 ) );
MLX_FILL_1 ( &qpctx, 43, qpc_eec_data.rcv_db_record_index,
arbel_qp->recv.doorbell_idx );
- MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey );
if ( ( rc = arbel_cmd_rst2init_qpee ( arbel, qp->qpn, &qpctx )) != 0 ){
DBGC ( arbel, "Arbel %p RST2INIT_QPEE failed: %s\n",
arbel, strerror ( rc ) );
@@ -906,24 +907,17 @@ static int arbel_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 arbel_modify_qp ( struct ib_device *ibdev,
- struct ib_queue_pair *qp,
- unsigned long mod_list ) {
+ struct ib_queue_pair *qp ) {
struct arbel *arbel = ib_get_drvdata ( ibdev );
struct arbelprm_qp_ee_state_transitions qpctx;
- unsigned long optparammask = 0;
int rc;
- /* Construct optparammask */
- if ( mod_list & IB_MODIFY_QKEY )
- optparammask |= ARBEL_QPEE_OPT_PARAM_QKEY;
-
/* Issue RTS2RTS_QP */
memset ( &qpctx, 0, sizeof ( qpctx ) );
- MLX_FILL_1 ( &qpctx, 0, opt_param_mask, optparammask );
+ MLX_FILL_1 ( &qpctx, 0, opt_param_mask, ARBEL_QPEE_OPT_PARAM_QKEY );
MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey );
if ( ( rc = arbel_cmd_rts2rts_qp ( arbel, qp->qpn, &qpctx ) ) != 0 ){
DBGC ( arbel, "Arbel %p RTS2RTS_QP failed: %s\n",
@@ -2241,8 +2235,8 @@ static void arbel_remove ( struct pci_device *pci ) {
}
static struct pci_device_id arbel_nics[] = {
- PCI_ROM ( 0x15b3, 0x6282, "mt25218", "MT25218 HCA driver" ),
- PCI_ROM ( 0x15b3, 0x6274, "mt25204", "MT25204 HCA driver" ),
+ PCI_ROM ( 0x15b3, 0x6282, "mt25218", "MT25218 HCA driver", 0 ),
+ PCI_ROM ( 0x15b3, 0x6274, "mt25204", "MT25204 HCA driver", 0 ),
};
struct pci_driver arbel_driver __pci_driver = {
diff --git a/gpxe/src/drivers/infiniband/arbel.h b/gpxe/src/drivers/infiniband/arbel.h
index 7d97b156..87f5933d 100644
--- a/gpxe/src/drivers/infiniband/arbel.h
+++ b/gpxe/src/drivers/infiniband/arbel.h
@@ -7,6 +7,8 @@
*
*/
+FILE_LICENCE ( GPL2_OR_LATER );
+
#include <stdint.h>
#include <gpxe/uaccess.h>
#include "mlx_bitops.h"
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 = {
diff --git a/gpxe/src/drivers/infiniband/hermon.h b/gpxe/src/drivers/infiniband/hermon.h
index ed39da69..c53f3da5 100644
--- a/gpxe/src/drivers/infiniband/hermon.h
+++ b/gpxe/src/drivers/infiniband/hermon.h
@@ -7,8 +7,11 @@
*
*/
+FILE_LICENCE ( GPL2_OR_LATER );
+
#include <stdint.h>
#include <gpxe/uaccess.h>
+#include <gpxe/ib_packet.h>
#include "mlx_bitops.h"
#include "MT25408_PRM.h"
@@ -18,7 +21,7 @@
*/
/* Ports in existence */
-#define HERMON_NUM_PORTS 2
+#define HERMON_MAX_PORTS 2
#define HERMON_PORT_BASE 1
/* PCI BARs */
@@ -26,7 +29,13 @@
#define HERMON_PCI_CONFIG_BAR_SIZE 0x100000
#define HERMON_PCI_UAR_BAR PCI_BASE_ADDRESS_2
+/* Device reset */
+#define HERMON_RESET_OFFSET 0x0f0010
+#define HERMON_RESET_MAGIC 0x01000000UL
+#define HERMON_RESET_WAIT_TIME_MS 1000
+
/* Work queue entry and completion queue entry opcodes */
+#define HERMON_OPCODE_NOP 0x00
#define HERMON_OPCODE_SEND 0x0a
#define HERMON_OPCODE_RECV_ERROR 0xfe
#define HERMON_OPCODE_SEND_ERROR 0xff
@@ -51,10 +60,13 @@
#define HERMON_HCR_RTR2RTS_QP 0x001b
#define HERMON_HCR_RTS2RTS_QP 0x001c
#define HERMON_HCR_2RST_QP 0x0021
+#define HERMON_HCR_QUERY_QP 0x0022
+#define HERMON_HCR_CONF_SPECIAL_QP 0x0023
#define HERMON_HCR_MAD_IFC 0x0024
#define HERMON_HCR_READ_MCG 0x0025
#define HERMON_HCR_WRITE_MCG 0x0026
#define HERMON_HCR_MGID_HASH 0x0027
+#define HERMON_HCR_SENSE_PORT 0x004d
#define HERMON_HCR_RUN_FW 0x0ff6
#define HERMON_HCR_DISABLE_LAM 0x0ff7
#define HERMON_HCR_ENABLE_LAM 0x0ff8
@@ -67,7 +79,9 @@
#define HERMON_HCR_MAP_FA 0x0fff
/* Service types */
+#define HERMON_ST_RC 0x00
#define HERMON_ST_UD 0x03
+#define HERMON_ST_MLX 0x07
/* MTUs */
#define HERMON_MTU_2048 0x04
@@ -80,13 +94,24 @@
#define HERMON_DB_EQ_OFFSET(_eqn) \
( 0x800 + HERMON_PAGE_SIZE * ( (_eqn) / 4 ) + 0x08 * ( (_eqn) % 4 ) )
+#define HERMON_QP_OPT_PARAM_PM_STATE 0x00000400UL
#define HERMON_QP_OPT_PARAM_QKEY 0x00000020UL
+#define HERMON_QP_OPT_PARAM_ALT_PATH 0x00000001UL
#define HERMON_MAP_EQ ( 0UL << 31 )
#define HERMON_UNMAP_EQ ( 1UL << 31 )
#define HERMON_EV_PORT_STATE_CHANGE 0x09
+#define HERMON_SCHED_QP0 0x3f
+#define HERMON_SCHED_DEFAULT 0x83
+
+#define HERMON_PM_STATE_ARMED 0x00
+#define HERMON_PM_STATE_REARM 0x01
+#define HERMON_PM_STATE_MIGRATED 0x03
+
+#define HERMON_RETRY_MAX 0x07
+
/*
* Datatypes that seem to be missing from the autogenerated documentation
*
@@ -145,6 +170,14 @@ struct hermonprm_port_state_change_event_st {
struct hermonprm_port_state_change_st data;
} __attribute__ (( packed ));
+/** Hermon sense port */
+struct hermonprm_sense_port_st {
+ pseudo_bit_t port_type[0x00020];
+/* -------------- */
+ pseudo_bit_t reserved[0x00020];
+};
+#define HERMON_PORT_TYPE_IB 1
+
/*
* Wrapper structures for hardware datatypes
*
@@ -173,9 +206,11 @@ struct MLX_DECLARE_STRUCT ( hermonprm_query_dev_cap );
struct MLX_DECLARE_STRUCT ( hermonprm_query_fw );
struct MLX_DECLARE_STRUCT ( hermonprm_queue_pair_ee_context_entry );
struct MLX_DECLARE_STRUCT ( hermonprm_scalar_parameter );
+struct MLX_DECLARE_STRUCT ( hermonprm_sense_port );
struct MLX_DECLARE_STRUCT ( hermonprm_send_db_register );
struct MLX_DECLARE_STRUCT ( hermonprm_ud_address_vector );
struct MLX_DECLARE_STRUCT ( hermonprm_virtual_physical_mapping );
+struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_ctrl_mlx );
struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_ctrl_send );
struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_data_ptr );
struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_ud );
@@ -191,7 +226,7 @@ struct hermonprm_write_mtt {
struct hermonprm_mtt mtt;
} __attribute__ (( packed ));
-#define HERMON_MAX_GATHER 1
+#define HERMON_MAX_GATHER 2
struct hermonprm_ud_send_wqe {
struct hermonprm_wqe_segment_ctrl_send ctrl;
@@ -199,6 +234,17 @@ struct hermonprm_ud_send_wqe {
struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_GATHER];
} __attribute__ (( packed ));
+struct hermonprm_mlx_send_wqe {
+ struct hermonprm_wqe_segment_ctrl_mlx ctrl;
+ struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_GATHER];
+ uint8_t headers[IB_MAX_HEADER_SIZE];
+} __attribute__ (( packed ));
+
+struct hermonprm_rc_send_wqe {
+ struct hermonprm_wqe_segment_ctrl_send ctrl;
+ struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_GATHER];
+} __attribute__ (( packed ));
+
#define HERMON_MAX_SCATTER 1
struct hermonprm_recv_wqe {
@@ -265,6 +311,10 @@ struct hermon_dev_cap {
size_t dmpt_entry_size;
/** Number of reserved UARs */
unsigned int reserved_uars;
+ /** Number of ports */
+ unsigned int num_ports;
+ /** Dual-port different protocol */
+ int dpdp;
};
/** Number of cMPT entries of each type */
@@ -318,7 +368,10 @@ struct hermon_mtt {
/** A Hermon send work queue entry */
union hermon_send_wqe {
+ struct hermonprm_wqe_segment_ctrl_send ctrl;
struct hermonprm_ud_send_wqe ud;
+ struct hermonprm_mlx_send_wqe mlx;
+ struct hermonprm_rc_send_wqe rc;
uint8_t force_align[HERMON_SEND_WQE_ALIGN];
} __attribute__ (( packed ));
@@ -358,14 +411,32 @@ struct hermon_recv_work_queue {
struct hermonprm_qp_db_record doorbell __attribute__ (( aligned (4) ));
};
+/** Number of special queue pairs */
+#define HERMON_NUM_SPECIAL_QPS 8
+
+/** Number of queue pairs reserved for the "special QP" block
+ *
+ * The special QPs must be within a contiguous block aligned on its
+ * own size.
+ */
+#define HERMON_RSVD_SPECIAL_QPS ( ( HERMON_NUM_SPECIAL_QPS << 1 ) - 1 )
+
/** Maximum number of allocatable queue pairs
*
* This is a policy decision, not a device limit.
*/
#define HERMON_MAX_QPS 8
-/** Base queue pair number */
-#define HERMON_QPN_BASE 0x550000
+/** Queue pair number randomisation mask */
+#define HERMON_QPN_RANDOM_MASK 0xfff000
+
+/** Hermon queue pair state */
+enum hermon_queue_pair_state {
+ HERMON_QP_ST_RST = 0,
+ HERMON_QP_ST_INIT,
+ HERMON_QP_ST_RTR,
+ HERMON_QP_ST_RTS,
+};
/** A Hermon queue pair */
struct hermon_queue_pair {
@@ -379,6 +450,8 @@ struct hermon_queue_pair {
struct hermon_send_work_queue send;
/** Receive work queue */
struct hermon_recv_work_queue recv;
+ /** Queue state */
+ enum hermon_queue_pair_state state;
};
/** Maximum number of allocatable completion queues
@@ -458,11 +531,11 @@ struct hermon {
/** Event queue */
struct hermon_event_queue eq;
- /** Reserved LKey
+ /** Unrestricted LKey
*
* Used to get unrestricted memory access.
*/
- unsigned long reserved_lkey;
+ unsigned long lkey;
/** Completion queue in-use bitmask */
hermon_bitmask_t cq_inuse[ HERMON_BITMASK_SIZE ( HERMON_MAX_CQS ) ];
@@ -473,9 +546,13 @@ struct hermon {
/** Device capabilities */
struct hermon_dev_cap cap;
+ /** Special QPN base */
+ unsigned long special_qpn_base;
+ /** QPN base */
+ unsigned long qpn_base;
/** Infiniband devices */
- struct ib_device *ibdev[HERMON_NUM_PORTS];
+ struct ib_device *ibdev[HERMON_MAX_PORTS];
};
/** Global protection domain */
diff --git a/gpxe/src/drivers/infiniband/ib_packet.c b/gpxe/src/drivers/infiniband/ib_packet.c
deleted file mode 100644
index 0f21617f..00000000
--- a/gpxe/src/drivers/infiniband/ib_packet.c
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/ib_packet.h>
-
-/**
- * @file
- *
- * Infiniband Packet Formats
- *
- */
-
-/**
- * Add IB headers
- *
- * @v ibdev Infiniband device
- * @v iobuf I/O buffer to contain headers
- * @v qp Queue pair
- * @v payload_len Payload length
- * @v av Address vector
- */
-int ib_push ( struct ib_device *ibdev, struct io_buffer *iobuf,
- struct ib_queue_pair *qp, size_t payload_len,
- const struct ib_address_vector *av ) {
- struct ib_local_route_header *lrh;
- struct ib_global_route_header *grh;
- struct ib_base_transport_header *bth;
- struct ib_datagram_extended_transport_header *deth;
- size_t orig_iob_len = iob_len ( iobuf );
- size_t pad_len;
- size_t lrh_len;
- size_t grh_len;
- unsigned int vl;
- unsigned int lnh;
-
- DBGC2 ( ibdev, "IBDEV %p TX %04x:%08lx => %04x:%08lx (key %08lx)\n",
- ibdev, ibdev->lid, qp->qpn, av->lid, av->qpn, av->qkey );
-
- /* Calculate packet length */
- pad_len = ( (-payload_len) & 0x3 );
- payload_len += pad_len;
- payload_len += 4; /* ICRC */
-
- /* Reserve space for headers */
- orig_iob_len = iob_len ( iobuf );
- deth = iob_push ( iobuf, sizeof ( *deth ) );
- bth = iob_push ( iobuf, sizeof ( *bth ) );
- grh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len );
- grh = ( av->gid_present ?
- iob_push ( iobuf, sizeof ( *grh ) ) : NULL );
- lrh = iob_push ( iobuf, sizeof ( *lrh ) );
- lrh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len );
-
- /* Construct LRH */
- vl = ( ( av->qpn == IB_QPN_SMP ) ? IB_VL_SMP : IB_VL_DEFAULT );
- lrh->vl__lver = ( vl << 4 );
- lnh = ( grh ? IB_LNH_GRH : IB_LNH_BTH );
- lrh->sl__lnh = ( ( av->sl << 4 ) | lnh );
- lrh->dlid = htons ( av->lid );
- lrh->length = htons ( lrh_len >> 2 );
- lrh->slid = htons ( ibdev->lid );
-
- /* Construct GRH, if required */
- if ( grh ) {
- grh->ipver__tclass__flowlabel =
- htonl ( IB_GRH_IPVER_IPv6 << 28 );
- grh->paylen = htons ( grh_len );
- grh->nxthdr = IB_GRH_NXTHDR_IBA;
- grh->hoplmt = 0;
- memcpy ( &grh->sgid, &ibdev->gid, sizeof ( grh->sgid ) );
- memcpy ( &grh->dgid, &av->gid, sizeof ( grh->dgid ) );
- }
-
- /* Construct BTH */
- bth->opcode = BTH_OPCODE_UD_SEND;
- bth->se__m__padcnt__tver = ( pad_len << 4 );
- bth->pkey = htons ( ibdev->pkey );
- bth->dest_qp = htonl ( av->qpn );
- bth->ack__psn = htonl ( ( ibdev->psn++ ) & 0xffffffUL );
-
- /* Construct DETH */
- deth->qkey = htonl ( av->qkey );
- deth->src_qp = htonl ( qp->qpn );
-
- DBGCP_HDA ( ibdev, 0, iobuf->data,
- ( iob_len ( iobuf ) - orig_iob_len ) );
-
- return 0;
-}
-
-/**
- * Remove IB headers
- *
- * @v ibdev Infiniband device
- * @v iobuf I/O buffer containing headers
- * @v qp Queue pair to fill in, or NULL
- * @v payload_len Payload length to fill in, or NULL
- * @v av Address vector to fill in
- */
-int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf,
- struct ib_queue_pair **qp, size_t *payload_len,
- struct ib_address_vector *av ) {
- struct ib_local_route_header *lrh;
- struct ib_global_route_header *grh;
- struct ib_base_transport_header *bth;
- struct ib_datagram_extended_transport_header *deth;
- size_t orig_iob_len = iob_len ( iobuf );
- unsigned int lnh;
- size_t pad_len;
- unsigned long qpn;
- unsigned int lid;
-
- /* Clear return values */
- if ( qp )
- *qp = NULL;
- if ( payload_len )
- *payload_len = 0;
- memset ( av, 0, sizeof ( *av ) );
-
- /* Extract LRH */
- if ( iob_len ( iobuf ) < sizeof ( *lrh ) ) {
- DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for LRH\n",
- ibdev, iob_len ( iobuf ) );
- return -EINVAL;
- }
- lrh = iobuf->data;
- iob_pull ( iobuf, sizeof ( *lrh ) );
- av->lid = ntohs ( lrh->slid );
- av->sl = ( lrh->sl__lnh >> 4 );
- lnh = ( lrh->sl__lnh & 0x3 );
- lid = ntohs ( lrh->dlid );
-
- /* Reject unsupported packets */
- if ( ! ( ( lnh == IB_LNH_BTH ) || ( lnh == IB_LNH_GRH ) ) ) {
- DBGC ( ibdev, "IBDEV %p RX unsupported LNH %x\n",
- ibdev, lnh );
- return -ENOTSUP;
- }
-
- /* Extract GRH, if present */
- if ( lnh == IB_LNH_GRH ) {
- if ( iob_len ( iobuf ) < sizeof ( *grh ) ) {
- DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) "
- "for GRH\n", ibdev, iob_len ( iobuf ) );
- return -EINVAL;
- }
- grh = iobuf->data;
- iob_pull ( iobuf, sizeof ( *grh ) );
- av->gid_present = 1;
- memcpy ( &av->gid, &grh->sgid, sizeof ( av->gid ) );
- } else {
- grh = NULL;
- }
-
- /* Extract BTH */
- if ( iob_len ( iobuf ) < sizeof ( *bth ) ) {
- DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for BTH\n",
- ibdev, iob_len ( iobuf ) );
- return -EINVAL;
- }
- bth = iobuf->data;
- iob_pull ( iobuf, sizeof ( *bth ) );
- if ( bth->opcode != BTH_OPCODE_UD_SEND ) {
- DBGC ( ibdev, "IBDEV %p unsupported BTH opcode %x\n",
- ibdev, bth->opcode );
- return -ENOTSUP;
- }
- qpn = ntohl ( bth->dest_qp );
-
- /* Extract DETH */
- if ( iob_len ( iobuf ) < sizeof ( *deth ) ) {
- DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for DETH\n",
- ibdev, iob_len ( iobuf ) );
- return -EINVAL;
- }
- deth = iobuf->data;
- iob_pull ( iobuf, sizeof ( *deth ) );
- av->qpn = ntohl ( deth->src_qp );
- av->qkey = ntohl ( deth->qkey );
-
- /* Calculate payload length, if applicable */
- if ( payload_len ) {
- pad_len = ( ( bth->se__m__padcnt__tver >> 4 ) & 0x3 );
- *payload_len = ( ( ntohs ( lrh->length ) << 2 )
- - ( orig_iob_len - iob_len ( iobuf ) )
- - pad_len - 4 /* ICRC */ );
- }
-
- /* Determine destination QP, if applicable */
- if ( qp ) {
- if ( IB_LID_MULTICAST ( lid ) && grh ) {
- *qp = ib_find_qp_mgid ( ibdev, &grh->dgid );
- } else {
- *qp = ib_find_qp_qpn ( ibdev, qpn );
- }
- if ( ! *qp ) {
- DBGC ( ibdev, "IBDEV %p RX for nonexistent QP\n",
- ibdev );
- return -ENODEV;
- }
- }
-
- DBGC2 ( ibdev, "IBDEV %p RX %04x:%08lx <= %04x:%08lx (key %08x)\n",
- ibdev, lid,
- ( IB_LID_MULTICAST( lid ) ? ( qp ? (*qp)->qpn : -1UL ) : qpn ),
- av->lid, av->qpn, ntohl ( deth->qkey ) );
- DBGCP_HDA ( ibdev, 0,
- ( iobuf->data - ( orig_iob_len - iob_len ( iobuf ) ) ),
- ( orig_iob_len - iob_len ( iobuf ) ) );
-
- return 0;
-}
diff --git a/gpxe/src/drivers/infiniband/ib_sma.c b/gpxe/src/drivers/infiniband/ib_sma.c
deleted file mode 100644
index 2bd3a9e8..00000000
--- a/gpxe/src/drivers/infiniband/ib_sma.c
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <byteswap.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/process.h>
-#include <gpxe/ib_sma.h>
-
-/**
- * @file
- *
- * Infiniband Subnet Management Agent
- *
- */
-
-/**
- * Get node information
- *
- * @v sma Subnet management agent
- * @v get Attribute to get
- */
-static void ib_sma_get_node_info ( struct ib_sma *sma,
- union ib_smp_data *get ) {
- struct ib_device *ibdev = sma->ibdev;
- struct ib_node_info *node_info = &get->node_info;
- struct ib_device *tmp;
-
- memset ( node_info, 0, sizeof ( *node_info ) );
- node_info->base_version = IB_MGMT_BASE_VERSION;
- node_info->class_version = IB_SMP_CLASS_VERSION;
- node_info->node_type = IB_NODE_TYPE_HCA;
- /* Search for IB devices with the same physical device to
- * identify port count and a suitable Node GUID.
- */
- for_each_ibdev ( tmp ) {
- if ( tmp->dev != ibdev->dev )
- continue;
- if ( node_info->num_ports == 0 ) {
- memcpy ( node_info->sys_guid, &tmp->gid.u.half[1],
- sizeof ( node_info->sys_guid ) );
- memcpy ( node_info->node_guid, &tmp->gid.u.half[1],
- sizeof ( node_info->node_guid ) );
- }
- node_info->num_ports++;
- }
- memcpy ( node_info->port_guid, &ibdev->gid.u.half[1],
- sizeof ( node_info->port_guid ) );
- node_info->partition_cap = htons ( 1 );
- node_info->local_port_num = ibdev->port;
-}
-
-/**
- * Get node description
- *
- * @v sma Subnet management agent
- * @v get Attribute to get
- */
-static void ib_sma_get_node_desc ( struct ib_sma *sma,
- union ib_smp_data *get ) {
- struct ib_device *ibdev = sma->ibdev;
- struct ib_node_desc *node_desc = &get->node_desc;
- struct ib_gid_half *guid = &ibdev->gid.u.half[1];
-
- memset ( node_desc, 0, sizeof ( *node_desc ) );
- snprintf ( node_desc->node_string, sizeof ( node_desc->node_string ),
- "gPXE %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x (%s)",
- guid->bytes[0], guid->bytes[1], guid->bytes[2],
- guid->bytes[3], guid->bytes[4], guid->bytes[5],
- guid->bytes[6], guid->bytes[7], ibdev->dev->name );
-}
-
-/**
- * Get GUID information
- *
- * @v sma Subnet management agent
- * @v get Attribute to get
- */
-static void ib_sma_get_guid_info ( struct ib_sma *sma,
- union ib_smp_data *get ) {
- struct ib_device *ibdev = sma->ibdev;
- struct ib_guid_info *guid_info = &get->guid_info;
-
- memset ( guid_info, 0, sizeof ( *guid_info ) );
- memcpy ( guid_info->guid[0], &ibdev->gid.u.half[1],
- sizeof ( guid_info->guid[0] ) );
-}
-
-/**
- * Get port information
- *
- * @v sma Subnet management agent
- * @v get Attribute to get
- */
-static void ib_sma_get_port_info ( struct ib_sma *sma,
- union ib_smp_data *get ) {
- struct ib_device *ibdev = sma->ibdev;
- struct ib_port_info *port_info = &get->port_info;
-
- memset ( port_info, 0, sizeof ( *port_info ) );
- memcpy ( port_info->gid_prefix, &ibdev->gid.u.half[0],
- sizeof ( port_info->gid_prefix ) );
- port_info->lid = ntohs ( ibdev->lid );
- port_info->mastersm_lid = ntohs ( ibdev->sm_lid );
- port_info->local_port_num = ibdev->port;
- port_info->link_width_enabled = ibdev->link_width;
- port_info->link_width_supported = ibdev->link_width;
- port_info->link_width_active = ibdev->link_width;
- port_info->link_speed_supported__port_state =
- ( ( ibdev->link_speed << 4 ) | ibdev->port_state );
- port_info->port_phys_state__link_down_def_state =
- ( ( IB_PORT_PHYS_STATE_POLLING << 4 ) |
- IB_PORT_PHYS_STATE_POLLING );
- port_info->link_speed_active__link_speed_enabled =
- ( ( ibdev->link_speed << 4 ) | ibdev->link_speed );
- port_info->neighbour_mtu__mastersm_sl =
- ( ( IB_MTU_2048 << 4 ) | ibdev->sm_sl );
- port_info->vl_cap__init_type = ( IB_VL_0 << 4 );
- port_info->init_type_reply__mtu_cap = IB_MTU_2048;
- port_info->operational_vls__enforcement = ( IB_VL_0 << 4 );
- port_info->guid_cap = 1;
-}
-
-/**
- * Set port information
- *
- * @v sma Subnet management agent
- * @v set Attribute to set
- * @ret rc Return status code
- */
-static int ib_sma_set_port_info ( struct ib_sma *sma,
- const union ib_smp_data *set ) {
- struct ib_device *ibdev = sma->ibdev;
- const struct ib_port_info *port_info = &set->port_info;
-
- memcpy ( &ibdev->gid.u.half[0], port_info->gid_prefix,
- sizeof ( ibdev->gid.u.half[0] ) );
- ibdev->lid = ntohs ( port_info->lid );
- ibdev->sm_lid = ntohs ( port_info->mastersm_lid );
- ibdev->sm_sl = ( port_info->neighbour_mtu__mastersm_sl & 0xf );
-
- if ( ! sma->op->set_port_info ) {
- /* Not an error; we just ignore all other settings */
- return 0;
- }
-
- return sma->op->set_port_info ( ibdev, port_info );
-}
-
-/**
- * Get partition key table
- *
- * @v sma Subnet management agent
- * @v get Attribute to get
- */
-static void ib_sma_get_pkey_table ( struct ib_sma *sma,
- union ib_smp_data *get ) {
- struct ib_device *ibdev = sma->ibdev;
- struct ib_pkey_table *pkey_table = &get->pkey_table;
-
- memset ( pkey_table, 0, sizeof ( *pkey_table ) );
- pkey_table->pkey[0] = htons ( ibdev->pkey );
-}
-
-/**
- * Set partition key table
- *
- * @v sma Subnet management agent
- * @v set Attribute to set
- */
-static int ib_sma_set_pkey_table ( struct ib_sma *sma,
- const union ib_smp_data *get ) {
- struct ib_device *ibdev = sma->ibdev;
- const struct ib_pkey_table *pkey_table = &get->pkey_table;
-
- ibdev->pkey = ntohs ( pkey_table->pkey[0] );
- return 0;
-}
-
-/** An attribute handler */
-struct ib_sma_handler {
- /** Attribute (in network byte order) */
- uint16_t attr_id;
- /** Get attribute
- *
- * @v sma Subnet management agent
- * @v get Attribute to get
- * @ret rc Return status code
- */
- void ( * get ) ( struct ib_sma *sma, union ib_smp_data *get );
- /** Set attribute
- *
- * @v sma Subnet management agent
- * @v set Attribute to set
- * @ret rc Return status code
- */
- int ( * set ) ( struct ib_sma *sma, const union ib_smp_data *set );
-};
-
-/** List of attribute handlers */
-static struct ib_sma_handler ib_sma_handlers[] = {
- { htons ( IB_SMP_ATTR_NODE_DESC ),
- ib_sma_get_node_desc, NULL },
- { htons ( IB_SMP_ATTR_NODE_INFO ),
- ib_sma_get_node_info, NULL },
- { htons ( IB_SMP_ATTR_GUID_INFO ),
- ib_sma_get_guid_info, NULL },
- { htons ( IB_SMP_ATTR_PORT_INFO ),
- ib_sma_get_port_info, ib_sma_set_port_info },
- { htons ( IB_SMP_ATTR_PKEY_TABLE ),
- ib_sma_get_pkey_table, ib_sma_set_pkey_table },
-};
-
-/**
- * Identify attribute handler
- *
- * @v attr_id Attribute ID (in network byte order)
- * @ret handler Attribute handler (or NULL)
- */
-static struct ib_sma_handler * ib_sma_handler ( uint16_t attr_id ) {
- struct ib_sma_handler *handler;
- unsigned int i;
-
- for ( i = 0 ; i < ( sizeof ( ib_sma_handlers ) /
- sizeof ( ib_sma_handlers[0] ) ) ; i++ ) {
- handler = &ib_sma_handlers[i];
- if ( handler->attr_id == attr_id )
- return handler;
- }
-
- return NULL;
-}
-
-/**
- * Respond to management datagram
- *
- * @v sma Subnet management agent
- * @v mad Management datagram
- * @ret rc Return status code
- */
-static int ib_sma_mad ( struct ib_sma *sma, union ib_mad *mad ) {
- struct ib_device *ibdev = sma->ibdev;
- struct ib_mad_hdr *hdr = &mad->hdr;
- struct ib_mad_smp *smp = &mad->smp;
- struct ib_sma_handler *handler = NULL;
- unsigned int hop_pointer;
- unsigned int hop_count;
- int rc;
-
- DBGC ( sma, "SMA %p received SMP with bv=%02x mc=%02x cv=%02x "
- "meth=%02x attr=%04x mod=%08x\n", sma, hdr->base_version,
- hdr->mgmt_class, hdr->class_version, hdr->method,
- ntohs ( hdr->attr_id ), ntohl ( hdr->attr_mod ) );
- DBGC2_HDA ( sma, 0, mad, sizeof ( *mad ) );
-
- /* Sanity checks */
- if ( hdr->base_version != IB_MGMT_BASE_VERSION ) {
- DBGC ( sma, "SMA %p unsupported base version %x\n",
- sma, hdr->base_version );
- return -ENOTSUP;
- }
- if ( ( hdr->mgmt_class != IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE ) &&
- ( hdr->mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED ) ) {
- DBGC ( sma, "SMA %p unsupported management class %x\n",
- sma, hdr->mgmt_class );
- return -ENOTSUP;
- }
- if ( hdr->class_version != IB_SMP_CLASS_VERSION ) {
- DBGC ( sma, "SMA %p unsupported class version %x\n",
- sma, hdr->class_version );
- return -ENOTSUP;
- }
- if ( ( hdr->method != IB_MGMT_METHOD_GET ) &&
- ( hdr->method != IB_MGMT_METHOD_SET ) ) {
- DBGC ( sma, "SMA %p unsupported method %x\n",
- sma, hdr->method );
- return -ENOTSUP;
- }
-
- /* Identify handler */
- if ( ! ( handler = ib_sma_handler ( hdr->attr_id ) ) ) {
- DBGC ( sma, "SMA %p unsupported attribute %x\n",
- sma, ntohs ( hdr->attr_id ) );
- hdr->status = htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR );
- goto respond_without_data;
- }
-
- /* Set attribute (if applicable) */
- if ( hdr->method != IB_MGMT_METHOD_SET ) {
- hdr->status = htons ( IB_MGMT_STATUS_OK );
- goto respond;
- }
- if ( ! handler->set ) {
- DBGC ( sma, "SMA %p attribute %x is unsettable\n",
- sma, ntohs ( hdr->attr_id ) );
- hdr->status = htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR );
- goto respond;
- }
- if ( ( rc = handler->set ( sma, &smp->smp_data ) ) != 0 ) {
- DBGC ( sma, "SMA %p could not set attribute %x: %s\n",
- sma, ntohs ( hdr->attr_id ), strerror ( rc ) );
- hdr->status = htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR );
- goto respond;
- }
-
- hdr->status = htons ( IB_MGMT_STATUS_OK );
-
- respond:
- /* Get attribute */
- handler->get ( sma, &smp->smp_data );
-
- respond_without_data:
-
- /* Set method to "Get Response" */
- hdr->method = IB_MGMT_METHOD_GET_RESP;
-
- /* Set response fields for directed route SMPs */
- if ( hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE ) {
- hdr->status |= htons ( IB_SMP_STATUS_D_INBOUND );
- hop_pointer = smp->mad_hdr.class_specific.smp.hop_pointer;
- hop_count = smp->mad_hdr.class_specific.smp.hop_count;
- assert ( hop_count == hop_pointer );
- if ( hop_pointer < ( sizeof ( smp->return_path.hops ) /
- sizeof ( smp->return_path.hops[0] ) ) ) {
- smp->return_path.hops[hop_pointer] = ibdev->port;
- } else {
- DBGC ( sma, "SMA %p invalid hop pointer %d\n",
- sma, hop_pointer );
- return -EINVAL;
- }
- }
-
- DBGC ( sma, "SMA %p responding with status=%04x\n",
- sma, ntohs ( hdr->status ) );
- DBGC2_HDA ( sma, 0, mad, sizeof ( *mad ) );
-
- return 0;
-}
-
-/**
- * Refill SMA receive ring
- *
- * @v sma Subnet management agent
- */
-static void ib_sma_refill_recv ( struct ib_sma *sma ) {
- struct ib_device *ibdev = sma->ibdev;
- struct io_buffer *iobuf;
- int rc;
-
- while ( sma->qp->recv.fill < IB_SMA_NUM_RECV_WQES ) {
-
- /* Allocate I/O buffer */
- iobuf = alloc_iob ( IB_SMA_PAYLOAD_LEN );
- if ( ! iobuf ) {
- /* Non-fatal; we will refill on next attempt */
- return;
- }
-
- /* Post I/O buffer */
- if ( ( rc = ib_post_recv ( ibdev, sma->qp, iobuf ) ) != 0 ) {
- DBGC ( sma, "SMA %p could not refill: %s\n",
- sma, strerror ( rc ) );
- free_iob ( iobuf );
- /* Give up */
- return;
- }
- }
-}
-
-/**
- * Complete SMA send
- *
- *
- * @v ibdev Infiniband device
- * @v qp Queue pair
- * @v iobuf I/O buffer
- * @v rc Completion status code
- */
-static void ib_sma_complete_send ( struct ib_device *ibdev __unused,
- struct ib_queue_pair *qp,
- struct io_buffer *iobuf, int rc ) {
- struct ib_sma *sma = ib_qp_get_ownerdata ( qp );
-
- if ( rc != 0 ) {
- DBGC ( sma, "SMA %p send completion error: %s\n",
- sma, strerror ( rc ) );
- }
- free_iob ( iobuf );
-}
-
-/**
- * Complete SMA receive
- *
- *
- * @v ibdev Infiniband device
- * @v qp Queue pair
- * @v av Address vector
- * @v iobuf I/O buffer
- * @v rc Completion status code
- */
-static void ib_sma_complete_recv ( struct ib_device *ibdev,
- struct ib_queue_pair *qp,
- struct ib_address_vector *av,
- struct io_buffer *iobuf, int rc ) {
- struct ib_sma *sma = ib_qp_get_ownerdata ( qp );
- union ib_mad *mad;
-
- /* Ignore errors */
- if ( rc != 0 ) {
- DBGC ( sma, "SMA %p RX error: %s\n", sma, strerror ( rc ) );
- goto err;
- }
-
- /* Sanity check */
- if ( iob_len ( iobuf ) != sizeof ( *mad ) ) {
- DBGC ( sma, "SMA %p RX bad size (%zd bytes)\n",
- sma, iob_len ( iobuf ) );
- goto err;
- }
- mad = iobuf->data;
-
- /* Construct MAD response */
- if ( ( rc = ib_sma_mad ( sma, mad ) ) != 0 ) {
- DBGC ( sma, "SMA %p could not construct MAD response: %s\n",
- sma, strerror ( rc ) );
- goto err;
- }
-
- /* Send MAD response */
- if ( ( rc = ib_post_send ( ibdev, qp, av, iobuf ) ) != 0 ) {
- DBGC ( sma, "SMA %p could not send MAD response: %s\n",
- sma, strerror ( rc ) );
- goto err;
- }
-
- return;
-
- err:
- free_iob ( iobuf );
-}
-
-/** SMA completion operations */
-static struct ib_completion_queue_operations ib_sma_completion_ops = {
- .complete_send = ib_sma_complete_send,
- .complete_recv = ib_sma_complete_recv,
-};
-
-/**
- * Poll SMA
- *
- * @v process Process
- */
-static void ib_sma_step ( struct process *process ) {
- struct ib_sma *sma =
- container_of ( process, struct ib_sma, poll );
- struct ib_device *ibdev = sma->ibdev;
-
- /* Poll the kernel completion queue */
- ib_poll_cq ( ibdev, sma->cq );
-
- /* Refill the receive ring */
- ib_sma_refill_recv ( sma );
-}
-
-/**
- * Create SMA
- *
- * @v sma Subnet management agent
- * @v ibdev Infiniband device
- * @v op Subnet management operations
- * @ret rc Return status code
- */
-int ib_create_sma ( struct ib_sma *sma, struct ib_device *ibdev,
- struct ib_sma_operations *op ) {
- int rc;
-
- /* Initialise fields */
- memset ( sma, 0, sizeof ( *sma ) );
- sma->ibdev = ibdev;
- sma->op = op;
- process_init ( &sma->poll, ib_sma_step, &ibdev->refcnt );
-
- /* Create completion queue */
- sma->cq = ib_create_cq ( ibdev, IB_SMA_NUM_CQES,
- &ib_sma_completion_ops );
- if ( ! sma->cq ) {
- rc = -ENOMEM;
- goto err_create_cq;
- }
-
- /* Create queue pair */
- sma->qp = ib_create_qp ( ibdev, IB_SMA_NUM_SEND_WQES, sma->cq,
- IB_SMA_NUM_RECV_WQES, sma->cq, 0 );
- if ( ! sma->qp ) {
- rc = -ENOMEM;
- goto err_create_qp;
- }
- ib_qp_set_ownerdata ( sma->qp, sma );
-
- /* If we don't get QP0, we can't function */
- if ( sma->qp->qpn != IB_QPN_SMP ) {
- DBGC ( sma, "SMA %p on QPN %lx, needs to be on QPN 0\n",
- sma, sma->qp->qpn );
- rc = -ENOTSUP;
- goto err_not_qp0;
- }
-
- /* Fill receive ring */
- ib_sma_refill_recv ( sma );
- return 0;
-
- err_not_qp0:
- ib_destroy_qp ( ibdev, sma->qp );
- err_create_qp:
- ib_destroy_cq ( ibdev, sma->cq );
- err_create_cq:
- process_del ( &sma->poll );
- return rc;
-}
-
-/**
- * Destroy SMA
- *
- * @v sma Subnet management agent
- */
-void ib_destroy_sma ( struct ib_sma *sma ) {
- struct ib_device *ibdev = sma->ibdev;
-
- ib_destroy_qp ( ibdev, sma->qp );
- ib_destroy_cq ( ibdev, sma->cq );
- process_del ( &sma->poll );
-}
diff --git a/gpxe/src/drivers/infiniband/ib_smc.c b/gpxe/src/drivers/infiniband/ib_smc.c
deleted file mode 100644
index af0c4ab9..00000000
--- a/gpxe/src/drivers/infiniband/ib_smc.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <byteswap.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/ib_smc.h>
-
-/**
- * @file
- *
- * Infiniband Subnet Management Client
- *
- */
-
-/**
- * Get port information
- *
- * @v ibdev Infiniband device
- * @v local_mad Method for issuing local MADs
- * @v mad Management datagram to fill in
- * @ret rc Return status code
- */
-static int ib_smc_get_port_info ( struct ib_device *ibdev,
- ib_local_mad_t local_mad,
- union ib_mad *mad ) {
- int rc;
-
- /* Construct MAD */
- memset ( mad, 0, sizeof ( *mad ) );
- mad->hdr.base_version = IB_MGMT_BASE_VERSION;
- mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
- mad->hdr.class_version = 1;
- mad->hdr.method = IB_MGMT_METHOD_GET;
- mad->hdr.attr_id = htons ( IB_SMP_ATTR_PORT_INFO );
- mad->hdr.attr_mod = htonl ( ibdev->port );
-
- if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) {
- DBGC ( ibdev, "IBDEV %p could not get port info: %s\n",
- ibdev, strerror ( rc ) );
- return rc;
- }
- return 0;
-}
-
-/**
- * Get GUID information
- *
- * @v ibdev Infiniband device
- * @v local_mad Method for issuing local MADs
- * @v mad Management datagram to fill in
- * @ret rc Return status code
- */
-static int ib_smc_get_guid_info ( struct ib_device *ibdev,
- ib_local_mad_t local_mad,
- union ib_mad *mad ) {
- int rc;
-
- /* Construct MAD */
- memset ( mad, 0, sizeof ( *mad ) );
- mad->hdr.base_version = IB_MGMT_BASE_VERSION;
- mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
- mad->hdr.class_version = 1;
- mad->hdr.method = IB_MGMT_METHOD_GET;
- mad->hdr.attr_id = htons ( IB_SMP_ATTR_GUID_INFO );
-
- if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) {
- DBGC ( ibdev, "IBDEV %p could not get GUID info: %s\n",
- ibdev, strerror ( rc ) );
- return rc;
- }
- return 0;
-}
-
-/**
- * Get partition key table
- *
- * @v ibdev Infiniband device
- * @v local_mad Method for issuing local MADs
- * @v mad Management datagram to fill in
- * @ret rc Return status code
- */
-static int ib_smc_get_pkey_table ( struct ib_device *ibdev,
- ib_local_mad_t local_mad,
- union ib_mad *mad ) {
- int rc;
-
- /* Construct MAD */
- memset ( mad, 0, sizeof ( *mad ) );
- mad->hdr.base_version = IB_MGMT_BASE_VERSION;
- mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
- mad->hdr.class_version = 1;
- mad->hdr.method = IB_MGMT_METHOD_GET;
- mad->hdr.attr_id = htons ( IB_SMP_ATTR_PKEY_TABLE );
-
- if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) {
- DBGC ( ibdev, "IBDEV %p could not get pkey table: %s\n",
- ibdev, strerror ( rc ) );
- return rc;
- }
- return 0;
-}
-
-/**
- * Get MAD parameters
- *
- * @v ibdev Infiniband device
- * @v local_mad Method for issuing local MADs
- * @ret rc Return status code
- */
-int ib_smc_update ( struct ib_device *ibdev, ib_local_mad_t local_mad ) {
- union ib_mad mad;
- union ib_smp_data *smp = &mad.smp.smp_data;
- int rc;
-
- /* Port info gives us the link state, the first half of the
- * port GID and the SM LID.
- */
- if ( ( rc = ib_smc_get_port_info ( ibdev, local_mad, &mad ) ) != 0 )
- return rc;
- ibdev->port_state =
- ( smp->port_info.link_speed_supported__port_state & 0x0f );
- memcpy ( &ibdev->gid.u.half[0], smp->port_info.gid_prefix,
- sizeof ( ibdev->gid.u.half[0] ) );
- ibdev->lid = ntohs ( smp->port_info.lid );
- ibdev->sm_lid = ntohs ( smp->port_info.mastersm_lid );
- ibdev->sm_sl = ( smp->port_info.neighbour_mtu__mastersm_sl & 0xf );
-
- /* GUID info gives us the second half of the port GID */
- if ( ( rc = ib_smc_get_guid_info ( ibdev, local_mad, &mad ) ) != 0 )
- return rc;
- memcpy ( &ibdev->gid.u.half[1], smp->guid_info.guid[0],
- sizeof ( ibdev->gid.u.half[1] ) );
-
- /* Get partition key */
- if ( ( rc = ib_smc_get_pkey_table ( ibdev, local_mad, &mad ) ) != 0 )
- return rc;
- ibdev->pkey = ntohs ( smp->pkey_table.pkey[0] );
-
- DBGC ( ibdev, "IBDEV %p port GID is %08x:%08x:%08x:%08x\n", ibdev,
- htonl ( ibdev->gid.u.dwords[0] ),
- htonl ( ibdev->gid.u.dwords[1] ),
- htonl ( ibdev->gid.u.dwords[2] ),
- htonl ( ibdev->gid.u.dwords[3] ) );
-
- return 0;
-}
diff --git a/gpxe/src/drivers/infiniband/linda.c b/gpxe/src/drivers/infiniband/linda.c
index c5d13177..b9a7ba58 100644
--- a/gpxe/src/drivers/infiniband/linda.c
+++ b/gpxe/src/drivers/infiniband/linda.c
@@ -16,6 +16,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+FILE_LICENCE ( GPL2_OR_LATER );
+
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
@@ -28,7 +30,6 @@
#include <gpxe/bitbash.h>
#include <gpxe/malloc.h>
#include <gpxe/iobuf.h>
-#include <gpxe/ib_sma.h>
#include "linda.h"
/**
@@ -95,9 +96,6 @@ struct linda {
struct i2c_bit_basher i2c;
/** I2C serial EEPROM */
struct i2c_device eeprom;
-
- /** Subnet management agent */
- struct ib_sma sma;
};
/***************************************************************************
@@ -233,22 +231,48 @@ static void linda_link_state_changed ( struct ib_device *ibdev ) {
/* Notify Infiniband core of link state change */
ibdev->port_state = ( link_state + 1 );
- ibdev->link_width =
+ ibdev->link_width_active =
( link_width ? IB_LINK_WIDTH_4X : IB_LINK_WIDTH_1X );
- ibdev->link_speed =
+ ibdev->link_speed_active =
( link_speed ? IB_LINK_SPEED_DDR : IB_LINK_SPEED_SDR );
ib_link_state_changed ( ibdev );
}
/**
+ * Wait for link state change to take effect
+ *
+ * @v linda Linda device
+ * @v new_link_state Expected link state
+ * @ret rc Return status code
+ */
+static int linda_link_state_check ( struct linda *linda,
+ unsigned int new_link_state ) {
+ struct QIB_7220_IBCStatus ibcstatus;
+ unsigned int link_state;
+ unsigned int i;
+
+ for ( i = 0 ; i < LINDA_LINK_STATE_MAX_WAIT_US ; i++ ) {
+ linda_readq ( linda, &ibcstatus, QIB_7220_IBCStatus_offset );
+ link_state = BIT_GET ( &ibcstatus, LinkState );
+ if ( link_state == new_link_state )
+ return 0;
+ udelay ( 1 );
+ }
+
+ DBGC ( linda, "Linda %p timed out waiting for link state %s\n",
+ linda, linda_link_state_text ( link_state ) );
+ return -ETIMEDOUT;
+}
+
+/**
* Set port information
*
* @v ibdev Infiniband device
- * @v port_info New port information
+ * @v mad Set port information MAD
*/
-static int linda_set_port_info ( struct ib_device *ibdev,
- const struct ib_port_info *port_info ) {
+static int linda_set_port_info ( struct ib_device *ibdev, union ib_mad *mad ) {
struct linda *linda = ib_get_drvdata ( ibdev );
+ struct ib_port_info *port_info = &mad->smp.smp_data.port_info;
struct QIB_7220_IBCCtrl ibcctrl;
unsigned int port_state;
unsigned int link_state;
@@ -262,6 +286,12 @@ static int linda_set_port_info ( struct ib_device *ibdev,
linda_readq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset );
BIT_SET ( &ibcctrl, LinkCmd, link_state );
linda_writeq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset );
+
+ /* Wait for link state change to take effect. Ignore
+ * errors; the current link state will be returned via
+ * the GetResponse MAD.
+ */
+ linda_link_state_check ( linda, link_state );
}
/* Detect and report link state change */
@@ -270,10 +300,17 @@ static int linda_set_port_info ( struct ib_device *ibdev,
return 0;
}
-/** Linda subnet management operations */
-static struct ib_sma_operations linda_sma_operations = {
- .set_port_info = linda_set_port_info,
-};
+/**
+ * Set partition key table
+ *
+ * @v ibdev Infiniband device
+ * @v mad Set partition key table MAD
+ */
+static int linda_set_pkey_table ( struct ib_device *ibdev __unused,
+ union ib_mad *mad __unused ) {
+ /* Nothing to do */
+ return 0;
+}
/***************************************************************************
*
@@ -859,12 +896,10 @@ static int linda_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 linda_modify_qp ( struct ib_device *ibdev,
- struct ib_queue_pair *qp,
- unsigned long mod_list __unused ) {
+ struct ib_queue_pair *qp ) {
struct linda *linda = ib_get_drvdata ( ibdev );
/* Nothing to do; the hardware doesn't have a notion of queue
@@ -1462,6 +1497,8 @@ static struct ib_device_operations linda_ib_operations = {
.close = linda_close,
.mcast_attach = linda_mcast_attach,
.mcast_detach = linda_mcast_detach,
+ .set_port_info = linda_set_port_info,
+ .set_pkey_table = linda_set_pkey_table,
};
/***************************************************************************
@@ -1601,15 +1638,15 @@ static int linda_read_eeprom ( struct linda *linda,
/* Read GUID */
if ( ( rc = i2c->read ( i2c, &linda->eeprom, LINDA_EEPROM_GUID_OFFSET,
- guid->bytes, sizeof ( *guid ) ) ) != 0 ) {
+ guid->u.bytes, sizeof ( *guid ) ) ) != 0 ) {
DBGC ( linda, "Linda %p could not read GUID: %s\n",
linda, strerror ( rc ) );
return rc;
}
DBGC2 ( linda, "Linda %p has GUID %02x:%02x:%02x:%02x:%02x:%02x:"
- "%02x:%02x\n", linda, guid->bytes[0], guid->bytes[1],
- guid->bytes[2], guid->bytes[3], guid->bytes[4],
- guid->bytes[5], guid->bytes[6], guid->bytes[7] );
+ "%02x:%02x\n", linda, guid->u.bytes[0], guid->u.bytes[1],
+ guid->u.bytes[2], guid->u.bytes[3], guid->u.bytes[4],
+ guid->u.bytes[5], guid->u.bytes[6], guid->u.bytes[7] );
/* Read serial number (debug only) */
if ( DBG_LOG ) {
@@ -2219,7 +2256,7 @@ static int linda_init_ib_serdes ( struct linda *linda ) {
linda_writeq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset );
/* Force SDR only to avoid needing all the DDR tuning,
- * Mellanox compatibiltiy hacks etc. SDR is plenty for
+ * Mellanox compatibility hacks etc. SDR is plenty for
* boot-time operation.
*/
linda_readq ( linda, &ibcddrctrl, QIB_7220_IBCDDRCtrl_offset );
@@ -2317,6 +2354,14 @@ static int linda_probe ( struct pci_device *pci,
BIT_GET ( &revision, R_ChipRevMajor ),
BIT_GET ( &revision, R_ChipRevMinor ) );
+ /* Record link capabilities. Note that we force SDR only to
+ * avoid having to carry extra code for DDR tuning etc.
+ */
+ ibdev->link_width_enabled = ibdev->link_width_supported =
+ ( IB_LINK_WIDTH_4X | IB_LINK_WIDTH_1X );
+ ibdev->link_speed_enabled = ibdev->link_speed_supported =
+ IB_LINK_SPEED_SDR;
+
/* Initialise I2C subsystem */
if ( ( rc = linda_init_i2c ( linda ) ) != 0 )
goto err_init_i2c;
@@ -2337,13 +2382,6 @@ static int linda_probe ( struct pci_device *pci,
if ( ( rc = linda_init_ib_serdes ( linda ) ) != 0 )
goto err_init_ib_serdes;
- /* Create the SMA */
- if ( ( rc = ib_create_sma ( &linda->sma, ibdev,
- &linda_sma_operations ) ) != 0 )
- goto err_create_sma;
- /* If the SMA doesn't get context 0, we're screwed */
- assert ( linda_qpn_to_ctx ( linda->sma.qp->qpn ) == 0 );
-
/* Register Infiniband device */
if ( ( rc = register_ibdev ( ibdev ) ) != 0 ) {
DBGC ( linda, "Linda %p could not register IB "
@@ -2355,8 +2393,6 @@ static int linda_probe ( struct pci_device *pci,
unregister_ibdev ( ibdev );
err_register_ibdev:
- ib_destroy_sma ( &linda->sma );
- err_create_sma:
linda_fini_recv ( linda );
err_init_recv:
linda_fini_send ( linda );
@@ -2379,14 +2415,13 @@ static void linda_remove ( struct pci_device *pci ) {
struct linda *linda = ib_get_drvdata ( ibdev );
unregister_ibdev ( ibdev );
- ib_destroy_sma ( &linda->sma );
linda_fini_recv ( linda );
linda_fini_send ( linda );
ibdev_put ( ibdev );
}
static struct pci_device_id linda_nics[] = {
- PCI_ROM ( 0x1077, 0x7220, "iba7220", "QLE7240/7280 HCA driver" ),
+ PCI_ROM ( 0x1077, 0x7220, "iba7220", "QLE7240/7280 HCA driver", 0 ),
};
struct pci_driver linda_driver __pci_driver = {
diff --git a/gpxe/src/drivers/infiniband/linda.h b/gpxe/src/drivers/infiniband/linda.h
index dd1737a6..3068421b 100644
--- a/gpxe/src/drivers/infiniband/linda.h
+++ b/gpxe/src/drivers/infiniband/linda.h
@@ -19,6 +19,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+FILE_LICENCE ( GPL2_OR_LATER );
+
/**
* @file
*
@@ -268,4 +270,7 @@ enum linda_link_state {
LINDA_LINK_STATE_ACT_DEFER = 4,
};
+/** Maximum time to wait for link state changes, in us */
+#define LINDA_LINK_STATE_MAX_WAIT_US 20
+
#endif /* _LINDA_H */
diff --git a/gpxe/src/drivers/infiniband/linda_fw.c b/gpxe/src/drivers/infiniband/linda_fw.c
index fc5ea077..968a5f8d 100644
--- a/gpxe/src/drivers/infiniband/linda_fw.c
+++ b/gpxe/src/drivers/infiniband/linda_fw.c
@@ -30,6 +30,8 @@
* SOFTWARE.
*/
+FILE_LICENCE ( GPL2_ONLY );
+
/*
* This file contains the memory image from the vendor, to be copied into
* the IB SERDES of the IBA7220 during initialization.
diff --git a/gpxe/src/drivers/infiniband/mlx_bitops.h b/gpxe/src/drivers/infiniband/mlx_bitops.h
index ec57d7b0..71a9bf1e 100644
--- a/gpxe/src/drivers/infiniband/mlx_bitops.h
+++ b/gpxe/src/drivers/infiniband/mlx_bitops.h
@@ -19,6 +19,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+FILE_LICENCE ( GPL2_OR_LATER );
+
/**
* @file
*
@@ -104,6 +106,10 @@ typedef unsigned char pseudo_bit_t;
( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \
MLX_ASSEMBLE_5 ( _structure_st, _index, __VA_ARGS__ ) )
+#define MLX_ASSEMBLE_7( _structure_st, _index, _field, _value, ... ) \
+ ( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \
+ MLX_ASSEMBLE_6 ( _structure_st, _index, __VA_ARGS__ ) )
+
/*
* Build native-endian (positive) dword bitmasks from named fields
*
@@ -133,6 +139,10 @@ typedef unsigned char pseudo_bit_t;
( MLX_MASK_1 ( _structure_st, _index, _field ) | \
MLX_MASK_5 ( _structure_st, _index, __VA_ARGS__ ) )
+#define MLX_MASK_7( _structure_st, _index, _field, ... ) \
+ ( MLX_MASK_1 ( _structure_st, _index, _field ) | \
+ MLX_MASK_6 ( _structure_st, _index, __VA_ARGS__ ) )
+
/*
* Populate big-endian dwords from named fields and values
*
@@ -169,6 +179,10 @@ typedef unsigned char pseudo_bit_t;
MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_6 ( MLX_PSEUDO_STRUCT ( _ptr ),\
_index, __VA_ARGS__ ) )
+#define MLX_FILL_7( _ptr, _index, ... ) \
+ MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_7 ( MLX_PSEUDO_STRUCT ( _ptr ),\
+ _index, __VA_ARGS__ ) )
+
/*
* Modify big-endian dword using named field and value
*
diff --git a/gpxe/src/drivers/infiniband/qib_7220_regs.h b/gpxe/src/drivers/infiniband/qib_7220_regs.h
index 0dd3c53d..0637ec80 100644
--- a/gpxe/src/drivers/infiniband/qib_7220_regs.h
+++ b/gpxe/src/drivers/infiniband/qib_7220_regs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 QLogic Corporation. All rights reserved.
+ * Copyright (c) 2008, 2009 QLogic Corporation. All rights reserved.
*
*
* This software is available to you under a choice of one of two
@@ -30,12 +30,12 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
- * This file is mechanically generated. Any hand-edits will be lost.
- * If not now, soon.
*/
+/* This file is mechanically generated from RTL. Any hand-edits will be lost! */
/* This file has been further processed by ./drivers/infiniband/qib_genbits.pl */
+FILE_LICENCE ( GPL2_ONLY );
#define QIB_7220_Revision_offset 0x00000000UL
struct QIB_7220_Revision_pb {
@@ -44,8 +44,8 @@ struct QIB_7220_Revision_pb {
pseudo_bit_t R_Arch[8];
pseudo_bit_t R_SW[8];
pseudo_bit_t BoardID[8];
- pseudo_bit_t R_Palldium_Revcode[22];
- pseudo_bit_t R_Palladium[1];
+ pseudo_bit_t R_Emulation_Revcode[22];
+ pseudo_bit_t R_Emulation[1];
pseudo_bit_t R_Simulator[1];
};
struct QIB_7220_Revision {
@@ -1250,7 +1250,6 @@ struct QIB_7220_SendDmaReqTagUsed_pb {
pseudo_bit_t ReqTagUsed_7_0[8];
pseudo_bit_t _unused_0[8];
pseudo_bit_t Reserved[48];
- pseudo_bit_t _unused_1[8];
};
struct QIB_7220_SendDmaReqTagUsed {
PSEUDO_BIT_STRUCT ( struct QIB_7220_SendDmaReqTagUsed_pb );
diff --git a/gpxe/src/drivers/infiniband/qib_genbits.pl b/gpxe/src/drivers/infiniband/qib_genbits.pl
index 9eba4da5..1d5eeded 100644..100755
--- a/gpxe/src/drivers/infiniband/qib_genbits.pl
+++ b/gpxe/src/drivers/infiniband/qib_genbits.pl
@@ -20,6 +20,7 @@ use strict;
use warnings;
my $offsets = {};
+my $defaults = {};
my $structures = {};
my $structure = "";
@@ -28,8 +29,12 @@ while ( <> ) {
if ( /^\#define (\S+)_OFFS (\S+)$/ ) {
$structure = $1;
$offsets->{$structure} = $2;
+ } elsif ( /^\#define ${structure}_DEF (\S+)$/ ) {
+ $defaults->{$structure} = $1;
} elsif ( /^\#define ${structure}_(\S+)_LSB (\S+)$/ ) {
$structures->{$structure}->{$1}->{LSB} = $2;
+ } elsif ( /^\#define ${structure}_(\S+)_MSB (\S+)$/ ) {
+ $structures->{$structure}->{$1}->{MSB} = $2;
} elsif ( /^\#define ${structure}_(\S+)_RMASK (\S+)$/ ) {
$structures->{$structure}->{$1}->{RMASK} = $2;
} elsif ( /^\s*$/ ) {
@@ -39,7 +44,8 @@ while ( <> ) {
}
}
-my $data = [ map { { name => $_, offset => $offsets->{$_} }; }
+my $data = [ map { { name => $_, offset => $offsets->{$_},
+ default => $defaults->{$_} }; }
sort { hex ( $offsets->{$a} ) <=> hex ( $offsets->{$b} ) }
keys %$offsets ];
@@ -47,6 +53,7 @@ foreach my $datum ( @$data ) {
next unless exists $structures->{$datum->{name}};
$structure = $structures->{$datum->{name}};
my $fields = [ map { { name => $_, lsb => $structure->{$_}->{LSB},
+ msb => $structure->{$_}->{MSB},
rmask => $structure->{$_}->{RMASK} }; }
sort { hex ( $structure->{$a}->{LSB} ) <=>
hex ( $structure->{$b}->{LSB} ) }
@@ -54,7 +61,8 @@ foreach my $datum ( @$data ) {
$datum->{fields} = $fields;
}
-print "\n/* This file has been further processed by $0 */\n\n\n";
+print "\n/* This file has been further processed by $0 */\n\n";
+print "FILE_LICENCE ( GPL2_ONLY );\n\n";
foreach my $datum ( @$data ) {
printf "#define %s_offset 0x%08xUL\n",
@@ -65,11 +73,15 @@ foreach my $datum ( @$data ) {
printf "struct %s_pb {\n", $datum->{name};
foreach my $field ( @{$datum->{fields}} ) {
my $pad_width = ( hex ( $field->{lsb} ) - $lsb );
- die "Inconsistent LSB/RMASK in $datum->{name}\n" if $pad_width < 0;
+ die "Inconsistent LSB/RMASK in $datum->{name} before $field->{name}\n"
+ if $pad_width < 0;
printf "\tpseudo_bit_t _unused_%u[%u];\n", $reserved_idx++, $pad_width
if $pad_width;
+ $lsb += $pad_width;
# Damn Perl can't cope with 64-bit hex constants
my $width = 0;
+ die "Missing RMASK in $datum->{name}.$field->{name}\n"
+ unless defined $field->{rmask};
my $rmask = $field->{rmask};
while ( $rmask =~ /^(0x.+)f$/i ) {
$width += 4;
@@ -80,16 +92,25 @@ foreach my $datum ( @$data ) {
$width++;
$rmask >>= 1;
}
+ if ( defined $field->{msb} ) {
+ my $msb_width = ( hex ( $field->{msb} ) - $lsb + 1 );
+ $width ||= $msb_width;
+ die "Inconsistent LSB/MSB/RMASK in $datum->{name}.$field->{name}\n"
+ unless $width == $msb_width;
+ }
printf "\tpseudo_bit_t %s[%u];\n", $field->{name}, $width;
$lsb += $width;
}
my $pad_width = ( 64 - $lsb );
- die "Inconsistent LSB/RMASK in $datum->{name}\n" if $pad_width < 0;
+ die "Inconsistent LSB/RMASK in $datum->{name} final field\n"
+ if $pad_width < 0;
printf "\tpseudo_bit_t _unused_%u[%u];\n", $reserved_idx++, $pad_width
if $pad_width;
printf "};\n";
printf "struct %s {\n\tPSEUDO_BIT_STRUCT ( struct %s_pb );\n};\n",
$datum->{name}, $datum->{name};
}
+ printf "/* Default value: %s */\n", $datum->{default}
+ if defined $datum->{default};
print "\n";
}