RE: [RFC] xhci: Fix command ring replay after resume.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Sarah,

This patch looks OK to me and I've verified S3 with USB2.0/3.0 devices,
it works fine.

However, the redundant variable val_64 in xhci_resume() should be
removed. It shows warning message when I compile the driver:
Drivers/usb/host/xhci.c:698: warning: unused variable 'val_64' 

Thanks & Best regards,
Andiry
 
> -----Original Message-----
> From: Sarah Sharp [mailto:sarah.a.sharp@xxxxxxxxxxxxxxx]
> Sent: Saturday, November 13, 2010 5:58 AM
> To: Xu, Andiry
> Cc: linux-usb@xxxxxxxxxxxxxxx
> Subject: [RFC] xhci: Fix command ring replay after resume.
> 
> Andiry,
> 
> I noticed an issue with resume from suspend when I was testing your
> spinlock release patch.  I'm not affected because my xHCI host fails
to
> restore the registers, but I know you have a box that the xHCI host
> doesn't fail in.  Can you test this patch?
> 
> Sarah Sharp
> 
> 8<------------------------------------------------------->8
> 
> Andiry's xHCI bus suspend patch introduced the possibly of a host
> controller replaying old commands on the command ring, if the host
> successfully restores the registers after a resume.
> 
> After a resume from suspend, the xHCI driver must restore the
registers,
> including the command ring pointer.  I had suggested that Andiry set
the
> command ring pointer to the current command ring dequeue pointer, so
that
> the driver wouldn't have to zero the command ring.
> 
> Unfortunately, setting the command ring pointer to the current dequeue
> pointer won't work because the register assumes the pointer is 64-byte
> aligned, and TRBs on the command ring are 16-byte aligned.  The lower
> seven bits will always be masked off, leading to the written pointer
being
> up to 3 TRBs behind the intended pointer.
> 
> Here's a log excerpt.  On init, the xHCI driver places a
vendor-specific
> command on the command ring:
> 
> [  215.750958] xhci_hcd 0000:01:00.0: Vendor specific event TRB type =
48
> [  215.750960] xhci_hcd 0000:01:00.0: NEC firmware version 30.25
> [  215.750962] xhci_hcd 0000:01:00.0: Command ring deq = 0x3781e010
(DMA)
> 
> When we resume, the command ring dequeue pointer to be written should
have
> been 0x3781e010.  Instead, it's 0x3781e000:
> 
> [  235.557846] xhci_hcd 0000:01:00.0: // Setting command ring address
to
> 0x3781e001
> [  235.557848] xhci_hcd 0000:01:00.0: `MEM_WRITE_DWORD(3'b000,
> 64'hffffc900100bc038, 64'h3781e001, 4'hf);
> [  235.557850] xhci_hcd 0000:01:00.0: `MEM_WRITE_DWORD(3'b000,
> 32'hffffc900100bc020, 32'h204, 4'hf);
> [  235.557866] usb usb9: root hub lost power or was reset
> 
> (I can't see the results of this bug because the xHCI restore always
fails
> on this box, and the xHCI driver re-allocates everything.)
> 
> The fix is to zero the command ring and put the software and hardware
> enqueue and dequeue pointer back to the beginning of the ring.  We do
this
> before the system suspends, to be paranoid and prevent the BIOS from
> starting the host without clearing the command ring pointer, which
might
> cause the host to muck with stale memory.  (The pointer isn't required
to
> be in the suspend power well, but it could be.)  The command ring
pointer
> is set again after the host resumes.
> 
> Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
> Cc: Andiry Xu <andiry.xu@xxxxxxx>
> 
> Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
> ---
>  drivers/usb/host/xhci.c |   70
+++++++++++++++++++++++++++++++++++++++++-
> -----
>  1 files changed, 61 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index 7c8d70f..de1ca5f 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -577,6 +577,65 @@ static void xhci_restore_registers(struct
xhci_hcd
> *xhci)
>  	xhci_write_64(xhci, xhci->s3.erst_base,
&xhci->ir_set->erst_base);
>  }
> 
> +static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)
> +{
> +	u64	val_64;
> +
> +	/* step 2: initialize command ring buffer */
> +	val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
> +	val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
> +		(xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
> +				      xhci->cmd_ring->dequeue) &
> +		 (u64) ~CMD_RING_RSVD_BITS) |
> +		xhci->cmd_ring->cycle_state;
> +	xhci_dbg(xhci, "// Setting command ring address to 0x%llx\n",
> +			(long unsigned long) val_64);
> +	xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
> +}
> +
> +/*
> + * The whole command ring must be cleared to zero when we suspend the
> host.
> + *
> + * The host doesn't save the command ring pointer in the suspend
well, so
> we
> + * need to re-program it on resume.  Unfortunately, the pointer must
be
> 64-byte
> + * aligned, because of the reserved bits in the command ring dequeue
> pointer
> + * register.  Therefore, we can't just set the dequeue pointer back
in
> the
> + * middle of the ring (TRBs are 16-byte aligned).
> + */
> +static void xhci_clear_command_ring(struct xhci_hcd *xhci)
> +{
> +	struct xhci_ring *ring;
> +	struct xhci_segment *seg;
> +
> +	ring = xhci->cmd_ring;
> +	seg = ring->deq_seg;
> +	do {
> +		memset(seg->trbs, 0, SEGMENT_SIZE);
> +		seg = seg->next;
> +	} while (seg != ring->deq_seg);
> +
> +	/* Reset the software enqueue and dequeue pointers */
> +	ring->deq_seg = ring->first_seg;
> +	ring->dequeue = ring->first_seg->trbs;
> +	ring->enq_seg = ring->deq_seg;
> +	ring->enqueue = ring->dequeue;
> +
> +	/*
> +	 * Ring is now zeroed, so the HW should look for change of
ownership
> +	 * when the cycle bit is set to 1.
> +	 */
> +	ring->cycle_state = 1;
> +
> +	/*
> +	 * Reset the hardware dequeue pointer.
> +	 * Yes, this will need to be re-written after resume, but we're
> paranoid
> +	 * and want to make sure the hardware doesn't access bogus
memory
> +	 * because, say, the BIOS or an SMI started the host without
> changing
> +	 * the command ring pointers.
> +	 */
> +	xhci_set_cmd_ring_deq(xhci);
> +}
> +
>  /*
>   * Stop HC (not bus-specific)
>   *
> @@ -604,6 +663,7 @@ int xhci_suspend(struct xhci_hcd *xhci)
>  		spin_unlock_irq(&xhci->lock);
>  		return -ETIMEDOUT;
>  	}
> +	xhci_clear_command_ring(xhci);
> 
>  	/* step 3: save registers */
>  	xhci_save_registers(xhci);
> @@ -648,15 +708,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool
> hibernated)
>  		/* step 1: restore register */
>  		xhci_restore_registers(xhci);
>  		/* step 2: initialize command ring buffer */
> -		val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
> -		val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
> -			 (xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
> -					       xhci->cmd_ring->dequeue)
&
> -			 (u64) ~CMD_RING_RSVD_BITS) |
> -			 xhci->cmd_ring->cycle_state;
> -		xhci_dbg(xhci, "// Setting command ring address to
0x%llx\n",
> -				(long unsigned long) val_64);
> -		xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
> +		xhci_set_cmd_ring_deq(xhci);
>  		/* step 3: restore state and start state*/
>  		/* step 3: set CRS flag */
>  		command = xhci_readl(xhci, &xhci->op_regs->command);
> --
> 1.6.3.3
> 


--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux