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

Commit 465f0c0b authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman
Browse files

cdc-acm: introduce a cool down



commit a4e7279cd1d19f48f0af2a10ed020febaa9ac092 upstream.

Immediate submission in case of a babbling device can lead
to a busy loop. Introducing a delayed work.

Signed-off-by: default avatarOliver Neukum <oneukum@suse.com>
Cc: stable <stable@vger.kernel.org>
Tested-by: default avatarJonas Karlsson <jonas.karlsson@actia.se>
Link: https://lore.kernel.org/r/20200415151358.32664-2-oneukum@suse.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 681c2486
Loading
Loading
Loading
Loading
+28 −2
Original line number Diff line number Diff line
@@ -412,9 +412,12 @@ static void acm_ctrl_irq(struct urb *urb)

exit:
	retval = usb_submit_urb(urb, GFP_ATOMIC);
	if (retval && retval != -EPERM)
	if (retval && retval != -EPERM && retval != -ENODEV)
		dev_err(&acm->control->dev,
			"%s - usb_submit_urb failed: %d\n", __func__, retval);
	else
		dev_vdbg(&acm->control->dev,
			"control resubmission terminated %d\n", retval);
}

static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
@@ -430,6 +433,8 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
			dev_err(&acm->data->dev,
				"urb %d failed submission with %d\n",
				index, res);
		} else {
			dev_vdbg(&acm->data->dev, "intended failure %d\n", res);
		}
		set_bit(index, &acm->read_urbs_free);
		return res;
@@ -472,6 +477,7 @@ static void acm_read_bulk_callback(struct urb *urb)
	int status = urb->status;
	bool stopped = false;
	bool stalled = false;
	bool cooldown = false;

	dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n",
		rb->index, urb->actual_length, status);
@@ -498,6 +504,14 @@ static void acm_read_bulk_callback(struct urb *urb)
			__func__, status);
		stopped = true;
		break;
	case -EOVERFLOW:
	case -EPROTO:
		dev_dbg(&acm->data->dev,
			"%s - cooling babbling device\n", __func__);
		usb_mark_last_busy(acm->dev);
		set_bit(rb->index, &acm->urbs_in_error_delay);
		cooldown = true;
		break;
	default:
		dev_dbg(&acm->data->dev,
			"%s - nonzero urb status received: %d\n",
@@ -519,9 +533,11 @@ static void acm_read_bulk_callback(struct urb *urb)
	 */
	smp_mb__after_atomic();

	if (stopped || stalled) {
	if (stopped || stalled || cooldown) {
		if (stalled)
			schedule_work(&acm->work);
		else if (cooldown)
			schedule_delayed_work(&acm->dwork, HZ / 2);
		return;
	}

@@ -573,6 +589,12 @@ static void acm_softint(struct work_struct *work)
		}
	}

	if (test_and_clear_bit(ACM_ERROR_DELAY, &acm->flags)) {
		for (i = 0; i < ACM_NR; i++)
			if (test_and_clear_bit(i, &acm->urbs_in_error_delay))
					acm_submit_read_urb(acm, i, GFP_NOIO);
	}

	if (test_and_clear_bit(EVENT_TTY_WAKEUP, &acm->flags))
		tty_port_tty_wakeup(&acm->port);
}
@@ -1365,6 +1387,7 @@ static int acm_probe(struct usb_interface *intf,
	acm->readsize = readsize;
	acm->rx_buflimit = num_rx_buf;
	INIT_WORK(&acm->work, acm_softint);
	INIT_DELAYED_WORK(&acm->dwork, acm_softint);
	init_waitqueue_head(&acm->wioctl);
	spin_lock_init(&acm->write_lock);
	spin_lock_init(&acm->read_lock);
@@ -1574,6 +1597,7 @@ static void acm_disconnect(struct usb_interface *intf)

	acm_kill_urbs(acm);
	cancel_work_sync(&acm->work);
	cancel_delayed_work_sync(&acm->dwork);

	tty_unregister_device(acm_tty_driver, acm->minor);

@@ -1616,6 +1640,8 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)

	acm_kill_urbs(acm);
	cancel_work_sync(&acm->work);
	cancel_delayed_work_sync(&acm->dwork);
	acm->urbs_in_error_delay = 0;

	return 0;
}
+4 −1
Original line number Diff line number Diff line
@@ -108,8 +108,11 @@ struct acm {
	unsigned long flags;
#		define EVENT_TTY_WAKEUP	0
#		define EVENT_RX_STALL	1
#		define ACM_ERROR_DELAY	3
	unsigned long urbs_in_error_delay;		/* these need to be restarted after a delay */
	struct usb_cdc_line_coding line;		/* bits, stop, parity */
	struct work_struct work;			/* work queue entry for line discipline waking up */
	struct work_struct work;			/* work queue entry for various purposes*/
	struct delayed_work dwork;			/* for cool downs needed in error recovery */
	unsigned int ctrlin;				/* input control lines (DCD, DSR, RI, break, overruns) */
	unsigned int ctrlout;				/* output control lines (DTR, RTS) */
	struct async_icount iocount;			/* counters for control line changes */