david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[infiniband] Flush uncompleted work queue entries at QP teardown

Avoid leaking I/O buffers in ib_destroy_qp() by completing any
outstanding work queue entries with a generic error code.  This
requires the completion handlers to be available to ib_destroy_qp(),
which is done by making them static configuration parameters of the CQ
(set by ib_create_cq()) rather than being provided on each call to
ib_poll_cq().

This mimics the functionality of netdev_{tx,rx}_flush().  The netdev
flush functions would previously have been catching any I/O buffers
leaked by the IPoIB data queue (though not by the IPoIB metadata
queue).
This commit is contained in:
Michael Brown 2008-10-03 00:07:52 +01:00
parent dd34500188
commit d9751edafa
5 changed files with 134 additions and 73 deletions

View File

@ -1106,15 +1106,11 @@ static int arbel_post_recv ( struct ib_device *ibdev,
* @v ibdev Infiniband device * @v ibdev Infiniband device
* @v cq Completion queue * @v cq Completion queue
* @v cqe Hardware completion queue entry * @v cqe Hardware completion queue entry
* @v complete_send Send completion handler
* @v complete_recv Receive completion handler
* @ret rc Return status code * @ret rc Return status code
*/ */
static int arbel_complete ( struct ib_device *ibdev, static int arbel_complete ( struct ib_device *ibdev,
struct ib_completion_queue *cq, struct ib_completion_queue *cq,
union arbelprm_completion_entry *cqe, union arbelprm_completion_entry *cqe ) {
ib_completer_t complete_send,
ib_completer_t complete_recv ) {
struct arbel *arbel = ib_get_drvdata ( ibdev ); struct arbel *arbel = ib_get_drvdata ( ibdev );
struct ib_completion completion; struct ib_completion completion;
struct ib_work_queue *wq; struct ib_work_queue *wq;
@ -1124,7 +1120,6 @@ static int arbel_complete ( struct ib_device *ibdev,
struct arbel_recv_work_queue *arbel_recv_wq; struct arbel_recv_work_queue *arbel_recv_wq;
struct arbelprm_recv_wqe *recv_wqe; struct arbelprm_recv_wqe *recv_wqe;
struct io_buffer *iobuf; struct io_buffer *iobuf;
ib_completer_t complete;
unsigned int opcode; unsigned int opcode;
unsigned long qpn; unsigned long qpn;
int is_send; int is_send;
@ -1201,8 +1196,11 @@ static int arbel_complete ( struct ib_device *ibdev,
} }
/* Pass off to caller's completion handler */ /* Pass off to caller's completion handler */
complete = ( is_send ? complete_send : complete_recv ); if ( is_send ) {
complete ( ibdev, qp, &completion, iobuf ); ib_complete_send ( ibdev, qp, &completion, iobuf );
} else {
ib_complete_recv ( ibdev, qp, &completion, iobuf );
}
return rc; return rc;
} }
@ -1212,13 +1210,9 @@ static int arbel_complete ( struct ib_device *ibdev,
* *
* @v ibdev Infiniband device * @v ibdev Infiniband device
* @v cq Completion queue * @v cq Completion queue
* @v complete_send Send completion handler
* @v complete_recv Receive completion handler
*/ */
static void arbel_poll_cq ( struct ib_device *ibdev, static void arbel_poll_cq ( struct ib_device *ibdev,
struct ib_completion_queue *cq, struct ib_completion_queue *cq ) {
ib_completer_t complete_send,
ib_completer_t complete_recv ) {
struct arbel *arbel = ib_get_drvdata ( ibdev ); struct arbel *arbel = ib_get_drvdata ( ibdev );
struct arbel_completion_queue *arbel_cq = ib_cq_get_drvdata ( cq ); struct arbel_completion_queue *arbel_cq = ib_cq_get_drvdata ( cq );
struct arbelprm_cq_ci_db_record *ci_db_rec; struct arbelprm_cq_ci_db_record *ci_db_rec;
@ -1236,8 +1230,7 @@ static void arbel_poll_cq ( struct ib_device *ibdev,
} }
/* Handle completion */ /* Handle completion */
if ( ( rc = arbel_complete ( ibdev, cq, cqe, complete_send, if ( ( rc = arbel_complete ( ibdev, cq, cqe ) ) != 0 ) {
complete_recv ) ) != 0 ) {
DBGC ( arbel, "Arbel %p failed to complete: %s\n", DBGC ( arbel, "Arbel %p failed to complete: %s\n",
arbel, strerror ( rc ) ); arbel, strerror ( rc ) );
DBGC_HD ( arbel, cqe, sizeof ( *cqe ) ); DBGC_HD ( arbel, cqe, sizeof ( *cqe ) );

View File

@ -1101,22 +1101,17 @@ static int hermon_post_recv ( struct ib_device *ibdev,
* @v ibdev Infiniband device * @v ibdev Infiniband device
* @v cq Completion queue * @v cq Completion queue
* @v cqe Hardware completion queue entry * @v cqe Hardware completion queue entry
* @v complete_send Send completion handler
* @v complete_recv Receive completion handler
* @ret rc Return status code * @ret rc Return status code
*/ */
static int hermon_complete ( struct ib_device *ibdev, static int hermon_complete ( struct ib_device *ibdev,
struct ib_completion_queue *cq, struct ib_completion_queue *cq,
union hermonprm_completion_entry *cqe, union hermonprm_completion_entry *cqe ) {
ib_completer_t complete_send,
ib_completer_t complete_recv ) {
struct hermon *hermon = ib_get_drvdata ( ibdev ); struct hermon *hermon = ib_get_drvdata ( ibdev );
struct ib_completion completion; struct ib_completion completion;
struct ib_work_queue *wq; struct ib_work_queue *wq;
struct ib_queue_pair *qp; struct ib_queue_pair *qp;
struct hermon_queue_pair *hermon_qp; struct hermon_queue_pair *hermon_qp;
struct io_buffer *iobuf; struct io_buffer *iobuf;
ib_completer_t complete;
unsigned int opcode; unsigned int opcode;
unsigned long qpn; unsigned long qpn;
int is_send; int is_send;
@ -1172,8 +1167,11 @@ static int hermon_complete ( struct ib_device *ibdev,
} }
/* Pass off to caller's completion handler */ /* Pass off to caller's completion handler */
complete = ( is_send ? complete_send : complete_recv ); if ( is_send ) {
complete ( ibdev, qp, &completion, iobuf ); ib_complete_send ( ibdev, qp, &completion, iobuf );
} else {
ib_complete_recv ( ibdev, qp, &completion, iobuf );
}
return rc; return rc;
} }
@ -1183,13 +1181,9 @@ static int hermon_complete ( struct ib_device *ibdev,
* *
* @v ibdev Infiniband device * @v ibdev Infiniband device
* @v cq Completion queue * @v cq Completion queue
* @v complete_send Send completion handler
* @v complete_recv Receive completion handler
*/ */
static void hermon_poll_cq ( struct ib_device *ibdev, static void hermon_poll_cq ( struct ib_device *ibdev,
struct ib_completion_queue *cq, struct ib_completion_queue *cq ) {
ib_completer_t complete_send,
ib_completer_t complete_recv ) {
struct hermon *hermon = ib_get_drvdata ( ibdev ); struct hermon *hermon = ib_get_drvdata ( ibdev );
struct hermon_completion_queue *hermon_cq = ib_cq_get_drvdata ( cq ); struct hermon_completion_queue *hermon_cq = ib_cq_get_drvdata ( cq );
union hermonprm_completion_entry *cqe; union hermonprm_completion_entry *cqe;
@ -1209,8 +1203,7 @@ static void hermon_poll_cq ( struct ib_device *ibdev,
DBGCP_HD ( hermon, cqe, sizeof ( *cqe ) ); DBGCP_HD ( hermon, cqe, sizeof ( *cqe ) );
/* Handle completion */ /* Handle completion */
if ( ( rc = hermon_complete ( ibdev, cq, cqe, complete_send, if ( ( rc = hermon_complete ( ibdev, cq, cqe ) ) != 0 ) {
complete_recv ) ) != 0 ) {
DBGC ( hermon, "Hermon %p failed to complete: %s\n", DBGC ( hermon, "Hermon %p failed to complete: %s\n",
hermon, strerror ( rc ) ); hermon, strerror ( rc ) );
DBGC_HD ( hermon, cqe, sizeof ( *cqe ) ); DBGC_HD ( hermon, cqe, sizeof ( *cqe ) );

View File

@ -278,13 +278,21 @@ static void ipoib_destroy_qset ( struct ipoib_device *ipoib,
* *
* @v ipoib IPoIB device * @v ipoib IPoIB device
* @v qset Queue set * @v qset Queue set
* @v num_cqes Number of completion queue entries
* @v num_send_wqes Number of send work queue entries
* @v complete_send Send completion handler
* @v num_recv_wqes Number of receive work queue entries
* @v complete_recv Receive completion handler
* @v qkey Queue key
* @ret rc Return status code * @ret rc Return status code
*/ */
static int ipoib_create_qset ( struct ipoib_device *ipoib, static int ipoib_create_qset ( struct ipoib_device *ipoib,
struct ipoib_queue_set *qset, struct ipoib_queue_set *qset,
unsigned int num_cqes, unsigned int num_cqes,
unsigned int num_send_wqes, unsigned int num_send_wqes,
ib_completer_t complete_send,
unsigned int num_recv_wqes, unsigned int num_recv_wqes,
ib_completer_t complete_recv,
unsigned long qkey ) { unsigned long qkey ) {
struct ib_device *ibdev = ipoib->ibdev; struct ib_device *ibdev = ipoib->ibdev;
int rc; int rc;
@ -297,7 +305,8 @@ static int ipoib_create_qset ( struct ipoib_device *ipoib,
qset->recv_max_fill = num_recv_wqes; qset->recv_max_fill = num_recv_wqes;
/* Allocate completion queue */ /* Allocate completion queue */
qset->cq = ib_create_cq ( ibdev, num_cqes ); qset->cq = ib_create_cq ( ibdev, num_cqes, complete_send,
complete_recv );
if ( ! qset->cq ) { if ( ! qset->cq ) {
DBGC ( ipoib, "IPoIB %p could not allocate completion queue\n", DBGC ( ipoib, "IPoIB %p could not allocate completion queue\n",
ipoib ); ipoib );
@ -759,10 +768,8 @@ static void ipoib_poll ( struct net_device *netdev ) {
struct ipoib_device *ipoib = netdev->priv; struct ipoib_device *ipoib = netdev->priv;
struct ib_device *ibdev = ipoib->ibdev; struct ib_device *ibdev = ipoib->ibdev;
ib_poll_cq ( ibdev, ipoib->meta.cq, ipoib_meta_complete_send, ib_poll_cq ( ibdev, ipoib->meta.cq );
ipoib_meta_complete_recv ); ib_poll_cq ( ibdev, ipoib->data.cq );
ib_poll_cq ( ibdev, ipoib->data.cq, ipoib_data_complete_send,
ipoib_data_complete_recv );
ipoib_refill_recv ( ipoib, &ipoib->meta ); ipoib_refill_recv ( ipoib, &ipoib->meta );
ipoib_refill_recv ( ipoib, &ipoib->data ); ipoib_refill_recv ( ipoib, &ipoib->data );
} }
@ -847,7 +854,9 @@ static int ipoib_open ( struct net_device *netdev ) {
if ( ( rc = ipoib_create_qset ( ipoib, &ipoib->meta, if ( ( rc = ipoib_create_qset ( ipoib, &ipoib->meta,
IPOIB_META_NUM_CQES, IPOIB_META_NUM_CQES,
IPOIB_META_NUM_SEND_WQES, IPOIB_META_NUM_SEND_WQES,
ipoib_meta_complete_send,
IPOIB_META_NUM_RECV_WQES, IPOIB_META_NUM_RECV_WQES,
ipoib_meta_complete_recv,
IB_GLOBAL_QKEY ) ) != 0 ) { IB_GLOBAL_QKEY ) ) != 0 ) {
DBGC ( ipoib, "IPoIB %p could not allocate metadata QP: %s\n", DBGC ( ipoib, "IPoIB %p could not allocate metadata QP: %s\n",
ipoib, strerror ( rc ) ); ipoib, strerror ( rc ) );
@ -858,7 +867,9 @@ static int ipoib_open ( struct net_device *netdev ) {
if ( ( rc = ipoib_create_qset ( ipoib, &ipoib->data, if ( ( rc = ipoib_create_qset ( ipoib, &ipoib->data,
IPOIB_DATA_NUM_CQES, IPOIB_DATA_NUM_CQES,
IPOIB_DATA_NUM_SEND_WQES, IPOIB_DATA_NUM_SEND_WQES,
ipoib_data_complete_send,
IPOIB_DATA_NUM_RECV_WQES, IPOIB_DATA_NUM_RECV_WQES,
ipoib_data_complete_recv,
IB_GLOBAL_QKEY ) ) != 0 ) { IB_GLOBAL_QKEY ) ) != 0 ) {
DBGC ( ipoib, "IPoIB %p could not allocate data QP: %s\n", DBGC ( ipoib, "IPoIB %p could not allocate data QP: %s\n",
ipoib, strerror ( rc ) ); ipoib, strerror ( rc ) );

View File

@ -101,6 +101,37 @@ enum ib_queue_pair_mods {
IB_MODIFY_QKEY = 0x0001, IB_MODIFY_QKEY = 0x0001,
}; };
/** An Infiniband completion */
struct ib_completion {
/** Syndrome
*
* If non-zero, then the completion is in error.
*/
unsigned int syndrome;
/** Length */
size_t len;
};
/** Infiniband completion syndromes */
enum ib_syndrome {
IB_SYN_NONE = 0,
IB_SYN_LOCAL_LENGTH = 1,
IB_SYN_LOCAL_QP = 2,
IB_SYN_LOCAL_PROT = 4,
};
/** An Infiniband completion handler
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @v completion Completion
* @v iobuf I/O buffer
*/
typedef void ( * ib_completer_t ) ( struct ib_device *ibdev,
struct ib_queue_pair *qp,
struct ib_completion *completion,
struct io_buffer *iobuf );
/** An Infiniband Completion Queue */ /** An Infiniband Completion Queue */
struct ib_completion_queue { struct ib_completion_queue {
/** Completion queue number */ /** Completion queue number */
@ -117,33 +148,14 @@ struct ib_completion_queue {
unsigned long next_idx; unsigned long next_idx;
/** List of work queues completing to this queue */ /** List of work queues completing to this queue */
struct list_head work_queues; struct list_head work_queues;
/** Send completion handler */
ib_completer_t complete_send;
/** Receive completion handler */
ib_completer_t complete_recv;
/** Driver private data */ /** Driver private data */
void *drv_priv; void *drv_priv;
}; };
/** An Infiniband completion */
struct ib_completion {
/** Syndrome
*
* If non-zero, then the completion is in error.
*/
unsigned int syndrome;
/** Length */
size_t len;
};
/** An Infiniband completion handler
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @v completion Completion
* @v iobuf I/O buffer
*/
typedef void ( * ib_completer_t ) ( struct ib_device *ibdev,
struct ib_queue_pair *qp,
struct ib_completion *completion,
struct io_buffer *iobuf );
/** An Infiniband Address Vector */ /** An Infiniband Address Vector */
struct ib_address_vector { struct ib_address_vector {
/** Destination Queue Pair */ /** Destination Queue Pair */
@ -246,15 +258,12 @@ struct ib_device_operations {
* *
* @v ibdev Infiniband device * @v ibdev Infiniband device
* @v cq Completion queue * @v cq Completion queue
* @v complete_send Send completion handler
* @v complete_recv Receive completion handler
* *
* The completion handler takes ownership of the I/O buffer. * The relevant completion handler (specified at completion
* queue creation time) takes ownership of the I/O buffer.
*/ */
void ( * poll_cq ) ( struct ib_device *ibdev, void ( * poll_cq ) ( struct ib_device *ibdev,
struct ib_completion_queue *cq, struct ib_completion_queue *cq );
ib_completer_t complete_send,
ib_completer_t complete_recv );
/** /**
* Poll event queue * Poll event queue
* *
@ -331,8 +340,9 @@ struct ib_device {
void *owner_priv; void *owner_priv;
}; };
extern struct ib_completion_queue * ib_create_cq ( struct ib_device *ibdev, extern struct ib_completion_queue *
unsigned int num_cqes ); ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes,
ib_completer_t complete_send, ib_completer_t complete_recv );
extern void ib_destroy_cq ( struct ib_device *ibdev, extern void ib_destroy_cq ( struct ib_device *ibdev,
struct ib_completion_queue *cq ); struct ib_completion_queue *cq );
extern struct ib_queue_pair * extern struct ib_queue_pair *
@ -379,18 +389,45 @@ ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp,
return ibdev->op->post_recv ( ibdev, qp, iobuf ); return ibdev->op->post_recv ( ibdev, qp, iobuf );
} }
/**
* Complete send work queue entry
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @v completion Completion
* @v iobuf I/O buffer
*/
static inline __attribute__ (( always_inline )) void
ib_complete_send ( struct ib_device *ibdev, struct ib_queue_pair *qp,
struct ib_completion *completion,
struct io_buffer *iobuf ) {
return qp->send.cq->complete_send ( ibdev, qp, completion, iobuf );
}
/**
* Complete receive work queue entry
*
* @v ibdev Infiniband device
* @v qp Queue pair
* @v completion Completion
* @v iobuf I/O buffer
*/
static inline __attribute__ (( always_inline )) void
ib_complete_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp,
struct ib_completion *completion,
struct io_buffer *iobuf ) {
return qp->recv.cq->complete_recv ( ibdev, qp, completion, iobuf );
}
/** /**
* Poll completion queue * Poll completion queue
* *
* @v ibdev Infiniband device * @v ibdev Infiniband device
* @v cq Completion queue * @v cq Completion queue
* @v complete_send Send completion handler
* @v complete_recv Receive completion handler
*/ */
static inline __attribute__ (( always_inline )) void static inline __attribute__ (( always_inline )) void
ib_poll_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq, ib_poll_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq ) {
ib_completer_t complete_send, ib_completer_t complete_recv ) { ibdev->op->poll_cq ( ibdev, cq );
ibdev->op->poll_cq ( ibdev, cq, complete_send, complete_recv );
} }
/** /**

View File

@ -46,10 +46,13 @@ struct list_head ib_devices = LIST_HEAD_INIT ( ib_devices );
* *
* @v ibdev Infiniband device * @v ibdev Infiniband device
* @v num_cqes Number of completion queue entries * @v num_cqes Number of completion queue entries
* @v complete_send Send completion handler
* @v complete_recv Receive completion handler
* @ret cq New completion queue * @ret cq New completion queue
*/ */
struct ib_completion_queue * ib_create_cq ( struct ib_device *ibdev, struct ib_completion_queue *
unsigned int num_cqes ) { ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes,
ib_completer_t complete_send, ib_completer_t complete_recv ) {
struct ib_completion_queue *cq; struct ib_completion_queue *cq;
int rc; int rc;
@ -61,6 +64,8 @@ struct ib_completion_queue * ib_create_cq ( struct ib_device *ibdev,
return NULL; return NULL;
cq->num_cqes = num_cqes; cq->num_cqes = num_cqes;
INIT_LIST_HEAD ( &cq->work_queues ); INIT_LIST_HEAD ( &cq->work_queues );
cq->complete_send = complete_send;
cq->complete_recv = complete_recv;
/* Perform device-specific initialisation and get CQN */ /* Perform device-specific initialisation and get CQN */
if ( ( rc = ibdev->op->create_cq ( ibdev, cq ) ) != 0 ) { if ( ( rc = ibdev->op->create_cq ( ibdev, cq ) ) != 0 ) {
@ -190,11 +195,33 @@ int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp,
* @v qp Queue pair * @v qp Queue pair
*/ */
void ib_destroy_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { void ib_destroy_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) {
struct ib_completion completion = {
.syndrome = IB_SYN_LOCAL_QP,
};
struct io_buffer *iobuf;
unsigned int i;
DBGC ( ibdev, "IBDEV %p destroying QPN %#lx\n", DBGC ( ibdev, "IBDEV %p destroying QPN %#lx\n",
ibdev, qp->qpn ); ibdev, qp->qpn );
/* Perform device-specific destruction */
ibdev->op->destroy_qp ( ibdev, qp ); ibdev->op->destroy_qp ( ibdev, qp );
/* Complete any remaining I/O buffers with errors */
for ( i = 0 ; i < qp->send.num_wqes ; i++ ) {
if ( ( iobuf = qp->send.iobufs[i] ) != NULL )
ib_complete_send ( ibdev, qp, &completion, iobuf );
}
for ( i = 0 ; i < qp->recv.num_wqes ; i++ ) {
if ( ( iobuf = qp->recv.iobufs[i] ) != NULL )
ib_complete_recv ( ibdev, qp, &completion, iobuf );
}
/* Remove work queues from completion queue */
list_del ( &qp->send.list ); list_del ( &qp->send.list );
list_del ( &qp->recv.list ); list_del ( &qp->recv.list );
/* Free QP */
free ( qp ); free ( qp );
} }