mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	cdc-acm: introduce a cool down
Immediate submission in case of a babbling device can lead to a busy loop. Introducing a delayed work. Signed-off-by: Oliver Neukum <oneukum@suse.com> Cc: stable <stable@vger.kernel.org> Tested-by: Jonas Karlsson <jonas.karlsson@actia.se> Link: https://lore.kernel.org/r/20200415151358.32664-2-oneukum@suse.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									0afccd7601
								
							
						
					
					
						commit
						a4e7279cd1
					
				
					 2 changed files with 32 additions and 3 deletions
				
			
		| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -471,6 +476,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);
 | 
			
		||||
| 
						 | 
				
			
			@ -497,6 +503,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",
 | 
			
		||||
| 
						 | 
				
			
			@ -518,9 +532,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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -567,6 +583,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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1333,6 +1355,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);
 | 
			
		||||
| 
						 | 
				
			
			@ -1542,6 +1565,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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1584,6 +1608,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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -109,8 +109,11 @@ struct acm {
 | 
			
		|||
#		define EVENT_TTY_WAKEUP	0
 | 
			
		||||
#		define EVENT_RX_STALL	1
 | 
			
		||||
#		define ACM_THROTTLED	2
 | 
			
		||||
#		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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue