tty: serial: imx: console write routing is unsafe on SMP
The console feature's write routing is unsafe on SMP with the startup/shutdown call. There could be several consumers of the console * the kernel printk * the init process using /dev/kmsg to call printk to show log * shell, which open /dev/console and write with sys_write() The shell goes into the normal uart open/write routing, but the other two go into the console operations. The open routing calls imx serial startup, which will write USR1/2 register without any lock and critical with imx_console_write call. Add a spin_lock for startup/shutdown/console_write routing. This patch is a port from Freescale's Android kernel. Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com> Tested-by: Dirk Behme <dirk.behme@de.bosch.com> CC: Sascha Hauer <s.hauer@pengutronix.de> Acked-by: Shawn Guo <shawn.guo@linaro.org> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
					parent
					
						
							
								fea7a08acb
							
						
					
				
			
			
				commit
				
					
						9ec1882df2
					
				
			
		
					 1 changed files with 11 additions and 1 deletions
				
			
		|  | @ -754,6 +754,7 @@ static int imx_startup(struct uart_port *port) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	spin_lock_irqsave(&sport->port.lock, flags); | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Finally, clear and enable interrupts | 	 * Finally, clear and enable interrupts | ||||||
| 	 */ | 	 */ | ||||||
|  | @ -807,7 +808,6 @@ static int imx_startup(struct uart_port *port) | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Enable modem status interrupts | 	 * Enable modem status interrupts | ||||||
| 	 */ | 	 */ | ||||||
| 	spin_lock_irqsave(&sport->port.lock,flags); |  | ||||||
| 	imx_enable_ms(&sport->port); | 	imx_enable_ms(&sport->port); | ||||||
| 	spin_unlock_irqrestore(&sport->port.lock,flags); | 	spin_unlock_irqrestore(&sport->port.lock,flags); | ||||||
| 
 | 
 | ||||||
|  | @ -837,10 +837,13 @@ static void imx_shutdown(struct uart_port *port) | ||||||
| { | { | ||||||
| 	struct imx_port *sport = (struct imx_port *)port; | 	struct imx_port *sport = (struct imx_port *)port; | ||||||
| 	unsigned long temp; | 	unsigned long temp; | ||||||
|  | 	unsigned long flags; | ||||||
| 
 | 
 | ||||||
|  | 	spin_lock_irqsave(&sport->port.lock, flags); | ||||||
| 	temp = readl(sport->port.membase + UCR2); | 	temp = readl(sport->port.membase + UCR2); | ||||||
| 	temp &= ~(UCR2_TXEN); | 	temp &= ~(UCR2_TXEN); | ||||||
| 	writel(temp, sport->port.membase + UCR2); | 	writel(temp, sport->port.membase + UCR2); | ||||||
|  | 	spin_unlock_irqrestore(&sport->port.lock, flags); | ||||||
| 
 | 
 | ||||||
| 	if (USE_IRDA(sport)) { | 	if (USE_IRDA(sport)) { | ||||||
| 		struct imxuart_platform_data *pdata; | 		struct imxuart_platform_data *pdata; | ||||||
|  | @ -869,12 +872,14 @@ static void imx_shutdown(struct uart_port *port) | ||||||
| 	 * Disable all interrupts, port and break condition. | 	 * Disable all interrupts, port and break condition. | ||||||
| 	 */ | 	 */ | ||||||
| 
 | 
 | ||||||
|  | 	spin_lock_irqsave(&sport->port.lock, flags); | ||||||
| 	temp = readl(sport->port.membase + UCR1); | 	temp = readl(sport->port.membase + UCR1); | ||||||
| 	temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); | 	temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); | ||||||
| 	if (USE_IRDA(sport)) | 	if (USE_IRDA(sport)) | ||||||
| 		temp &= ~(UCR1_IREN); | 		temp &= ~(UCR1_IREN); | ||||||
| 
 | 
 | ||||||
| 	writel(temp, sport->port.membase + UCR1); | 	writel(temp, sport->port.membase + UCR1); | ||||||
|  | 	spin_unlock_irqrestore(&sport->port.lock, flags); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
|  | @ -1217,6 +1222,9 @@ imx_console_write(struct console *co, const char *s, unsigned int count) | ||||||
| 	struct imx_port *sport = imx_ports[co->index]; | 	struct imx_port *sport = imx_ports[co->index]; | ||||||
| 	struct imx_port_ucrs old_ucr; | 	struct imx_port_ucrs old_ucr; | ||||||
| 	unsigned int ucr1; | 	unsigned int ucr1; | ||||||
|  | 	unsigned long flags; | ||||||
|  | 
 | ||||||
|  | 	spin_lock_irqsave(&sport->port.lock, flags); | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 *	First, save UCR1/2/3 and then disable interrupts | 	 *	First, save UCR1/2/3 and then disable interrupts | ||||||
|  | @ -1242,6 +1250,8 @@ imx_console_write(struct console *co, const char *s, unsigned int count) | ||||||
| 	while (!(readl(sport->port.membase + USR2) & USR2_TXDC)); | 	while (!(readl(sport->port.membase + USR2) & USR2_TXDC)); | ||||||
| 
 | 
 | ||||||
| 	imx_port_ucrs_restore(&sport->port, &old_ucr); | 	imx_port_ucrs_restore(&sport->port, &old_ucr); | ||||||
|  | 
 | ||||||
|  | 	spin_unlock_irqrestore(&sport->port.lock, flags); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Xinyu Chen
				Xinyu Chen