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

Commit 2f78d8e2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull IEEE 1394 (FireWire) subsystem updates from Stefan Richter:

 - Fix mismatch between DMA mapping direction (was wrong) and DMA
   synchronization direction (was correct) of isochronous reception
   buffers of userspace drivers if vma-mapped for R/W access.  For
   example, libdc1394 was affected.

 - more consistent retry stategy in device discovery/ rediscovery, and
   improved failure diagnostics

 - various small cleanups, e.g. use SCSI layer's DMA mapping API in
   firewire-sbp2

* tag 'firewire-updates' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394:
  firewire: sbp2: document the absence of alignment requirements
  firewire: sbp2: remove superfluous blk_queue_max_segment_size() call
  firewire: sbp2: use scsi_dma_(un)map
  firewire: sbp2: give correct DMA device to scsi framework
  firewire: core: fw_device_refresh(): clean up error handling
  firewire: core: log config rom reading errors
  firewire: core: log error in case of failed bus manager lock
  firewire: move rcode_string() to core
  firewire: core: improve reread_config_rom() interface
  firewire: core: wait for inaccessible devices after bus reset
  firewire: ohci: omit spinlock IRQ flags where possible
  firewire: ohci: correct signedness of a local variable
  firewire: core: fix DMA mapping direction
  firewire: use module_pci_driver
parents f2fde3a6 26c72e22
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -421,8 +421,8 @@ static void bm_work(struct work_struct *work)
			 * root, and thus, IRM.
			 */
			new_root_id = local_id;
			fw_notice(card, "%s, making local node (%02x) root\n",
				  "BM lock failed", new_root_id);
			fw_notice(card, "BM lock failed (%s), making local node (%02x) root\n",
				  fw_rcode_string(rcode), new_root_id);
			goto pick_me;
		}
	} else if (card->bm_generation != generation) {
+41 −10
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <linux/compat.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/firewire.h>
#include <linux/firewire-cdev.h>
@@ -70,6 +71,7 @@ struct client {
	u64 iso_closure;
	struct fw_iso_buffer buffer;
	unsigned long vm_start;
	bool buffer_is_mapped;

	struct list_head phy_receiver_link;
	u64 phy_receiver_closure;
@@ -959,11 +961,20 @@ static void iso_mc_callback(struct fw_iso_context *context,
		    sizeof(e->interrupt), NULL, 0);
}

static enum dma_data_direction iso_dma_direction(struct fw_iso_context *context)
{
		if (context->type == FW_ISO_CONTEXT_TRANSMIT)
			return DMA_TO_DEVICE;
		else
			return DMA_FROM_DEVICE;
}

static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
{
	struct fw_cdev_create_iso_context *a = &arg->create_iso_context;
	struct fw_iso_context *context;
	fw_iso_callback_t cb;
	int ret;

	BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT ||
		     FW_CDEV_ISO_CONTEXT_RECEIVE  != FW_ISO_CONTEXT_RECEIVE  ||
@@ -1004,8 +1015,21 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
	if (client->iso_context != NULL) {
		spin_unlock_irq(&client->lock);
		fw_iso_context_destroy(context);

		return -EBUSY;
	}
	if (!client->buffer_is_mapped) {
		ret = fw_iso_buffer_map_dma(&client->buffer,
					    client->device->card,
					    iso_dma_direction(context));
		if (ret < 0) {
			spin_unlock_irq(&client->lock);
			fw_iso_context_destroy(context);

			return ret;
		}
		client->buffer_is_mapped = true;
	}
	client->iso_closure = a->closure;
	client->iso_context = context;
	spin_unlock_irq(&client->lock);
@@ -1651,7 +1675,6 @@ static long fw_device_op_compat_ioctl(struct file *file,
static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
{
	struct client *client = file->private_data;
	enum dma_data_direction direction;
	unsigned long size;
	int page_count, ret;

@@ -1674,20 +1697,28 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
	if (size & ~PAGE_MASK)
		return -EINVAL;

	if (vma->vm_flags & VM_WRITE)
		direction = DMA_TO_DEVICE;
	else
		direction = DMA_FROM_DEVICE;

	ret = fw_iso_buffer_init(&client->buffer, client->device->card,
				 page_count, direction);
	ret = fw_iso_buffer_alloc(&client->buffer, page_count);
	if (ret < 0)
		return ret;

	ret = fw_iso_buffer_map(&client->buffer, vma);
	spin_lock_irq(&client->lock);
	if (client->iso_context) {
		ret = fw_iso_buffer_map_dma(&client->buffer,
				client->device->card,
				iso_dma_direction(client->iso_context));
		client->buffer_is_mapped = (ret == 0);
	}
	spin_unlock_irq(&client->lock);
	if (ret < 0)
		fw_iso_buffer_destroy(&client->buffer, client->device->card);
		goto fail;

	ret = fw_iso_buffer_map_vma(&client->buffer, vma);
	if (ret < 0)
		goto fail;

	return 0;
 fail:
	fw_iso_buffer_destroy(&client->buffer, client->device->card);
	return ret;
}

+57 −59
Original line number Diff line number Diff line
@@ -481,6 +481,7 @@ static int read_rom(struct fw_device *device,
 * generation changes under us, read_config_rom will fail and get retried.
 * It's better to start all over in this case because the node from which we
 * are reading the ROM may have changed the ROM during the reset.
 * Returns either a result code or a negative error code.
 */
static int read_config_rom(struct fw_device *device, int generation)
{
@@ -488,7 +489,7 @@ static int read_config_rom(struct fw_device *device, int generation)
	const u32 *old_rom, *new_rom;
	u32 *rom, *stack;
	u32 sp, key;
	int i, end, length, ret = -1;
	int i, end, length, ret;

	rom = kmalloc(sizeof(*rom) * MAX_CONFIG_ROM_SIZE +
		      sizeof(*stack) * MAX_CONFIG_ROM_SIZE, GFP_KERNEL);
@@ -502,19 +503,22 @@ static int read_config_rom(struct fw_device *device, int generation)

	/* First read the bus info block. */
	for (i = 0; i < 5; i++) {
		if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE)
		ret = read_rom(device, generation, i, &rom[i]);
		if (ret != RCODE_COMPLETE)
			goto out;
		/*
		 * As per IEEE1212 7.2, during power-up, devices can
		 * As per IEEE1212 7.2, during initialization, devices can
		 * reply with a 0 for the first quadlet of the config
		 * rom to indicate that they are booting (for example,
		 * if the firmware is on the disk of a external
		 * harddisk).  In that case we just fail, and the
		 * retry mechanism will try again later.
		 */
		if (i == 0 && rom[i] == 0)
		if (i == 0 && rom[i] == 0) {
			ret = RCODE_BUSY;
			goto out;
		}
	}

	device->max_speed = device->node->max_speed;

@@ -563,11 +567,14 @@ static int read_config_rom(struct fw_device *device, int generation)
		 */
		key = stack[--sp];
		i = key & 0xffffff;
		if (WARN_ON(i >= MAX_CONFIG_ROM_SIZE))
		if (WARN_ON(i >= MAX_CONFIG_ROM_SIZE)) {
			ret = -ENXIO;
			goto out;
		}

		/* Read header quadlet for the block to get the length. */
		if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE)
		ret = read_rom(device, generation, i, &rom[i]);
		if (ret != RCODE_COMPLETE)
			goto out;
		end = i + (rom[i] >> 16) + 1;
		if (end > MAX_CONFIG_ROM_SIZE) {
@@ -590,8 +597,8 @@ static int read_config_rom(struct fw_device *device, int generation)
		 * it references another block, and push it in that case.
		 */
		for (; i < end; i++) {
			if (read_rom(device, generation, i, &rom[i]) !=
			    RCODE_COMPLETE)
			ret = read_rom(device, generation, i, &rom[i]);
			if (ret != RCODE_COMPLETE)
				goto out;

			if ((key >> 30) != 3 || (rom[i] >> 30) < 2)
@@ -619,8 +626,10 @@ static int read_config_rom(struct fw_device *device, int generation)

	old_rom = device->config_rom;
	new_rom = kmemdup(rom, length * 4, GFP_KERNEL);
	if (new_rom == NULL)
	if (new_rom == NULL) {
		ret = -ENOMEM;
		goto out;
	}

	down_write(&fw_device_rwsem);
	device->config_rom = new_rom;
@@ -628,7 +637,7 @@ static int read_config_rom(struct fw_device *device, int generation)
	up_write(&fw_device_rwsem);

	kfree(old_rom);
	ret = 0;
	ret = RCODE_COMPLETE;
	device->max_rec	= rom[2] >> 12 & 0xf;
	device->cmc	= rom[2] >> 30 & 1;
	device->irmc	= rom[2] >> 31 & 1;
@@ -967,15 +976,17 @@ static void fw_device_init(struct work_struct *work)
	 * device.
	 */

	if (read_config_rom(device, device->generation) < 0) {
	ret = read_config_rom(device, device->generation);
	if (ret != RCODE_COMPLETE) {
		if (device->config_rom_retries < MAX_RETRIES &&
		    atomic_read(&device->state) == FW_DEVICE_INITIALIZING) {
			device->config_rom_retries++;
			fw_schedule_device_work(device, RETRY_DELAY);
		} else {
			if (device->node->link_on)
				fw_notice(card, "giving up on Config ROM for node id %x\n",
					  device->node_id);
				fw_notice(card, "giving up on node %x: reading config rom failed: %s\n",
					  device->node_id,
					  fw_rcode_string(ret));
			if (device->node == card->root_node)
				fw_schedule_bm_work(card, 0);
			fw_device_release(&device->device);
@@ -1069,31 +1080,30 @@ static void fw_device_init(struct work_struct *work)
	put_device(&device->device);	/* our reference */
}

enum {
	REREAD_BIB_ERROR,
	REREAD_BIB_GONE,
	REREAD_BIB_UNCHANGED,
	REREAD_BIB_CHANGED,
};

/* Reread and compare bus info block and header of root directory */
static int reread_config_rom(struct fw_device *device, int generation)
static int reread_config_rom(struct fw_device *device, int generation,
			     bool *changed)
{
	u32 q;
	int i;
	int i, rcode;

	for (i = 0; i < 6; i++) {
		if (read_rom(device, generation, i, &q) != RCODE_COMPLETE)
			return REREAD_BIB_ERROR;
		rcode = read_rom(device, generation, i, &q);
		if (rcode != RCODE_COMPLETE)
			return rcode;

		if (i == 0 && q == 0)
			return REREAD_BIB_GONE;
			/* inaccessible (see read_config_rom); retry later */
			return RCODE_BUSY;

		if (q != device->config_rom[i])
			return REREAD_BIB_CHANGED;
		if (q != device->config_rom[i]) {
			*changed = true;
			return RCODE_COMPLETE;
		}
	}

	return REREAD_BIB_UNCHANGED;
	*changed = false;
	return RCODE_COMPLETE;
}

static void fw_device_refresh(struct work_struct *work)
@@ -1101,23 +1111,14 @@ static void fw_device_refresh(struct work_struct *work)
	struct fw_device *device =
		container_of(work, struct fw_device, work.work);
	struct fw_card *card = device->card;
	int node_id = device->node_id;

	switch (reread_config_rom(device, device->generation)) {
	case REREAD_BIB_ERROR:
		if (device->config_rom_retries < MAX_RETRIES / 2 &&
		    atomic_read(&device->state) == FW_DEVICE_INITIALIZING) {
			device->config_rom_retries++;
			fw_schedule_device_work(device, RETRY_DELAY / 2);
	int ret, node_id = device->node_id;
	bool changed;

			return;
		}
		goto give_up;

	case REREAD_BIB_GONE:
		goto gone;
	ret = reread_config_rom(device, device->generation, &changed);
	if (ret != RCODE_COMPLETE)
		goto failed_config_rom;

	case REREAD_BIB_UNCHANGED:
	if (!changed) {
		if (atomic_cmpxchg(&device->state,
				   FW_DEVICE_INITIALIZING,
				   FW_DEVICE_RUNNING) == FW_DEVICE_GONE)
@@ -1126,9 +1127,6 @@ static void fw_device_refresh(struct work_struct *work)
		fw_device_update(work);
		device->config_rom_retries = 0;
		goto out;

	case REREAD_BIB_CHANGED:
		break;
	}

	/*
@@ -1137,16 +1135,9 @@ static void fw_device_refresh(struct work_struct *work)
	 */
	device_for_each_child(&device->device, NULL, shutdown_unit);

	if (read_config_rom(device, device->generation) < 0) {
		if (device->config_rom_retries < MAX_RETRIES &&
		    atomic_read(&device->state) == FW_DEVICE_INITIALIZING) {
			device->config_rom_retries++;
			fw_schedule_device_work(device, RETRY_DELAY);

			return;
		}
		goto give_up;
	}
	ret = read_config_rom(device, device->generation);
	if (ret != RCODE_COMPLETE)
		goto failed_config_rom;

	fw_device_cdev_update(device);
	create_units(device);
@@ -1163,9 +1154,16 @@ static void fw_device_refresh(struct work_struct *work)
	device->config_rom_retries = 0;
	goto out;

 give_up:
	fw_notice(card, "giving up on refresh of device %s\n",
		  dev_name(&device->device));
 failed_config_rom:
	if (device->config_rom_retries < MAX_RETRIES &&
	    atomic_read(&device->state) == FW_DEVICE_INITIALIZING) {
		device->config_rom_retries++;
		fw_schedule_device_work(device, RETRY_DELAY);
		return;
	}

	fw_notice(card, "giving up on refresh of device %s: %s\n",
		  dev_name(&device->device), fw_rcode_string(ret));
 gone:
	atomic_set(&device->state, FW_DEVICE_GONE);
	PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown);
+52 −28
Original line number Diff line number Diff line
@@ -39,52 +39,73 @@
 * Isochronous DMA context management
 */

int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
		       int page_count, enum dma_data_direction direction)
int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count)
{
	int i, j;
	dma_addr_t address;

	buffer->page_count = page_count;
	buffer->direction = direction;
	int i;

	buffer->page_count = 0;
	buffer->page_count_mapped = 0;
	buffer->pages = kmalloc(page_count * sizeof(buffer->pages[0]),
				GFP_KERNEL);
	if (buffer->pages == NULL)
		goto out;
		return -ENOMEM;

	for (i = 0; i < buffer->page_count; i++) {
	for (i = 0; i < page_count; i++) {
		buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
		if (buffer->pages[i] == NULL)
			goto out_pages;
			break;
	}
	buffer->page_count = i;
	if (i < page_count) {
		fw_iso_buffer_destroy(buffer, NULL);
		return -ENOMEM;
	}

	return 0;
}

int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card,
			  enum dma_data_direction direction)
{
	dma_addr_t address;
	int i;

	buffer->direction = direction;

	for (i = 0; i < buffer->page_count; i++) {
		address = dma_map_page(card->device, buffer->pages[i],
				       0, PAGE_SIZE, direction);
		if (dma_mapping_error(card->device, address)) {
			__free_page(buffer->pages[i]);
			goto out_pages;
		}
		if (dma_mapping_error(card->device, address))
			break;

		set_page_private(buffer->pages[i], address);
	}
	buffer->page_count_mapped = i;
	if (i < buffer->page_count)
		return -ENOMEM;

	return 0;

 out_pages:
	for (j = 0; j < i; j++) {
		address = page_private(buffer->pages[j]);
		dma_unmap_page(card->device, address,
			       PAGE_SIZE, direction);
		__free_page(buffer->pages[j]);
}
	kfree(buffer->pages);
 out:
	buffer->pages = NULL;

	return -ENOMEM;
int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
		       int page_count, enum dma_data_direction direction)
{
	int ret;

	ret = fw_iso_buffer_alloc(buffer, page_count);
	if (ret < 0)
		return ret;

	ret = fw_iso_buffer_map_dma(buffer, card, direction);
	if (ret < 0)
		fw_iso_buffer_destroy(buffer, card);

	return ret;
}
EXPORT_SYMBOL(fw_iso_buffer_init);

int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma)
int fw_iso_buffer_map_vma(struct fw_iso_buffer *buffer,
			  struct vm_area_struct *vma)
{
	unsigned long uaddr;
	int i, err;
@@ -107,15 +128,18 @@ void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer,
	int i;
	dma_addr_t address;

	for (i = 0; i < buffer->page_count; i++) {
	for (i = 0; i < buffer->page_count_mapped; i++) {
		address = page_private(buffer->pages[i]);
		dma_unmap_page(card->device, address,
			       PAGE_SIZE, buffer->direction);
		__free_page(buffer->pages[i]);
	}
	for (i = 0; i < buffer->page_count; i++)
		__free_page(buffer->pages[i]);

	kfree(buffer->pages);
	buffer->pages = NULL;
	buffer->page_count = 0;
	buffer->page_count_mapped = 0;
}
EXPORT_SYMBOL(fw_iso_buffer_destroy);

+26 −0
Original line number Diff line number Diff line
@@ -1003,6 +1003,32 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
}
EXPORT_SYMBOL(fw_core_handle_response);

/**
 * fw_rcode_string - convert a firewire result code to an error description
 * @rcode: the result code
 */
const char *fw_rcode_string(int rcode)
{
	static const char *const names[] = {
		[RCODE_COMPLETE]       = "no error",
		[RCODE_CONFLICT_ERROR] = "conflict error",
		[RCODE_DATA_ERROR]     = "data error",
		[RCODE_TYPE_ERROR]     = "type error",
		[RCODE_ADDRESS_ERROR]  = "address error",
		[RCODE_SEND_ERROR]     = "send error",
		[RCODE_CANCELLED]      = "timeout",
		[RCODE_BUSY]           = "busy",
		[RCODE_GENERATION]     = "bus reset",
		[RCODE_NO_ACK]         = "no ack",
	};

	if ((unsigned int)rcode < ARRAY_SIZE(names) && names[rcode])
		return names[rcode];
	else
		return "unknown";
}
EXPORT_SYMBOL(fw_rcode_string);

static const struct fw_address_region topology_map_region =
	{ .start = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP,
	  .end   = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP_END, };
Loading