Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

thunderbolt: Add XDomain UUID exchange support

Currently ICM has been handling XDomain UUID exchange so there was no
need to have it in the driver yet. However, since now we are going to
add the same capabilities to the software connection manager it needs to
be handled properly.

For this reason modify the driver XDomain protocol handling so that if
the remote domain UUID is not filled in the core will query it first and
only then start the normal property exchange flow.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>

+145 -10
+11
drivers/thunderbolt/tb_msgs.h
··· 492 492 u32 type; 493 493 }; 494 494 495 + struct tb_xdp_uuid { 496 + struct tb_xdp_header hdr; 497 + }; 498 + 499 + struct tb_xdp_uuid_response { 500 + struct tb_xdp_header hdr; 501 + uuid_t src_uuid; 502 + u32 src_route_hi; 503 + u32 src_route_lo; 504 + }; 505 + 495 506 struct tb_xdp_properties { 496 507 struct tb_xdp_header hdr; 497 508 uuid_t src_uuid;
+126 -10
drivers/thunderbolt/xdomain.c
··· 18 18 #include "tb.h" 19 19 20 20 #define XDOMAIN_DEFAULT_TIMEOUT 5000 /* ms */ 21 + #define XDOMAIN_UUID_RETRIES 10 21 22 #define XDOMAIN_PROPERTIES_RETRIES 60 22 23 #define XDOMAIN_PROPERTIES_CHANGED_RETRIES 10 23 24 ··· 221 220 } 222 221 223 222 return 0; 223 + } 224 + 225 + static int tb_xdp_uuid_request(struct tb_ctl *ctl, u64 route, int retry, 226 + uuid_t *uuid) 227 + { 228 + struct tb_xdp_uuid_response res; 229 + struct tb_xdp_uuid req; 230 + int ret; 231 + 232 + memset(&req, 0, sizeof(req)); 233 + tb_xdp_fill_header(&req.hdr, route, retry % 4, UUID_REQUEST, 234 + sizeof(req)); 235 + 236 + memset(&res, 0, sizeof(res)); 237 + ret = __tb_xdomain_request(ctl, &req, sizeof(req), 238 + TB_CFG_PKG_XDOMAIN_REQ, &res, sizeof(res), 239 + TB_CFG_PKG_XDOMAIN_RESP, 240 + XDOMAIN_DEFAULT_TIMEOUT); 241 + if (ret) 242 + return ret; 243 + 244 + ret = tb_xdp_handle_error(&res.hdr); 245 + if (ret) 246 + return ret; 247 + 248 + uuid_copy(uuid, &res.src_uuid); 249 + return 0; 250 + } 251 + 252 + static int tb_xdp_uuid_response(struct tb_ctl *ctl, u64 route, u8 sequence, 253 + const uuid_t *uuid) 254 + { 255 + struct tb_xdp_uuid_response res; 256 + 257 + memset(&res, 0, sizeof(res)); 258 + tb_xdp_fill_header(&res.hdr, route, sequence, UUID_RESPONSE, 259 + sizeof(res)); 260 + 261 + uuid_copy(&res.src_uuid, uuid); 262 + res.src_route_hi = upper_32_bits(route); 263 + res.src_route_lo = lower_32_bits(route); 264 + 265 + return __tb_xdomain_response(ctl, &res, sizeof(res), 266 + TB_CFG_PKG_XDOMAIN_RESP); 224 267 } 225 268 226 269 static int tb_xdp_error_response(struct tb_ctl *ctl, u64 route, u8 sequence, ··· 557 512 break; 558 513 } 559 514 515 + case UUID_REQUEST_OLD: 516 + case UUID_REQUEST: 517 + ret = tb_xdp_uuid_response(ctl, route, sequence, uuid); 518 + break; 519 + 560 520 default: 521 + tb_xdp_error_response(ctl, route, sequence, 522 + ERROR_NOT_SUPPORTED); 561 523 break; 562 524 } 563 525 ··· 891 839 } 892 840 } 893 841 842 + static void tb_xdomain_get_uuid(struct work_struct *work) 843 + { 844 + struct tb_xdomain *xd = container_of(work, typeof(*xd), 845 + get_uuid_work.work); 846 + struct tb *tb = xd->tb; 847 + uuid_t uuid; 848 + int ret; 849 + 850 + ret = tb_xdp_uuid_request(tb->ctl, xd->route, xd->uuid_retries, &uuid); 851 + if (ret < 0) { 852 + if (xd->uuid_retries-- > 0) { 853 + queue_delayed_work(xd->tb->wq, &xd->get_uuid_work, 854 + msecs_to_jiffies(100)); 855 + } else { 856 + dev_dbg(&xd->dev, "failed to read remote UUID\n"); 857 + } 858 + return; 859 + } 860 + 861 + if (uuid_equal(&uuid, xd->local_uuid)) { 862 + dev_dbg(&xd->dev, "intra-domain loop detected\n"); 863 + return; 864 + } 865 + 866 + /* 867 + * If the UUID is different, there is another domain connected 868 + * so mark this one unplugged and wait for the connection 869 + * manager to replace it. 870 + */ 871 + if (xd->remote_uuid && !uuid_equal(&uuid, xd->remote_uuid)) { 872 + dev_dbg(&xd->dev, "remote UUID is different, unplugging\n"); 873 + xd->is_unplugged = true; 874 + return; 875 + } 876 + 877 + /* First time fill in the missing UUID */ 878 + if (!xd->remote_uuid) { 879 + xd->remote_uuid = kmemdup(&uuid, sizeof(uuid_t), GFP_KERNEL); 880 + if (!xd->remote_uuid) 881 + return; 882 + } 883 + 884 + /* Now we can start the normal properties exchange */ 885 + queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, 886 + msecs_to_jiffies(100)); 887 + queue_delayed_work(xd->tb->wq, &xd->get_properties_work, 888 + msecs_to_jiffies(1000)); 889 + } 890 + 894 891 static void tb_xdomain_get_properties(struct work_struct *work) 895 892 { 896 893 struct tb_xdomain *xd = container_of(work, typeof(*xd), ··· 1146 1045 1147 1046 static void start_handshake(struct tb_xdomain *xd) 1148 1047 { 1048 + xd->uuid_retries = XDOMAIN_UUID_RETRIES; 1149 1049 xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES; 1150 1050 xd->properties_changed_retries = XDOMAIN_PROPERTIES_CHANGED_RETRIES; 1151 1051 1152 - /* Start exchanging properties with the other host */ 1153 - queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, 1154 - msecs_to_jiffies(100)); 1155 - queue_delayed_work(xd->tb->wq, &xd->get_properties_work, 1156 - msecs_to_jiffies(1000)); 1052 + if (xd->needs_uuid) { 1053 + queue_delayed_work(xd->tb->wq, &xd->get_uuid_work, 1054 + msecs_to_jiffies(100)); 1055 + } else { 1056 + /* Start exchanging properties with the other host */ 1057 + queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, 1058 + msecs_to_jiffies(100)); 1059 + queue_delayed_work(xd->tb->wq, &xd->get_properties_work, 1060 + msecs_to_jiffies(1000)); 1061 + } 1157 1062 } 1158 1063 1159 1064 static void stop_handshake(struct tb_xdomain *xd) 1160 1065 { 1066 + xd->uuid_retries = 0; 1161 1067 xd->properties_retries = 0; 1162 1068 xd->properties_changed_retries = 0; 1163 1069 1070 + cancel_delayed_work_sync(&xd->get_uuid_work); 1164 1071 cancel_delayed_work_sync(&xd->get_properties_work); 1165 1072 cancel_delayed_work_sync(&xd->properties_changed_work); 1166 1073 } ··· 1211 1102 * other domain is reached). 1212 1103 * @route: Route string used to reach the other domain 1213 1104 * @local_uuid: Our local domain UUID 1214 - * @remote_uuid: UUID of the other domain 1105 + * @remote_uuid: UUID of the other domain (optional) 1215 1106 * 1216 1107 * Allocates new XDomain structure and returns pointer to that. The 1217 1108 * object must be released by calling tb_xdomain_put(). ··· 1230 1121 xd->route = route; 1231 1122 ida_init(&xd->service_ids); 1232 1123 mutex_init(&xd->lock); 1124 + INIT_DELAYED_WORK(&xd->get_uuid_work, tb_xdomain_get_uuid); 1233 1125 INIT_DELAYED_WORK(&xd->get_properties_work, tb_xdomain_get_properties); 1234 1126 INIT_DELAYED_WORK(&xd->properties_changed_work, 1235 1127 tb_xdomain_properties_changed); ··· 1239 1129 if (!xd->local_uuid) 1240 1130 goto err_free; 1241 1131 1242 - xd->remote_uuid = kmemdup(remote_uuid, sizeof(uuid_t), GFP_KERNEL); 1243 - if (!xd->remote_uuid) 1244 - goto err_free_local_uuid; 1132 + if (remote_uuid) { 1133 + xd->remote_uuid = kmemdup(remote_uuid, sizeof(uuid_t), 1134 + GFP_KERNEL); 1135 + if (!xd->remote_uuid) 1136 + goto err_free_local_uuid; 1137 + } else { 1138 + xd->needs_uuid = true; 1139 + } 1245 1140 1246 1141 device_initialize(&xd->dev); 1247 1142 xd->dev.parent = get_device(parent); ··· 1414 1299 xd = port->xdomain; 1415 1300 1416 1301 if (lookup->uuid) { 1417 - if (uuid_equal(xd->remote_uuid, lookup->uuid)) 1302 + if (xd->remote_uuid && 1303 + uuid_equal(xd->remote_uuid, lookup->uuid)) 1418 1304 return xd; 1419 1305 } else if (lookup->link && 1420 1306 lookup->link == xd->link &&
+8
include/linux/thunderbolt.h
··· 181 181 * @device_name: Name of the device (or %NULL if not known) 182 182 * @is_unplugged: The XDomain is unplugged 183 183 * @resume: The XDomain is being resumed 184 + * @needs_uuid: If the XDomain does not have @remote_uuid it will be 185 + * queried first 184 186 * @transmit_path: HopID which the remote end expects us to transmit 185 187 * @transmit_ring: Local ring (hop) where outgoing packets are pushed 186 188 * @receive_path: HopID which we expect the remote end to transmit ··· 191 189 * @properties: Properties exported by the remote domain 192 190 * @property_block_gen: Generation of @properties 193 191 * @properties_lock: Lock protecting @properties. 192 + * @get_uuid_work: Work used to retrieve @remote_uuid 193 + * @uuid_retries: Number of times left @remote_uuid is requested before 194 + * giving up 194 195 * @get_properties_work: Work used to get remote domain properties 195 196 * @properties_retries: Number of times left to read properties 196 197 * @properties_changed_work: Work used to notify the remote domain that ··· 225 220 const char *device_name; 226 221 bool is_unplugged; 227 222 bool resume; 223 + bool needs_uuid; 228 224 u16 transmit_path; 229 225 u16 transmit_ring; 230 226 u16 receive_path; ··· 233 227 struct ida service_ids; 234 228 struct tb_property_dir *properties; 235 229 u32 property_block_gen; 230 + struct delayed_work get_uuid_work; 231 + int uuid_retries; 236 232 struct delayed_work get_properties_work; 237 233 int properties_retries; 238 234 struct delayed_work properties_changed_work;