| @@ -19,13 +19,38 @@ · 18 unmodified lines |
| 19 | 19 | |
| 20 | 20 | #include "tb.h" |
| 21 | 21 | |
| 22 | | −#define XDOMAIN_DEFAULT_TIMEOUT 1000 /* ms */ |
| 23 | | −#define XDOMAIN_UUID_RETRIES 10 |
| 24 | | −#define XDOMAIN_PROPERTIES_RETRIES 10 |
| 25 | | −#define XDOMAIN_PROPERTIES_CHANGED_RETRIES 10 |
| 26 | | −#define XDOMAIN_BONDING_WAIT 100 /* ms */ |
| 22 | +#define XDOMAIN_SHORT_TIMEOUT 100 /* ms */ |
| 23 | +#define XDOMAIN_DEFAULT_TIMEOUT 1000 /* ms */ |
| 24 | +#define XDOMAIN_BONDING_TIMEOUT 10000 /* ms */ |
| 25 | +#define XDOMAIN_RETRIES 10 |
| 27 | 26 | #define XDOMAIN_DEFAULT_MAX_HOPID 15 |
| 28 | 27 | |
| 28 | +enum { |
| 29 | + XDOMAIN_STATE_INIT, |
| 30 | + XDOMAIN_STATE_UUID, |
| 31 | + XDOMAIN_STATE_LINK_STATUS, |
| 32 | + XDOMAIN_STATE_LINK_STATE_CHANGE, |
| 33 | + XDOMAIN_STATE_LINK_STATUS2, |
| 34 | + XDOMAIN_STATE_BONDING_UUID_LOW, |
| 35 | + XDOMAIN_STATE_BONDING_UUID_HIGH, |
| 36 | + XDOMAIN_STATE_PROPERTIES, |
| 37 | + XDOMAIN_STATE_ENUMERATED, |
| 38 | + XDOMAIN_STATE_ERROR, |
| 39 | +}; |
| 40 | + |
| 41 | +static const char * const state_names[] = { |
| 42 | + [XDOMAIN_STATE_INIT] = "INIT", |
| 43 | + [XDOMAIN_STATE_UUID] = "UUID", |
| 44 | + [XDOMAIN_STATE_LINK_STATUS] = "LINK_STATUS", |
| 45 | + [XDOMAIN_STATE_LINK_STATE_CHANGE] = "LINK_STATE_CHANGE", |
| 46 | + [XDOMAIN_STATE_LINK_STATUS2] = "LINK_STATUS2", |
| 47 | + [XDOMAIN_STATE_BONDING_UUID_LOW] = "BONDING_UUID_LOW", |
| 48 | + [XDOMAIN_STATE_BONDING_UUID_HIGH] = "BONDING_UUID_HIGH", |
| 49 | + [XDOMAIN_STATE_PROPERTIES] = "PROPERTIES", |
| 50 | + [XDOMAIN_STATE_ENUMERATED] = "ENUMERATED", |
| 51 | + [XDOMAIN_STATE_ERROR] = "ERROR", |
| 52 | +}; |
| 53 | + |
| 29 | 54 | struct xdomain_request_work { |
| 30 | 55 | struct work_struct work; |
| 31 | 56 | struct tb_xdp_header *pkg; |
| @@ -235,7 +260,7 @@ static int tb_xdp_handle_error(const struct tb_xdp_error_response *res) · 203 unmodified lines |
| 235 | 260 | } |
| 236 | 261 | |
| 237 | 262 | static int tb_xdp_uuid_request(struct tb_ctl *ctl, u64 route, int retry, |
| 238 | | − uuid_t *uuid) |
| 263 | + uuid_t *uuid, u64 *remote_route) |
| 239 | 264 | { |
| 240 | 265 | struct tb_xdp_uuid_response res; |
| 241 | 266 | struct tb_xdp_uuid req; |
| @@ -258,6 +283,8 @@ static int tb_xdp_uuid_request(struct tb_ctl *ctl, u64 route, int retry, · 16 unmodified lines |
| 258 | 283 | return ret; |
| 259 | 284 | |
| 260 | 285 | uuid_copy(uuid, &res.src_uuid); |
| 286 | + *remote_route = (u64)res.src_route_hi << 32 | res.src_route_lo; |
| 287 | + |
| 261 | 288 | return 0; |
| 262 | 289 | } |
| 263 | 290 | |
| @@ -473,6 +500,112 @@ tb_xdp_properties_changed_response(struct tb_ctl *ctl, u64 route, u8 sequence) · 209 unmodified lines |
| 473 | 500 | TB_CFG_PKG_XDOMAIN_RESP); |
| 474 | 501 | } |
| 475 | 502 | |
| 503 | +static int tb_xdp_link_state_status_request(struct tb_ctl *ctl, u64 route, |
| 504 | + u8 sequence, u8 *slw, u8 *tlw, |
| 505 | + u8 *sls, u8 *tls) |
| 506 | +{ |
| 507 | + struct tb_xdp_link_state_status_response res; |
| 508 | + struct tb_xdp_link_state_status req; |
| 509 | + int ret; |
| 510 | + |
| 511 | + memset(&req, 0, sizeof(req)); |
| 512 | + tb_xdp_fill_header(&req.hdr, route, sequence, LINK_STATE_STATUS_REQUEST, |
| 513 | + sizeof(req)); |
| 514 | + |
| 515 | + memset(&res, 0, sizeof(res)); |
| 516 | + ret = __tb_xdomain_request(ctl, &req, sizeof(req), TB_CFG_PKG_XDOMAIN_REQ, |
| 517 | + &res, sizeof(res), TB_CFG_PKG_XDOMAIN_RESP, |
| 518 | + XDOMAIN_DEFAULT_TIMEOUT); |
| 519 | + if (ret) |
| 520 | + return ret; |
| 521 | + |
| 522 | + ret = tb_xdp_handle_error(&res.err); |
| 523 | + if (ret) |
| 524 | + return ret; |
| 525 | + |
| 526 | + if (res.status != 0) |
| 527 | + return -EREMOTEIO; |
| 528 | + |
| 529 | + *slw = res.slw; |
| 530 | + *tlw = res.tlw; |
| 531 | + *sls = res.sls; |
| 532 | + *tls = res.tls; |
| 533 | + |
| 534 | + return 0; |
| 535 | +} |
| 536 | + |
| 537 | +static int tb_xdp_link_state_status_response(struct tb *tb, struct tb_ctl *ctl, |
| 538 | + struct tb_xdomain *xd, u8 sequence) |
| 539 | +{ |
| 540 | + struct tb_switch *sw = tb_to_switch(xd->dev.parent); |
| 541 | + struct tb_xdp_link_state_status_response res; |
| 542 | + struct tb_port *port = tb_port_at(xd->route, sw); |
| 543 | + u32 val[2]; |
| 544 | + int ret; |
| 545 | + |
| 546 | + memset(&res, 0, sizeof(res)); |
| 547 | + tb_xdp_fill_header(&res.hdr, xd->route, sequence, |
| 548 | + LINK_STATE_STATUS_RESPONSE, sizeof(res)); |
| 549 | + |
| 550 | + ret = tb_port_read(port, val, TB_CFG_PORT, |
| 551 | + port->cap_phy + LANE_ADP_CS_0, ARRAY_SIZE(val)); |
| 552 | + if (ret) |
| 553 | + return ret; |
| 554 | + |
| 555 | + res.slw = (val[0] & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >> |
| 556 | + LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT; |
| 557 | + res.sls = (val[0] & LANE_ADP_CS_0_SUPPORTED_SPEED_MASK) >> |
| 558 | + LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT; |
| 559 | + res.tls = val[1] & LANE_ADP_CS_1_TARGET_SPEED_MASK; |
| 560 | + res.tlw = (val[1] & LANE_ADP_CS_1_TARGET_WIDTH_MASK) >> |
| 561 | + LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; |
| 562 | + |
| 563 | + return __tb_xdomain_response(ctl, &res, sizeof(res), |
| 564 | + TB_CFG_PKG_XDOMAIN_RESP); |
| 565 | +} |
| 566 | + |
| 567 | +static int tb_xdp_link_state_change_request(struct tb_ctl *ctl, u64 route, |
| 568 | + u8 sequence, u8 tlw, u8 tls) |
| 569 | +{ |
| 570 | + struct tb_xdp_link_state_change_response res; |
| 571 | + struct tb_xdp_link_state_change req; |
| 572 | + int ret; |
| 573 | + |
| 574 | + memset(&req, 0, sizeof(req)); |
| 575 | + tb_xdp_fill_header(&req.hdr, route, sequence, LINK_STATE_CHANGE_REQUEST, |
| 576 | + sizeof(req)); |
| 577 | + req.tlw = tlw; |
| 578 | + req.tls = tls; |
| 579 | + |
| 580 | + memset(&res, 0, sizeof(res)); |
| 581 | + ret = __tb_xdomain_request(ctl, &req, sizeof(req), TB_CFG_PKG_XDOMAIN_REQ, |
| 582 | + &res, sizeof(res), TB_CFG_PKG_XDOMAIN_RESP, |
| 583 | + XDOMAIN_DEFAULT_TIMEOUT); |
| 584 | + if (ret) |
| 585 | + return ret; |
| 586 | + |
| 587 | + ret = tb_xdp_handle_error(&res.err); |
| 588 | + if (ret) |
| 589 | + return ret; |
| 590 | + |
| 591 | + return res.status != 0 ? -EREMOTEIO : 0; |
| 592 | +} |
| 593 | + |
| 594 | +static int tb_xdp_link_state_change_response(struct tb_ctl *ctl, u64 route, |
| 595 | + u8 sequence, u32 status) |
| 596 | +{ |
| 597 | + struct tb_xdp_link_state_change_response res; |
| 598 | + |
| 599 | + memset(&res, 0, sizeof(res)); |
| 600 | + tb_xdp_fill_header(&res.hdr, route, sequence, LINK_STATE_CHANGE_RESPONSE, |
| 601 | + sizeof(res)); |
| 602 | + |
| 603 | + res.status = status; |
| 604 | + |
| 605 | + return __tb_xdomain_response(ctl, &res, sizeof(res), |
| 606 | + TB_CFG_PKG_XDOMAIN_RESP); |
| 607 | +} |
| 608 | + |
| 476 | 609 | /** |
| 477 | 610 | * tb_register_protocol_handler() - Register protocol handler |
| 478 | 611 | * @handler: Handler to register |
| @@ -600,14 +733,13 @@ static void tb_xdp_handle_request(struct work_struct *work) · 121 unmodified lines |
| 600 | 733 | goto out; |
| 601 | 734 | } |
| 602 | 735 | |
| 603 | | − tb_dbg(tb, "%llx: received XDomain request %#x\n", route, pkg->type); |
| 604 | | − |
| 605 | 736 | xd = tb_xdomain_find_by_route_locked(tb, route); |
| 606 | 737 | if (xd) |
| 607 | 738 | update_property_block(xd); |
| 608 | 739 | |
| 609 | 740 | switch (pkg->type) { |
| 610 | 741 | case PROPERTIES_REQUEST: |
| 742 | + tb_dbg(tb, "%llx: received XDomain properties request\n", route); |
| 611 | 743 | if (xd) { |
| 612 | 744 | ret = tb_xdp_properties_response(tb, ctl, xd, sequence, |
| 613 | 745 | (const struct tb_xdp_properties *)pkg); |
| @@ -615,6 +747,9 @@ static void tb_xdp_handle_request(struct work_struct *work) · 1 unmodified line |
| 615 | 747 | break; |
| 616 | 748 | |
| 617 | 749 | case PROPERTIES_CHANGED_REQUEST: |
| 750 | + tb_dbg(tb, "%llx: received XDomain properties changed request\n", |
| 751 | + route); |
| 752 | + |
| 618 | 753 | ret = tb_xdp_properties_changed_response(ctl, route, sequence); |
| 619 | 754 | |
| 620 | 755 | /* |
| @@ -622,18 +757,51 @@ static void tb_xdp_handle_request(struct work_struct *work) · 1 unmodified line |
| 622 | 757 | * the xdomain related to this connection as well in |
| 623 | 758 | * case there is a change in services it offers. |
| 624 | 759 | */ |
| 625 | | − if (xd && device_is_registered(&xd->dev)) { |
| 626 | | − queue_delayed_work(tb->wq, &xd->get_properties_work, |
| 627 | | − msecs_to_jiffies(50)); |
| 628 | | − } |
| 760 | + if (xd && device_is_registered(&xd->dev)) |
| 761 | + queue_delayed_work(tb->wq, &xd->state_work, |
| 762 | + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); |
| 629 | 763 | break; |
| 630 | 764 | |
| 631 | 765 | case UUID_REQUEST_OLD: |
| 632 | 766 | case UUID_REQUEST: |
| 767 | + tb_dbg(tb, "%llx: received XDomain UUID request\n", route); |
| 633 | 768 | ret = tb_xdp_uuid_response(ctl, route, sequence, uuid); |
| 634 | 769 | break; |
| 635 | 770 | |
| 771 | + case LINK_STATE_STATUS_REQUEST: |
| 772 | + tb_dbg(tb, "%llx: received XDomain link state status request\n", |
| 773 | + route); |
| 774 | + |
| 775 | + if (xd) { |
| 776 | + ret = tb_xdp_link_state_status_response(tb, ctl, xd, |
| 777 | + sequence); |
| 778 | + } else { |
| 779 | + tb_xdp_error_response(ctl, route, sequence, |
| 780 | + ERROR_NOT_READY); |
| 781 | + } |
| 782 | + break; |
| 783 | + |
| 784 | + case LINK_STATE_CHANGE_REQUEST: |
| 785 | + tb_dbg(tb, "%llx: received XDomain link state change request\n", |
| 786 | + route); |
| 787 | + |
| 788 | + if (xd && xd->state == XDOMAIN_STATE_BONDING_UUID_HIGH) { |
| 789 | + const struct tb_xdp_link_state_change *lsc = |
| 790 | + (const struct tb_xdp_link_state_change *)pkg; |
| 791 | + |
| 792 | + ret = tb_xdp_link_state_change_response(ctl, route, |
| 793 | + sequence, 0); |
| 794 | + xd->target_link_width = lsc->tlw; |
| 795 | + queue_delayed_work(tb->wq, &xd->state_work, |
| 796 | + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); |
| 797 | + } else { |
| 798 | + tb_xdp_error_response(ctl, route, sequence, |
| 799 | + ERROR_NOT_READY); |
| 800 | + } |
| 801 | + break; |
| 802 | + |
| 636 | 803 | default: |
| 804 | + tb_dbg(tb, "%llx: unknown XDomain request %#x\n", route, pkg->type); |
| 637 | 805 | tb_xdp_error_response(ctl, route, sequence, |
| 638 | 806 | ERROR_NOT_SUPPORTED); |
| 639 | 807 | break; |
| @@ -1000,32 +1168,38 @@ static int tb_xdomain_update_link_attributes(struct tb_xdomain *xd) · 360 unmodified lines |
| 1000 | 1168 | return 0; |
| 1001 | 1169 | } |
| 1002 | 1170 | |
| 1003 | | −static void tb_xdomain_get_uuid(struct work_struct *work) |
| 1171 | +static int tb_xdomain_get_uuid(struct tb_xdomain *xd) |
| 1004 | 1172 | { |
| 1005 | | − struct tb_xdomain *xd = container_of(work, typeof(*xd), |
| 1006 | | − get_uuid_work.work); |
| 1007 | 1173 | struct tb *tb = xd->tb; |
| 1008 | 1174 | uuid_t uuid; |
| 1175 | + u64 route; |
| 1009 | 1176 | int ret; |
| 1010 | 1177 | |
| 1011 | 1178 | dev_dbg(&xd->dev, "requesting remote UUID\n"); |
| 1012 | 1179 | |
| 1013 | | − ret = tb_xdp_uuid_request(tb->ctl, xd->route, xd->uuid_retries, &uuid); |
| 1180 | + ret = tb_xdp_uuid_request(tb->ctl, xd->route, xd->state_retries, &uuid, |
| 1181 | + &route); |
| 1014 | 1182 | if (ret < 0) { |
| 1015 | | − if (xd->uuid_retries-- > 0) { |
| 1183 | + if (xd->state_retries-- > 0) { |
| 1016 | 1184 | dev_dbg(&xd->dev, "failed to request UUID, retrying\n"); |
| 1017 | | − queue_delayed_work(xd->tb->wq, &xd->get_uuid_work, |
| 1018 | | − msecs_to_jiffies(100)); |
| 1185 | + return -EAGAIN; |
| 1019 | 1186 | } else { |
| 1020 | 1187 | dev_dbg(&xd->dev, "failed to read remote UUID\n"); |
| 1021 | 1188 | } |
| 1022 | | − return; |
| 1189 | + return ret; |
| 1023 | 1190 | } |
| 1024 | 1191 | |
| 1025 | 1192 | dev_dbg(&xd->dev, "got remote UUID %pUb\n", &uuid); |
| 1026 | 1193 | |
| 1027 | | − if (uuid_equal(&uuid, xd->local_uuid)) |
| 1028 | | − dev_dbg(&xd->dev, "intra-domain loop detected\n"); |
| 1194 | + if (uuid_equal(&uuid, xd->local_uuid)) { |
| 1195 | + if (route == xd->route) |
| 1196 | + dev_dbg(&xd->dev, "loop back detected\n"); |
| 1197 | + else |
| 1198 | + dev_dbg(&xd->dev, "intra-domain loop detected\n"); |
| 1199 | + |
| 1200 | + /* Don't bond lanes automatically for loops */ |
| 1201 | + xd->bonding_possible = false; |
| 1202 | + } |
| 1029 | 1203 | |
| 1030 | 1204 | /* |
| 1031 | 1205 | * If the UUID is different, there is another domain connected |
| @@ -1035,27 +1209,152 @@ static void tb_xdomain_get_uuid(struct work_struct *work) · 3 unmodified lines |
| 1035 | 1209 | if (xd->remote_uuid && !uuid_equal(&uuid, xd->remote_uuid)) { |
| 1036 | 1210 | dev_dbg(&xd->dev, "remote UUID is different, unplugging\n"); |
| 1037 | 1211 | xd->is_unplugged = true; |
| 1038 | | − return; |
| 1212 | + return -ENODEV; |
| 1039 | 1213 | } |
| 1040 | 1214 | |
| 1041 | 1215 | /* First time fill in the missing UUID */ |
| 1042 | 1216 | if (!xd->remote_uuid) { |
| 1043 | 1217 | xd->remote_uuid = kmemdup(&uuid, sizeof(uuid_t), GFP_KERNEL); |
| 1044 | 1218 | if (!xd->remote_uuid) |
| 1045 | | − return; |
| 1219 | + return -ENOMEM; |
| 1046 | 1220 | } |
| 1047 | 1221 | |
| 1048 | | − /* Now we can start the normal properties exchange */ |
| 1049 | | − queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, |
| 1050 | | − msecs_to_jiffies(100)); |
| 1051 | | − queue_delayed_work(xd->tb->wq, &xd->get_properties_work, |
| 1052 | | − msecs_to_jiffies(1000)); |
| 1222 | + return 0; |
| 1053 | 1223 | } |
| 1054 | 1224 | |
| 1055 | | −static void tb_xdomain_get_properties(struct work_struct *work) |
| 1225 | +static int tb_xdomain_get_link_status(struct tb_xdomain *xd) |
| 1226 | +{ |
| 1227 | + struct tb *tb = xd->tb; |
| 1228 | + u8 slw, tlw, sls, tls; |
| 1229 | + int ret; |
| 1230 | + |
| 1231 | + dev_dbg(&xd->dev, "sending link state status request to %pUb\n", |
| 1232 | + xd->remote_uuid); |
| 1233 | + |
| 1234 | + ret = tb_xdp_link_state_status_request(tb->ctl, xd->route, |
| 1235 | + xd->state_retries, &slw, &tlw, &sls, |
| 1236 | + &tls); |
| 1237 | + if (ret) { |
| 1238 | + if (ret != -EOPNOTSUPP && xd->state_retries-- > 0) { |
| 1239 | + dev_dbg(&xd->dev, |
| 1240 | + "failed to request remote link status, retrying\n"); |
| 1241 | + return -EAGAIN; |
| 1242 | + } |
| 1243 | + dev_dbg(&xd->dev, "failed to receive remote link status\n"); |
| 1244 | + return ret; |
| 1245 | + } |
| 1246 | + |
| 1247 | + dev_dbg(&xd->dev, "remote link supports width %#x speed %#x\n", slw, sls); |
| 1248 | + |
| 1249 | + if (slw < LANE_ADP_CS_0_SUPPORTED_WIDTH_DUAL) { |
| 1250 | + dev_dbg(&xd->dev, "remote adapter is single lane only\n"); |
| 1251 | + return -EOPNOTSUPP; |
| 1252 | + } |
| 1253 | + |
| 1254 | + return 0; |
| 1255 | +} |
| 1256 | + |
| 1257 | +static int tb_xdomain_link_state_change(struct tb_xdomain *xd, |
| 1258 | + unsigned int width) |
| 1259 | +{ |
| 1260 | + struct tb_switch *sw = tb_to_switch(xd->dev.parent); |
| 1261 | + struct tb_port *port = tb_port_at(xd->route, sw); |
| 1262 | + struct tb *tb = xd->tb; |
| 1263 | + u8 tlw, tls; |
| 1264 | + u32 val; |
| 1265 | + int ret; |
| 1266 | + |
| 1267 | + if (width == 2) |
| 1268 | + tlw = LANE_ADP_CS_1_TARGET_WIDTH_DUAL; |
| 1269 | + else if (width == 1) |
| 1270 | + tlw = LANE_ADP_CS_1_TARGET_WIDTH_SINGLE; |
| 1271 | + else |
| 1272 | + return -EINVAL; |
| 1273 | + |
| 1274 | + /* Use the current target speed */ |
| 1275 | + ret = tb_port_read(port, &val, TB_CFG_PORT, port->cap_phy + LANE_ADP_CS_1, 1); |
| 1276 | + if (ret) |
| 1277 | + return ret; |
| 1278 | + tls = val & LANE_ADP_CS_1_TARGET_SPEED_MASK; |
| 1279 | + |
| 1280 | + dev_dbg(&xd->dev, "sending link state change request with width %#x speed %#x\n", |
| 1281 | + tlw, tls); |
| 1282 | + |
| 1283 | + ret = tb_xdp_link_state_change_request(tb->ctl, xd->route, |
| 1284 | + xd->state_retries, tlw, tls); |
| 1285 | + if (ret) { |
| 1286 | + if (ret != -EOPNOTSUPP && xd->state_retries-- > 0) { |
| 1287 | + dev_dbg(&xd->dev, |
| 1288 | + "failed to change remote link state, retrying\n"); |
| 1289 | + return -EAGAIN; |
| 1290 | + } |
| 1291 | + dev_err(&xd->dev, "failed request link state change, aborting\n"); |
| 1292 | + return ret; |
| 1293 | + } |
| 1294 | + |
| 1295 | + dev_dbg(&xd->dev, "received link state change response\n"); |
| 1296 | + return 0; |
| 1297 | +} |
| 1298 | + |
| 1299 | +static int tb_xdomain_bond_lanes_uuid_high(struct tb_xdomain *xd) |
| 1300 | +{ |
| 1301 | + struct tb_port *port; |
| 1302 | + int ret, width; |
| 1303 | + |
| 1304 | + if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_SINGLE) { |
| 1305 | + width = 1; |
| 1306 | + } else if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_DUAL) { |
| 1307 | + width = 2; |
| 1308 | + } else { |
| 1309 | + if (xd->state_retries-- > 0) { |
| 1310 | + dev_dbg(&xd->dev, |
| 1311 | + "link state change request not received yet, retrying\n"); |
| 1312 | + return -EAGAIN; |
| 1313 | + } |
| 1314 | + dev_dbg(&xd->dev, "timeout waiting for link change request\n"); |
| 1315 | + return -ETIMEDOUT; |
| 1316 | + } |
| 1317 | + |
| 1318 | + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); |
| 1319 | + |
| 1320 | + /* |
| 1321 | + * We can't use tb_xdomain_lane_bonding_enable() here because it |
| 1322 | + * is the other side that initiates lane bonding. So here we |
| 1323 | + * just set the width to both lane adapters and wait for the |
| 1324 | + * link to transition bonded. |
| 1325 | + */ |
| 1326 | + ret = tb_port_set_link_width(port->dual_link_port, width); |
| 1327 | + if (ret) { |
| 1328 | + tb_port_warn(port->dual_link_port, |
| 1329 | + "failed to set link width to %d\n", width); |
| 1330 | + return ret; |
| 1331 | + } |
| 1332 | + |
| 1333 | + ret = tb_port_set_link_width(port, width); |
| 1334 | + if (ret) { |
| 1335 | + tb_port_warn(port, "failed to set link width to %d\n", width); |
| 1336 | + return ret; |
| 1337 | + } |
| 1338 | + |
| 1339 | + ret = tb_port_wait_for_link_width(port, width, XDOMAIN_BONDING_TIMEOUT); |
| 1340 | + if (ret) { |
| 1341 | + dev_warn(&xd->dev, "error waiting for link width to become %d\n", |
| 1342 | + width); |
| 1343 | + return ret; |
| 1344 | + } |
| 1345 | + |
| 1346 | + port->bonded = width == 2; |
| 1347 | + port->dual_link_port->bonded = width == 2; |
| 1348 | + |
| 1349 | + tb_port_update_credits(port); |
| 1350 | + tb_xdomain_update_link_attributes(xd); |
| 1351 | + |
| 1352 | + dev_dbg(&xd->dev, "lane bonding %sabled\n", width == 2 ? "en" : "dis"); |
| 1353 | + return 0; |
| 1354 | +} |
| 1355 | + |
| 1356 | +static int tb_xdomain_get_properties(struct tb_xdomain *xd) |
| 1056 | 1357 | { |
| 1057 | | − struct tb_xdomain *xd = container_of(work, typeof(*xd), |
| 1058 | | − get_properties_work.work); |
| 1059 | 1358 | struct tb_property_dir *dir; |
| 1060 | 1359 | struct tb *tb = xd->tb; |
| 1061 | 1360 | bool update = false; |
| @@ -1066,34 +1365,35 @@ static void tb_xdomain_get_properties(struct work_struct *work) · 4 unmodified lines |
| 1066 | 1365 | dev_dbg(&xd->dev, "requesting remote properties\n"); |
| 1067 | 1366 | |
| 1068 | 1367 | ret = tb_xdp_properties_request(tb->ctl, xd->route, xd->local_uuid, |
| 1069 | | − xd->remote_uuid, xd->properties_retries, |
| 1368 | + xd->remote_uuid, xd->state_retries, |
| 1070 | 1369 | &block, &gen); |
| 1071 | 1370 | if (ret < 0) { |
| 1072 | | − if (xd->properties_retries-- > 0) { |
| 1371 | + if (xd->state_retries-- > 0) { |
| 1073 | 1372 | dev_dbg(&xd->dev, |
| 1074 | 1373 | "failed to request remote properties, retrying\n"); |
| 1075 | | − queue_delayed_work(xd->tb->wq, &xd->get_properties_work, |
| 1076 | | − msecs_to_jiffies(1000)); |
| 1374 | + return -EAGAIN; |
| 1077 | 1375 | } else { |
| 1078 | 1376 | /* Give up now */ |
| 1079 | 1377 | dev_err(&xd->dev, |
| 1080 | 1378 | "failed read XDomain properties from %pUb\n", |
| 1081 | 1379 | xd->remote_uuid); |
| 1082 | 1380 | } |
| 1083 | | − return; |
| 1084 | | − } |
| 1085 | 1381 | |
| 1086 | | − xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES; |
| 1382 | + return ret; |
| 1383 | + } |
| 1087 | 1384 | |
| 1088 | 1385 | mutex_lock(&xd->lock); |
| 1089 | 1386 | |
| 1090 | 1387 | /* Only accept newer generation properties */ |
| 1091 | | − if (xd->remote_properties && gen <= xd->remote_property_block_gen) |
| 1388 | + if (xd->remote_properties && gen <= xd->remote_property_block_gen) { |
| 1389 | + ret = 0; |
| 1092 | 1390 | goto err_free_block; |
| 1391 | + } |
| 1093 | 1392 | |
| 1094 | 1393 | dir = tb_property_parse_dir(block, ret); |
| 1095 | 1394 | if (!dir) { |
| 1096 | 1395 | dev_err(&xd->dev, "failed to parse XDomain properties\n"); |
| 1396 | + ret = -ENOMEM; |
| 1097 | 1397 | goto err_free_block; |
| 1098 | 1398 | } |
| 1099 | 1399 | |
| @@ -1124,9 +1424,16 @@ static void tb_xdomain_get_properties(struct work_struct *work) · 24 unmodified lines |
| 1124 | 1424 | * registered, we notify the userspace that it has changed. |
| 1125 | 1425 | */ |
| 1126 | 1426 | if (!update) { |
| 1427 | + struct tb_port *port; |
| 1428 | + |
| 1429 | + /* Now disable lane 1 if bonding was not enabled */ |
| 1430 | + port = tb_port_at(xd->route, tb_xdomain_parent(xd)); |
| 1431 | + if (!port->bonded) |
| 1432 | + tb_port_disable(port->dual_link_port); |
| 1433 | + |
| 1127 | 1434 | if (device_add(&xd->dev)) { |
| 1128 | 1435 | dev_err(&xd->dev, "failed to add XDomain device\n"); |
| 1129 | | − return; |
| 1436 | + return -ENODEV; |
| 1130 | 1437 | } |
| 1131 | 1438 | dev_info(&xd->dev, "new host found, vendor=%#x device=%#x\n", |
| 1132 | 1439 | xd->vendor, xd->device); |
| @@ -1138,13 +1445,193 @@ static void tb_xdomain_get_properties(struct work_struct *work) · 5 unmodified lines |
| 1138 | 1445 | } |
| 1139 | 1446 | |
| 1140 | 1447 | enumerate_services(xd); |
| 1141 | | − return; |
| 1448 | + return 0; |
| 1142 | 1449 | |
| 1143 | 1450 | err_free_dir: |
| 1144 | 1451 | tb_property_free_dir(dir); |
| 1145 | 1452 | err_free_block: |
| 1146 | 1453 | kfree(block); |
| 1147 | 1454 | mutex_unlock(&xd->lock); |
| 1455 | + |
| 1456 | + return ret; |
| 1457 | +} |
| 1458 | + |
| 1459 | +static void tb_xdomain_queue_uuid(struct tb_xdomain *xd) |
| 1460 | +{ |
| 1461 | + xd->state = XDOMAIN_STATE_UUID; |
| 1462 | + xd->state_retries = XDOMAIN_RETRIES; |
| 1463 | + queue_delayed_work(xd->tb->wq, &xd->state_work, |
| 1464 | + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); |
| 1465 | +} |
| 1466 | + |
| 1467 | +static void tb_xdomain_queue_link_status(struct tb_xdomain *xd) |
| 1468 | +{ |
| 1469 | + xd->state = XDOMAIN_STATE_LINK_STATUS; |
| 1470 | + xd->state_retries = XDOMAIN_RETRIES; |
| 1471 | + queue_delayed_work(xd->tb->wq, &xd->state_work, |
| 1472 | + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); |
| 1473 | +} |
| 1474 | + |
| 1475 | +static void tb_xdomain_queue_link_status2(struct tb_xdomain *xd) |
| 1476 | +{ |
| 1477 | + xd->state = XDOMAIN_STATE_LINK_STATUS2; |
| 1478 | + xd->state_retries = XDOMAIN_RETRIES; |
| 1479 | + queue_delayed_work(xd->tb->wq, &xd->state_work, |
| 1480 | + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); |
| 1481 | +} |
| 1482 | + |
| 1483 | +static void tb_xdomain_queue_bonding(struct tb_xdomain *xd) |
| 1484 | +{ |
| 1485 | + if (memcmp(xd->local_uuid, xd->remote_uuid, UUID_SIZE) > 0) { |
| 1486 | + dev_dbg(&xd->dev, "we have higher UUID, other side bonds the lanes\n"); |
| 1487 | + xd->state = XDOMAIN_STATE_BONDING_UUID_HIGH; |
| 1488 | + } else { |
| 1489 | + dev_dbg(&xd->dev, "we have lower UUID, bonding lanes\n"); |
| 1490 | + xd->state = XDOMAIN_STATE_LINK_STATE_CHANGE; |
| 1491 | + } |
| 1492 | + |
| 1493 | + xd->state_retries = XDOMAIN_RETRIES; |
| 1494 | + queue_delayed_work(xd->tb->wq, &xd->state_work, |
| 1495 | + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); |
| 1496 | +} |
| 1497 | + |
| 1498 | +static void tb_xdomain_queue_bonding_uuid_low(struct tb_xdomain *xd) |
| 1499 | +{ |
| 1500 | + xd->state = XDOMAIN_STATE_BONDING_UUID_LOW; |
| 1501 | + xd->state_retries = XDOMAIN_RETRIES; |
| 1502 | + queue_delayed_work(xd->tb->wq, &xd->state_work, |
| 1503 | + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); |
| 1504 | +} |
| 1505 | + |
| 1506 | +static void tb_xdomain_queue_properties(struct tb_xdomain *xd) |
| 1507 | +{ |
| 1508 | + xd->state = XDOMAIN_STATE_PROPERTIES; |
| 1509 | + xd->state_retries = XDOMAIN_RETRIES; |
| 1510 | + queue_delayed_work(xd->tb->wq, &xd->state_work, |
| 1511 | + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); |
| 1512 | +} |
| 1513 | + |
| 1514 | +static void tb_xdomain_queue_properties_changed(struct tb_xdomain *xd) |
| 1515 | +{ |
| 1516 | + xd->properties_changed_retries = XDOMAIN_RETRIES; |
| 1517 | + queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, |
| 1518 | + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); |
| 1519 | +} |
| 1520 | + |
| 1521 | +static void tb_xdomain_state_work(struct work_struct *work) |
| 1522 | +{ |
| 1523 | + struct tb_xdomain *xd = container_of(work, typeof(*xd), state_work.work); |
| 1524 | + int ret, state = xd->state; |
| 1525 | + |
| 1526 | + if (WARN_ON_ONCE(state < XDOMAIN_STATE_INIT || |
| 1527 | + state > XDOMAIN_STATE_ERROR)) |
| 1528 | + return; |
| 1529 | + |
| 1530 | + dev_dbg(&xd->dev, "running state %s\n", state_names[state]); |
| 1531 | + |
| 1532 | + switch (state) { |
| 1533 | + case XDOMAIN_STATE_INIT: |
| 1534 | + if (xd->needs_uuid) { |
| 1535 | + tb_xdomain_queue_uuid(xd); |
| 1536 | + } else { |
| 1537 | + tb_xdomain_queue_properties_changed(xd); |
| 1538 | + tb_xdomain_queue_properties(xd); |
| 1539 | + } |
| 1540 | + break; |
| 1541 | + |
| 1542 | + case XDOMAIN_STATE_UUID: |
| 1543 | + ret = tb_xdomain_get_uuid(xd); |
| 1544 | + if (ret) { |
| 1545 | + if (ret == -EAGAIN) |
| 1546 | + goto retry_state; |
| 1547 | + xd->state = XDOMAIN_STATE_ERROR; |
| 1548 | + } else { |
| 1549 | + tb_xdomain_queue_properties_changed(xd); |
| 1550 | + if (xd->bonding_possible) |
| 1551 | + tb_xdomain_queue_link_status(xd); |
| 1552 | + else |
| 1553 | + tb_xdomain_queue_properties(xd); |
| 1554 | + } |
| 1555 | + break; |
| 1556 | + |
| 1557 | + case XDOMAIN_STATE_LINK_STATUS: |
| 1558 | + ret = tb_xdomain_get_link_status(xd); |
| 1559 | + if (ret) { |
| 1560 | + if (ret == -EAGAIN) |
| 1561 | + goto retry_state; |
| 1562 | + |
| 1563 | + /* |
| 1564 | + * If any of the lane bonding states fail we skip |
| 1565 | + * bonding completely and try to continue from |
| 1566 | + * reading properties. |
| 1567 | + */ |
| 1568 | + tb_xdomain_queue_properties(xd); |
| 1569 | + } else { |
| 1570 | + tb_xdomain_queue_bonding(xd); |
| 1571 | + } |
| 1572 | + break; |
| 1573 | + |
| 1574 | + case XDOMAIN_STATE_LINK_STATE_CHANGE: |
| 1575 | + ret = tb_xdomain_link_state_change(xd, 2); |
| 1576 | + if (ret) { |
| 1577 | + if (ret == -EAGAIN) |
| 1578 | + goto retry_state; |
| 1579 | + tb_xdomain_queue_properties(xd); |
| 1580 | + } else { |
| 1581 | + tb_xdomain_queue_link_status2(xd); |
| 1582 | + } |
| 1583 | + break; |
| 1584 | + |
| 1585 | + case XDOMAIN_STATE_LINK_STATUS2: |
| 1586 | + ret = tb_xdomain_get_link_status(xd); |
| 1587 | + if (ret) { |
| 1588 | + if (ret == -EAGAIN) |
| 1589 | + goto retry_state; |
| 1590 | + tb_xdomain_queue_properties(xd); |
| 1591 | + } else { |
| 1592 | + tb_xdomain_queue_bonding_uuid_low(xd); |
| 1593 | + } |
| 1594 | + break; |
| 1595 | + |
| 1596 | + case XDOMAIN_STATE_BONDING_UUID_LOW: |
| 1597 | + tb_xdomain_lane_bonding_enable(xd); |
| 1598 | + tb_xdomain_queue_properties(xd); |
| 1599 | + break; |
| 1600 | + |
| 1601 | + case XDOMAIN_STATE_BONDING_UUID_HIGH: |
| 1602 | + if (tb_xdomain_bond_lanes_uuid_high(xd) == -EAGAIN) |
| 1603 | + goto retry_state; |
| 1604 | + tb_xdomain_queue_properties(xd); |
| 1605 | + break; |
| 1606 | + |
| 1607 | + case XDOMAIN_STATE_PROPERTIES: |
| 1608 | + ret = tb_xdomain_get_properties(xd); |
| 1609 | + if (ret) { |
| 1610 | + if (ret == -EAGAIN) |
| 1611 | + goto retry_state; |
| 1612 | + xd->state = XDOMAIN_STATE_ERROR; |
| 1613 | + } else { |
| 1614 | + xd->state = XDOMAIN_STATE_ENUMERATED; |
| 1615 | + } |
| 1616 | + break; |
| 1617 | + |
| 1618 | + case XDOMAIN_STATE_ENUMERATED: |
| 1619 | + tb_xdomain_queue_properties(xd); |
| 1620 | + break; |
| 1621 | + |
| 1622 | + case XDOMAIN_STATE_ERROR: |
| 1623 | + break; |
| 1624 | + |
| 1625 | + default: |
| 1626 | + dev_warn(&xd->dev, "unexpected state %d\n", state); |
| 1627 | + break; |
| 1628 | + } |
| 1629 | + |
| 1630 | + return; |
| 1631 | + |
| 1632 | +retry_state: |
| 1633 | + queue_delayed_work(xd->tb->wq, &xd->state_work, |
| 1634 | + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); |
| 1148 | 1635 | } |
| 1149 | 1636 | |
| 1150 | 1637 | static void tb_xdomain_properties_changed(struct work_struct *work) |
| @@ -1163,13 +1650,13 @@ static void tb_xdomain_properties_changed(struct work_struct *work) · 12 unmodified lines |
| 1163 | 1650 | "failed to send properties changed notification, retrying\n"); |
| 1164 | 1651 | queue_delayed_work(xd->tb->wq, |
| 1165 | 1652 | &xd->properties_changed_work, |
| 1166 | | − msecs_to_jiffies(1000)); |
| 1653 | + msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT)); |
| 1167 | 1654 | } |
| 1168 | 1655 | dev_err(&xd->dev, "failed to send properties changed notification\n"); |
| 1169 | 1656 | return; |
| 1170 | 1657 | } |
| 1171 | 1658 | |
| 1172 | | − xd->properties_changed_retries = XDOMAIN_PROPERTIES_CHANGED_RETRIES; |
| 1659 | + xd->properties_changed_retries = XDOMAIN_RETRIES; |
| 1173 | 1660 | } |
| 1174 | 1661 | |
| 1175 | 1662 | static ssize_t device_show(struct device *dev, struct device_attribute *attr, |
| @@ -1304,31 +1791,17 @@ static void tb_xdomain_release(struct device *dev) · 128 unmodified lines |
| 1304 | 1791 | |
| 1305 | 1792 | static void start_handshake(struct tb_xdomain *xd) |
| 1306 | 1793 | { |
| 1307 | | − xd->uuid_retries = XDOMAIN_UUID_RETRIES; |
| 1308 | | − xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES; |
| 1309 | | − xd->properties_changed_retries = XDOMAIN_PROPERTIES_CHANGED_RETRIES; |
| 1310 | | − |
| 1311 | | − if (xd->needs_uuid) { |
| 1312 | | − queue_delayed_work(xd->tb->wq, &xd->get_uuid_work, |
| 1313 | | − msecs_to_jiffies(100)); |
| 1314 | | − } else { |
| 1315 | | − /* Start exchanging properties with the other host */ |
| 1316 | | − queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, |
| 1317 | | − msecs_to_jiffies(100)); |
| 1318 | | − queue_delayed_work(xd->tb->wq, &xd->get_properties_work, |
| 1319 | | − msecs_to_jiffies(1000)); |
| 1320 | | − } |
| 1794 | + xd->state = XDOMAIN_STATE_INIT; |
| 1795 | + queue_delayed_work(xd->tb->wq, &xd->state_work, |
| 1796 | + msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT)); |
| 1321 | 1797 | } |
| 1322 | 1798 | |
| 1323 | 1799 | static void stop_handshake(struct tb_xdomain *xd) |
| 1324 | 1800 | { |
| 1325 | | − xd->uuid_retries = 0; |
| 1326 | | − xd->properties_retries = 0; |
| 1327 | | − xd->properties_changed_retries = 0; |
| 1328 | | − |
| 1329 | | − cancel_delayed_work_sync(&xd->get_uuid_work); |
| 1330 | | − cancel_delayed_work_sync(&xd->get_properties_work); |
| 1331 | 1801 | cancel_delayed_work_sync(&xd->properties_changed_work); |
| 1802 | + cancel_delayed_work_sync(&xd->state_work); |
| 1803 | + xd->properties_changed_retries = 0; |
| 1804 | + xd->state_retries = 0; |
| 1332 | 1805 | } |
| 1333 | 1806 | |
| 1334 | 1807 | static int __maybe_unused tb_xdomain_suspend(struct device *dev) |
| @@ -1389,8 +1862,7 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, · 54 unmodified lines |
| 1389 | 1862 | ida_init(&xd->in_hopids); |
| 1390 | 1863 | ida_init(&xd->out_hopids); |
| 1391 | 1864 | mutex_init(&xd->lock); |
| 1392 | | − INIT_DELAYED_WORK(&xd->get_uuid_work, tb_xdomain_get_uuid); |
| 1393 | | − INIT_DELAYED_WORK(&xd->get_properties_work, tb_xdomain_get_properties); |
| 1865 | + INIT_DELAYED_WORK(&xd->state_work, tb_xdomain_state_work); |
| 1394 | 1866 | INIT_DELAYED_WORK(&xd->properties_changed_work, |
| 1395 | 1867 | tb_xdomain_properties_changed); |
| 1396 | 1868 | |
| @@ -1405,6 +1877,7 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, · 8 unmodified lines |
| 1405 | 1877 | goto err_free_local_uuid; |
| 1406 | 1878 | } else { |
| 1407 | 1879 | xd->needs_uuid = true; |
| 1880 | + xd->bonding_possible = !!down->dual_link_port; |
| 1408 | 1881 | } |
| 1409 | 1882 | |
| 1410 | 1883 | device_initialize(&xd->dev); |
| @@ -1523,9 +1996,9 @@ int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd) · 112 unmodified lines |
| 1523 | 1996 | return ret; |
| 1524 | 1997 | } |
| 1525 | 1998 | |
| 1526 | | − ret = tb_port_wait_for_link_width(port, 2, 100); |
| 1999 | + ret = tb_port_wait_for_link_width(port, 2, XDOMAIN_BONDING_TIMEOUT); |
| 1527 | 2000 | if (ret) { |
| 1528 | | − tb_port_warn(port, "timeout enabling lane bonding\n"); |
| 2001 | + tb_port_warn(port, "failed to enable lane bonding\n"); |
| 1529 | 2002 | return ret; |
| 1530 | 2003 | } |
| 1531 | 2004 | |