Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0db878a3 authored by Jack Pham's avatar Jack Pham Committed by Azhar Shaikh
Browse files

usb: gadget: storage: Prevent race condition from stalling thread



Parts of the mass storage gadget driver are prone to race conditions
which could leave the main worker thread in an unwakeable state.
This will lead to the function to not be responsive to the host's
command requests and will result in a host-issued reset. Protect
concurrent accesses to certain state variables via spinlock to
prevent this from happening.

CRs-Fixed: 480025
Change-Id: I5232d9367ff311817f63f8bfbea0c78ef2b5a4db
Signed-off-by: default avatarJack Pham <jackp@codeaurora.org>
Signed-off-by: default avatarAzhar Shaikh <azhars@codeaurora.org>
parent a5f9d251
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -612,12 +612,18 @@ static int sleep_thread(struct fsg_common *common, bool can_freeze)
			rc = -EINTR;
			break;
		}
		if (common->thread_wakeup_needed)
		spin_lock_irq(&common->lock);
		if (common->thread_wakeup_needed) {
			spin_unlock_irq(&common->lock);
			break;
		}
		spin_unlock_irq(&common->lock);
		schedule();
	}
	__set_current_state(TASK_RUNNING);
	spin_lock_irq(&common->lock);
	common->thread_wakeup_needed = 0;
	spin_unlock_irq(&common->lock);
	smp_rmb();	/* ensure the latest bh->state is visible */
	return rc;
}
@@ -678,12 +684,17 @@ static int do_read(struct fsg_common *common)
			     curlun->file_length - file_offset);

		/* Wait for the next buffer to become available */
		spin_lock_irq(&common->lock);
		bh = common->next_buffhd_to_fill;
		while (bh->state != BUF_STATE_EMPTY) {
			spin_unlock_irq(&common->lock);
			rc = sleep_thread(common, false);
			if (rc)
				return rc;

			spin_lock_irq(&common->lock);
		}
		spin_unlock_irq(&common->lock);

		/*
		 * If we were asked to read past the end of file,
@@ -695,8 +706,10 @@ static int do_read(struct fsg_common *common)
			curlun->sense_data_info =
					file_offset >> curlun->blkbits;
			curlun->info_valid = 1;
			spin_lock_irq(&common->lock);
			bh->inreq->length = 0;
			bh->state = BUF_STATE_FULL;
			spin_unlock_irq(&common->lock);
			break;
		}

@@ -727,8 +740,10 @@ static int do_read(struct fsg_common *common)
		 * equal to the buffer size, which is divisible by the
		 * bulk-in maxpacket size.
		 */
		spin_lock_irq(&common->lock);
		bh->inreq->length = nread;
		bh->state = BUF_STATE_FULL;
		spin_unlock_irq(&common->lock);

		/* If an error occurred, report it and its position */
		if (nread < amount) {
@@ -1621,12 +1636,17 @@ static int send_status(struct fsg_common *common)
	u32			sd, sdinfo = 0;

	/* Wait for the next buffer to become available */
	spin_lock_irq(&common->lock);
	bh = common->next_buffhd_to_fill;
	while (bh->state != BUF_STATE_EMPTY) {
		spin_unlock_irq(&common->lock);
		rc = sleep_thread(common, true);
		if (rc)
			return rc;

		spin_lock_irq(&common->lock);
	}
	spin_unlock_irq(&common->lock);

	if (curlun) {
		sd = curlun->sense_data;
@@ -1823,13 +1843,19 @@ static int do_scsi_command(struct fsg_common *common)
	dump_cdb(common);

	/* Wait for the next buffer to become available for data or status */
	spin_lock_irq(&common->lock);
	bh = common->next_buffhd_to_fill;
	common->next_buffhd_to_drain = bh;
	while (bh->state != BUF_STATE_EMPTY) {
		spin_unlock_irq(&common->lock);
		rc = sleep_thread(common, true);
		if (rc)
			return rc;

		spin_lock_irq(&common->lock);
	}
	spin_unlock_irq(&common->lock);

	common->phase_error = 0;
	common->short_packet_received = 0;

@@ -2170,12 +2196,17 @@ static int get_next_command(struct fsg_common *common)
	int			rc = 0;

	/* Wait for the next buffer to become available */
	spin_lock_irq(&common->lock);
	bh = common->next_buffhd_to_fill;
	while (bh->state != BUF_STATE_EMPTY) {
		spin_unlock_irq(&common->lock);
		rc = sleep_thread(common, true);
		if (rc)
			return rc;

		spin_lock_irq(&common->lock);
	}
	spin_unlock_irq(&common->lock);

	/* Queue a request to read a Bulk-only CBW */
	set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN);
@@ -2190,14 +2221,23 @@ static int get_next_command(struct fsg_common *common)
	 */

	/* Wait for the CBW to arrive */
	spin_lock_irq(&common->lock);
	while (bh->state != BUF_STATE_FULL) {
		spin_unlock_irq(&common->lock);
		rc = sleep_thread(common, true);
		if (rc)
			return rc;

		spin_lock_irq(&common->lock);
	}
	spin_unlock_irq(&common->lock);

	smp_rmb();
	rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO;

	spin_lock_irq(&common->lock);
	bh->state = BUF_STATE_EMPTY;
	spin_unlock_irq(&common->lock);

	return rc;
}
@@ -2388,10 +2428,13 @@ static void handle_exception(struct fsg_common *common)
		/* Wait until everything is idle */
		for (;;) {
			int num_active = 0;
			spin_lock_irq(&common->lock);
			for (i = 0; i < common->fsg_num_buffers; ++i) {
				bh = &common->buffhds[i];
				num_active += bh->inreq_busy + bh->outreq_busy;
			}
			spin_unlock_irq(&common->lock);

			if (num_active == 0)
				break;
			if (sleep_thread(common, true))