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

mei: retry connect if interrupted by link reset

When device is in D3cold the connect message will wake device
and cause link reset.
Link reset flow cleans all queues and wakes all waiters.
Retry the connect flow if connect is failed and link reset is detected.

Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://lore.kernel.org/r/20250918130435.3327400-4-alexander.usyskin@intel.com

authored by

Alexander Usyskin and committed by
Greg Kroah-Hartman
2b5c4cb2 bb29fc32

+32
+2
drivers/misc/mei/hw.h
··· 27 27 #define MKHI_RCV_TIMEOUT 500 /* receive timeout in msec */ 28 28 #define MKHI_RCV_TIMEOUT_SLOW 10000 /* receive timeout in msec, slow FW */ 29 29 30 + #define MEI_LINK_RESET_WAIT_TIMEOUT_MSEC 500 /* Max wait timeout for link reset, in msec */ 31 + 30 32 /* 31 33 * FW page size for DMA allocations 32 34 */
+2
drivers/misc/mei/init.c
··· 400 400 init_waitqueue_head(&dev->wait_pg); 401 401 init_waitqueue_head(&dev->wait_hbm_start); 402 402 dev->dev_state = MEI_DEV_UNINITIALIZED; 403 + init_waitqueue_head(&dev->wait_dev_state); 403 404 dev->reset_count = 0; 404 405 405 406 INIT_LIST_HEAD(&dev->write_list); ··· 443 442 dev->timeouts.hbm = mei_secs_to_jiffies(MEI_HBM_TIMEOUT); 444 443 dev->timeouts.mkhi_recv = msecs_to_jiffies(MKHI_RCV_TIMEOUT); 445 444 } 445 + dev->timeouts.link_reset_wait = msecs_to_jiffies(MEI_LINK_RESET_WAIT_TIMEOUT_MSEC); 446 446 } 447 447 EXPORT_SYMBOL_GPL(mei_device_init);
+25
drivers/misc/mei/main.c
··· 423 423 cl->state != MEI_FILE_DISCONNECTED) 424 424 return -EBUSY; 425 425 426 + retry: 426 427 /* find ME client we're trying to connect to */ 427 428 me_cl = mei_me_cl_by_uuid(dev, in_client_uuid); 428 429 if (!me_cl) { ··· 454 453 cl_dbg(dev, cl, "Can connect?\n"); 455 454 456 455 rets = mei_cl_connect(cl, me_cl, file); 456 + 457 + if (rets && cl->status == -EFAULT && 458 + (dev->dev_state == MEI_DEV_RESETTING || 459 + dev->dev_state == MEI_DEV_INIT_CLIENTS)) { 460 + /* in link reset, wait for it completion */ 461 + mutex_unlock(&dev->device_lock); 462 + rets = wait_event_interruptible_timeout(dev->wait_dev_state, 463 + dev->dev_state == MEI_DEV_ENABLED, 464 + dev->timeouts.link_reset_wait); 465 + mutex_lock(&dev->device_lock); 466 + if (rets < 0) { 467 + if (signal_pending(current)) 468 + rets = -EINTR; 469 + goto end; 470 + } 471 + if (dev->dev_state != MEI_DEV_ENABLED) { 472 + rets = -ETIME; 473 + goto end; 474 + } 475 + mei_me_cl_put(me_cl); 476 + goto retry; 477 + } 457 478 458 479 end: 459 480 mei_me_cl_put(me_cl); ··· 1142 1119 return; 1143 1120 1144 1121 dev->dev_state = state; 1122 + 1123 + wake_up_interruptible_all(&dev->wait_dev_state); 1145 1124 1146 1125 if (!dev->cdev) 1147 1126 return;
+3
drivers/misc/mei/mei_dev.h
··· 466 466 unsigned int d0i3; /* D0i3 set/unset max response time, in jiffies */ 467 467 unsigned long hbm; /* HBM operation timeout, in jiffies */ 468 468 unsigned long mkhi_recv; /* receive timeout, in jiffies */ 469 + unsigned long link_reset_wait; /* link reset wait timeout, in jiffies */ 469 470 }; 470 471 471 472 /** ··· 497 496 * 498 497 * @reset_count : number of consecutive resets 499 498 * @dev_state : device state 499 + * @wait_dev_state: wait queue for device state change 500 500 * @hbm_state : state of host bus message protocol 501 501 * @pxp_mode : PXP device mode 502 502 * @init_clients_timer : HBM init handshake timeout ··· 590 588 */ 591 589 unsigned long reset_count; 592 590 enum mei_dev_state dev_state; 591 + wait_queue_head_t wait_dev_state; 593 592 enum mei_hbm_state hbm_state; 594 593 enum mei_dev_pxp_mode pxp_mode; 595 594 u16 init_clients_timer;