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

Commit 5eb9291c authored by Sam Bradshaw's avatar Sam Bradshaw Committed by Jens Axboe
Browse files

mtip32xx: mtip_async_complete() bug fixes



This patch fixes 2 issues in the fast completion path:
1) Possible double completions / double dma_unmap_sg() calls due to lack
of atomicity in the check and subsequent dereference of the upper layer
callback function. Fixed with cmpxchg before unmap and callback.
2) Regression in unaligned IO constraining workaround for p420m devices.
Fixed by checking if IO is unaligned and using proper semaphore if so.

Signed-off-by: default avatarSam Bradshaw <sbradshaw@micron.com>
Cc: stable@kernel.org
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent 368c89d7
Loading
Loading
Loading
Loading
+49 −38
Original line number Original line Diff line number Diff line
@@ -252,39 +252,46 @@ static void mtip_async_complete(struct mtip_port *port,
				void *data,
				void *data,
				int status)
				int status)
{
{
	struct mtip_cmd *command;
	struct mtip_cmd *cmd;
	struct driver_data *dd = data;
	struct driver_data *dd = data;
	int cb_status = status ? -EIO : 0;
	int unaligned, cb_status = status ? -EIO : 0;
	void (*func)(void *, int);


	if (unlikely(!dd) || unlikely(!port))
	if (unlikely(!dd) || unlikely(!port))
		return;
		return;


	command = &port->commands[tag];
	cmd = &port->commands[tag];


	if (unlikely(status == PORT_IRQ_TF_ERR)) {
	if (unlikely(status == PORT_IRQ_TF_ERR)) {
		dev_warn(&port->dd->pdev->dev,
		dev_warn(&port->dd->pdev->dev,
			"Command tag %d failed due to TFE\n", tag);
			"Command tag %d failed due to TFE\n", tag);
	}
	}


	/* Unmap the DMA scatter list entries */
	/* Clear the active flag */
	dma_unmap_sg(&dd->pdev->dev,
	atomic_set(&port->commands[tag].active, 0);
		command->sg,
		command->scatter_ents,
		command->direction);


	/* Upper layer callback */
	/* Upper layer callback */
	if (likely(command->async_callback))
	func = cmd->async_callback;
		command->async_callback(command->async_data, cb_status);
	if (likely(func && cmpxchg(&cmd->async_callback, func, 0) == func)) {


	command->async_callback = NULL;
		/* Unmap the DMA scatter list entries */
	command->comp_func = NULL;
		dma_unmap_sg(&dd->pdev->dev,
			cmd->sg,
			cmd->scatter_ents,
			cmd->direction);


	/* Clear the allocated and active bits for the command */
		func(cmd->async_data, cb_status);
	atomic_set(&port->commands[tag].active, 0);
		unaligned = cmd->unaligned;

		/* Clear the allocated bit for the command */
		release_slot(port, tag);
		release_slot(port, tag);


		if (unlikely(unaligned))
			up(&port->cmd_slot_unal);
		else
			up(&port->cmd_slot);
			up(&port->cmd_slot);
	}
	}
}


/*
/*
 * This function is called for clean the pending command in the
 * This function is called for clean the pending command in the
@@ -660,11 +667,12 @@ static void mtip_timeout_function(unsigned long int data)
{
{
	struct mtip_port *port = (struct mtip_port *) data;
	struct mtip_port *port = (struct mtip_port *) data;
	struct host_to_dev_fis *fis;
	struct host_to_dev_fis *fis;
	struct mtip_cmd *command;
	struct mtip_cmd *cmd;
	int tag, cmdto_cnt = 0;
	int unaligned, tag, cmdto_cnt = 0;
	unsigned int bit, group;
	unsigned int bit, group;
	unsigned int num_command_slots;
	unsigned int num_command_slots;
	unsigned long to, tagaccum[SLOTBITS_IN_LONGS];
	unsigned long to, tagaccum[SLOTBITS_IN_LONGS];
	void (*func)(void *, int);


	if (unlikely(!port))
	if (unlikely(!port))
		return;
		return;
@@ -694,8 +702,8 @@ static void mtip_timeout_function(unsigned long int data)
			group = tag >> 5;
			group = tag >> 5;
			bit = tag & 0x1F;
			bit = tag & 0x1F;


			command = &port->commands[tag];
			cmd = &port->commands[tag];
			fis = (struct host_to_dev_fis *) command->command;
			fis = (struct host_to_dev_fis *) cmd->command;


			set_bit(tag, tagaccum);
			set_bit(tag, tagaccum);
			cmdto_cnt++;
			cmdto_cnt++;
@@ -709,29 +717,32 @@ static void mtip_timeout_function(unsigned long int data)
			 */
			 */
			writel(1 << bit, port->completed[group]);
			writel(1 << bit, port->completed[group]);


			/* Clear the active flag for the command */
			atomic_set(&port->commands[tag].active, 0);

			func = cmd->async_callback;
			if (func &&
			    cmpxchg(&cmd->async_callback, func, 0) == func) {

				/* Unmap the DMA scatter list entries */
				/* Unmap the DMA scatter list entries */
				dma_unmap_sg(&port->dd->pdev->dev,
				dma_unmap_sg(&port->dd->pdev->dev,
					command->sg,
						cmd->sg,
					command->scatter_ents,
						cmd->scatter_ents,
					command->direction);
						cmd->direction);

			/* Call the async completion callback. */
			if (likely(command->async_callback))
				command->async_callback(command->async_data,
							 -EIO);
			command->async_callback = NULL;
			command->comp_func = NULL;


			/*
				func(cmd->async_data, -EIO);
			 * Clear the allocated bit and active tag for the
				unaligned = cmd->unaligned;
			 * command.

			 */
				/* Clear the allocated bit for the command. */
			atomic_set(&port->commands[tag].active, 0);
				release_slot(port, tag);
				release_slot(port, tag);


				if (unaligned)
					up(&port->cmd_slot_unal);
				else
					up(&port->cmd_slot);
					up(&port->cmd_slot);
			}
			}
		}
		}
	}


	if (cmdto_cnt) {
	if (cmdto_cnt) {
		print_tags(port->dd, "timed out", tagaccum, cmdto_cnt);
		print_tags(port->dd, "timed out", tagaccum, cmdto_cnt);
+1 −1
Original line number Original line Diff line number Diff line
@@ -92,7 +92,7 @@


/* Driver name and version strings */
/* Driver name and version strings */
#define MTIP_DRV_NAME		"mtip32xx"
#define MTIP_DRV_NAME		"mtip32xx"
#define MTIP_DRV_VERSION	"1.3.0"
#define MTIP_DRV_VERSION	"1.3.1"


/* Maximum number of minor device numbers per device. */
/* Maximum number of minor device numbers per device. */
#define MTIP_MAX_MINORS		16
#define MTIP_MAX_MINORS		16