|
@@ -1984,15 +1984,64 @@ static int modify_qp(struct ib_uverbs_file *file,
|
|
|
goto release_qp;
|
|
|
}
|
|
|
|
|
|
- if ((cmd->base.attr_mask & IB_QP_AV) &&
|
|
|
- !rdma_is_port_valid(qp->device, cmd->base.dest.port_num)) {
|
|
|
- ret = -EINVAL;
|
|
|
- goto release_qp;
|
|
|
+ if ((cmd->base.attr_mask & IB_QP_AV)) {
|
|
|
+ if (!rdma_is_port_valid(qp->device, cmd->base.dest.port_num)) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto release_qp;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cmd->base.attr_mask & IB_QP_STATE &&
|
|
|
+ cmd->base.qp_state == IB_QPS_RTR) {
|
|
|
+ /* We are in INIT->RTR TRANSITION (if we are not,
|
|
|
+ * this transition will be rejected in subsequent checks).
|
|
|
+ * In the INIT->RTR transition, we cannot have IB_QP_PORT set,
|
|
|
+ * but the IB_QP_STATE flag is required.
|
|
|
+ *
|
|
|
+ * Since kernel 3.14 (commit dbf727de7440), the uverbs driver,
|
|
|
+ * when IB_QP_AV is set, has required inclusion of a valid
|
|
|
+ * port number in the primary AV. (AVs are created and handled
|
|
|
+ * differently for infiniband and ethernet (RoCE) ports).
|
|
|
+ *
|
|
|
+ * Check the port number included in the primary AV against
|
|
|
+ * the port number in the qp struct, which was set (and saved)
|
|
|
+ * in the RST->INIT transition.
|
|
|
+ */
|
|
|
+ if (cmd->base.dest.port_num != qp->real_qp->port) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto release_qp;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* We are in SQD->SQD. (If we are not, this transition will
|
|
|
+ * be rejected later in the verbs layer checks).
|
|
|
+ * Check for both IB_QP_PORT and IB_QP_AV, these can be set
|
|
|
+ * together in the SQD->SQD transition.
|
|
|
+ *
|
|
|
+ * If only IP_QP_AV was set, add in IB_QP_PORT as well (the
|
|
|
+ * verbs layer driver does not track primary port changes
|
|
|
+ * resulting from path migration. Thus, in SQD, if the primary
|
|
|
+ * AV is modified, the primary port should also be modified).
|
|
|
+ *
|
|
|
+ * Note that in this transition, the IB_QP_STATE flag
|
|
|
+ * is not allowed.
|
|
|
+ */
|
|
|
+ if (((cmd->base.attr_mask & (IB_QP_AV | IB_QP_PORT))
|
|
|
+ == (IB_QP_AV | IB_QP_PORT)) &&
|
|
|
+ cmd->base.port_num != cmd->base.dest.port_num) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto release_qp;
|
|
|
+ }
|
|
|
+ if ((cmd->base.attr_mask & (IB_QP_AV | IB_QP_PORT))
|
|
|
+ == IB_QP_AV) {
|
|
|
+ cmd->base.attr_mask |= IB_QP_PORT;
|
|
|
+ cmd->base.port_num = cmd->base.dest.port_num;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if ((cmd->base.attr_mask & IB_QP_ALT_PATH) &&
|
|
|
(!rdma_is_port_valid(qp->device, cmd->base.alt_port_num) ||
|
|
|
- !rdma_is_port_valid(qp->device, cmd->base.alt_dest.port_num))) {
|
|
|
+ !rdma_is_port_valid(qp->device, cmd->base.alt_dest.port_num) ||
|
|
|
+ cmd->base.alt_port_num != cmd->base.alt_dest.port_num)) {
|
|
|
ret = -EINVAL;
|
|
|
goto release_qp;
|
|
|
}
|