diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-09-23 21:55:27 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-09-23 21:55:27 +0200 |
commit | d7dfb07d4dacfc3d8fa553fe76f055724e2c7210 (patch) | |
tree | 93748e01de4518e4af8f540bd97f628bc5f96c91 /drivers/firewire/core-device.c | |
parent | Merge tag 'pci-v6.12-changes' of git://git.kernel.org/pub/scm/linux/kernel/gi... (diff) | |
parent | firewire: core: rename cause flag of tracepoints event (diff) | |
download | linux-d7dfb07d4dacfc3d8fa553fe76f055724e2c7210.tar.xz linux-d7dfb07d4dacfc3d8fa553fe76f055724e2c7210.zip |
Merge tag 'firewire-updates-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394
Pull firewire updates from Takashi Sakamoto:
"In the FireWire subsystem, tasklets have been used as the bottom half
of 1394 OHCi hardIRQ. In recent kernel updates, BH workqueues have
become available, and some developers have proposed replacing the
tasklet with a BH workqueue.
As a first step towards dropping tasklet use, the 1394 OHCI
isochronous context can use regular workqueues. In this context, the
batch of packets is processed in the specific queue, thus the timing
jitter caused by task scheduling is not so critical.
Additionally, DMA transmission can be scheduled per-packet basis,
therefore the context can be sleep between the operation of
transmissions. Furthermore, in-kernel protocol implementation involves
some CPU-bound tasks, which can sometimes consumes CPU time so long.
These characteristics suggest that normal workqueues are suitable,
through BH workqueues are not.
The replacement with a workqueue allows unit drivers to process the
content of packets in non-atomic context. It brings some reliefs to
some drivers in sound subsystem that spin-lock is not mandatory
anymore during isochronous packet processing.
Summary:
- Replace tasklet with workqueue for isochronous context
- Replace IDR with XArray
- Utilize guard macro where possible
- Print deprecation warning when enabling debug parameter of
firewire-ohci module
- Switch to nonatomic PCM operation"
* tag 'firewire-updates-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394: (55 commits)
firewire: core: rename cause flag of tracepoints event
firewire: core: update documentation of kernel APIs for flushing completions
firewire: core: add helper function to retire descriptors
Revert "firewire: core: move workqueue handler from 1394 OHCI driver to core function"
Revert "firewire: core: use mutex to coordinate concurrent calls to flush completions"
firewire: core: use mutex to coordinate concurrent calls to flush completions
firewire: core: move workqueue handler from 1394 OHCI driver to core function
firewire: core: fulfill documentation of fw_iso_context_flush_completions()
firewire: core: expose kernel API to schedule work item to process isochronous context
firewire: core: use WARN_ON_ONCE() to avoid superfluous dumps
ALSA: firewire: use nonatomic PCM operation
firewire: core: non-atomic memory allocation for isochronous event to user client
firewire: ohci: operate IT/IR events in sleepable work process instead of tasklet softIRQ
firewire: core: add local API to queue work item to workqueue specific to isochronous contexts
firewire: core: allocate workqueue to handle isochronous contexts in card
firewire: ohci: obsolete direct usage of printk_ratelimit()
firewire: ohci: deprecate debug parameter
firewire: core: update fw_device outside of device_find_child()
firewire: ohci: fix error path to detect initiated reset in TI TSB41BA3D phy
firewire: core/ohci: minor refactoring for computation of configuration ROM size
...
Diffstat (limited to 'drivers/firewire/core-device.c')
-rw-r--r-- | drivers/firewire/core-device.c | 202 |
1 files changed, 92 insertions, 110 deletions
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 00e9a13e6c45..a99fe35f1f0d 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -12,7 +12,6 @@ #include <linux/errno.h> #include <linux/firewire.h> #include <linux/firewire-constants.h> -#include <linux/idr.h> #include <linux/jiffies.h> #include <linux/kobject.h> #include <linux/list.h> @@ -288,7 +287,7 @@ static ssize_t show_immediate(struct device *dev, const u32 *directories[] = {NULL, NULL}; int i, value = -1; - down_read(&fw_device_rwsem); + guard(rwsem_read)(&fw_device_rwsem); if (is_fw_unit(dev)) { directories[0] = fw_unit(dev)->directory; @@ -317,8 +316,6 @@ static ssize_t show_immediate(struct device *dev, } } - up_read(&fw_device_rwsem); - if (value < 0) return -ENOENT; @@ -339,7 +336,7 @@ static ssize_t show_text_leaf(struct device *dev, char dummy_buf[2]; int i, ret = -ENOENT; - down_read(&fw_device_rwsem); + guard(rwsem_read)(&fw_device_rwsem); if (is_fw_unit(dev)) { directories[0] = fw_unit(dev)->directory; @@ -382,15 +379,14 @@ static ssize_t show_text_leaf(struct device *dev, } } - if (ret >= 0) { - /* Strip trailing whitespace and add newline. */ - while (ret > 0 && isspace(buf[ret - 1])) - ret--; - strcpy(buf + ret, "\n"); - ret++; - } + if (ret < 0) + return ret; - up_read(&fw_device_rwsem); + // Strip trailing whitespace and add newline. + while (ret > 0 && isspace(buf[ret - 1])) + ret--; + strcpy(buf + ret, "\n"); + ret++; return ret; } @@ -466,10 +462,10 @@ static ssize_t config_rom_show(struct device *dev, struct fw_device *device = fw_device(dev); size_t length; - down_read(&fw_device_rwsem); + guard(rwsem_read)(&fw_device_rwsem); + length = device->config_rom_length * 4; memcpy(buf, device->config_rom, length); - up_read(&fw_device_rwsem); return length; } @@ -478,13 +474,10 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fw_device *device = fw_device(dev); - int ret; - down_read(&fw_device_rwsem); - ret = sysfs_emit(buf, "0x%08x%08x\n", device->config_rom[3], device->config_rom[4]); - up_read(&fw_device_rwsem); + guard(rwsem_read)(&fw_device_rwsem); - return ret; + return sysfs_emit(buf, "0x%08x%08x\n", device->config_rom[3], device->config_rom[4]); } static ssize_t is_local_show(struct device *dev, @@ -524,7 +517,8 @@ static ssize_t units_show(struct device *dev, struct fw_csr_iterator ci; int key, value, i = 0; - down_read(&fw_device_rwsem); + guard(rwsem_read)(&fw_device_rwsem); + fw_csr_iterator_init(&ci, &device->config_rom[ROOT_DIR_OFFSET]); while (fw_csr_iterator_next(&ci, &key, &value)) { if (key != (CSR_UNIT | CSR_DIRECTORY)) @@ -533,7 +527,6 @@ static ssize_t units_show(struct device *dev, if (i >= PAGE_SIZE - (8 + 1 + 8 + 1)) break; } - up_read(&fw_device_rwsem); if (i) buf[i - 1] = '\n'; @@ -571,7 +564,8 @@ static int read_rom(struct fw_device *device, return rcode; } -#define MAX_CONFIG_ROM_SIZE 256 +// By quadlet unit. +#define MAX_CONFIG_ROM_SIZE ((CSR_CONFIG_ROM_END - CSR_CONFIG_ROM) / sizeof(u32)) /* * Read the bus info block, perform a speed probe, and read all of the rest of @@ -729,10 +723,10 @@ static int read_config_rom(struct fw_device *device, int generation) goto out; } - down_write(&fw_device_rwsem); - device->config_rom = new_rom; - device->config_rom_length = length; - up_write(&fw_device_rwsem); + scoped_guard(rwsem_write, &fw_device_rwsem) { + device->config_rom = new_rom; + device->config_rom_length = length; + } kfree(old_rom); ret = RCODE_COMPLETE; @@ -813,24 +807,21 @@ static int shutdown_unit(struct device *device, void *data) /* * fw_device_rwsem acts as dual purpose mutex: - * - serializes accesses to fw_device_idr, * - serializes accesses to fw_device.config_rom/.config_rom_length and * fw_unit.directory, unless those accesses happen at safe occasions */ DECLARE_RWSEM(fw_device_rwsem); -DEFINE_IDR(fw_device_idr); +DEFINE_XARRAY_ALLOC(fw_device_xa); int fw_cdev_major; struct fw_device *fw_device_get_by_devt(dev_t devt) { struct fw_device *device; - down_read(&fw_device_rwsem); - device = idr_find(&fw_device_idr, MINOR(devt)); + device = xa_load(&fw_device_xa, MINOR(devt)); if (device) fw_device_get(device); - up_read(&fw_device_rwsem); return device; } @@ -864,7 +855,6 @@ static void fw_device_shutdown(struct work_struct *work) { struct fw_device *device = container_of(work, struct fw_device, work.work); - int minor = MINOR(device->device.devt); if (time_before64(get_jiffies_64(), device->card->reset_jiffies + SHUTDOWN_DELAY) @@ -882,9 +872,7 @@ static void fw_device_shutdown(struct work_struct *work) device_for_each_child(&device->device, NULL, shutdown_unit); device_unregister(&device->device); - down_write(&fw_device_rwsem); - idr_remove(&fw_device_idr, minor); - up_write(&fw_device_rwsem); + xa_erase(&fw_device_xa, MINOR(device->device.devt)); fw_device_put(device); } @@ -893,16 +881,14 @@ static void fw_device_release(struct device *dev) { struct fw_device *device = fw_device(dev); struct fw_card *card = device->card; - unsigned long flags; /* * Take the card lock so we don't set this to NULL while a * FW_NODE_UPDATED callback is being handled or while the * bus manager work looks at this node. */ - spin_lock_irqsave(&card->lock, flags); - device->node->data = NULL; - spin_unlock_irqrestore(&card->lock, flags); + scoped_guard(spinlock_irqsave, &card->lock) + device->node->data = NULL; fw_node_put(device->node); kfree(device->config_rom); @@ -942,59 +928,6 @@ static void fw_device_update(struct work_struct *work) device_for_each_child(&device->device, NULL, update_unit); } -/* - * If a device was pending for deletion because its node went away but its - * bus info block and root directory header matches that of a newly discovered - * device, revive the existing fw_device. - * The newly allocated fw_device becomes obsolete instead. - */ -static int lookup_existing_device(struct device *dev, void *data) -{ - struct fw_device *old = fw_device(dev); - struct fw_device *new = data; - struct fw_card *card = new->card; - int match = 0; - - if (!is_fw_device(dev)) - return 0; - - down_read(&fw_device_rwsem); /* serialize config_rom access */ - spin_lock_irq(&card->lock); /* serialize node access */ - - if (memcmp(old->config_rom, new->config_rom, 6 * 4) == 0 && - atomic_cmpxchg(&old->state, - FW_DEVICE_GONE, - FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { - struct fw_node *current_node = new->node; - struct fw_node *obsolete_node = old->node; - - new->node = obsolete_node; - new->node->data = new; - old->node = current_node; - old->node->data = old; - - old->max_speed = new->max_speed; - old->node_id = current_node->node_id; - smp_wmb(); /* update node_id before generation */ - old->generation = card->generation; - old->config_rom_retries = 0; - fw_notice(card, "rediscovered device %s\n", dev_name(dev)); - - old->workfn = fw_device_update; - fw_schedule_device_work(old, 0); - - if (current_node == card->root_node) - fw_schedule_bm_work(card, 0); - - match = 1; - } - - spin_unlock_irq(&card->lock); - up_read(&fw_device_rwsem); - - return match; -} - enum { BC_UNKNOWN = 0, BC_UNIMPLEMENTED, BC_IMPLEMENTED, }; static void set_broadcast_channel(struct fw_device *device, int generation) @@ -1055,13 +988,26 @@ int fw_device_set_broadcast_channel(struct device *dev, void *gen) return 0; } +static int compare_configuration_rom(struct device *dev, void *data) +{ + const struct fw_device *old = fw_device(dev); + const u32 *config_rom = data; + + if (!is_fw_device(dev)) + return 0; + + // Compare the bus information block and root_length/root_crc. + return !memcmp(old->config_rom, config_rom, 6 * 4); +} + static void fw_device_init(struct work_struct *work) { struct fw_device *device = container_of(work, struct fw_device, work.work); struct fw_card *card = device->card; - struct device *revived_dev; - int minor, ret; + struct device *found; + u32 minor; + int ret; /* * All failure paths here set node->data to NULL, so that we @@ -1087,24 +1033,62 @@ static void fw_device_init(struct work_struct *work) return; } - revived_dev = device_find_child(card->device, - device, lookup_existing_device); - if (revived_dev) { - put_device(revived_dev); - fw_device_release(&device->device); + // If a device was pending for deletion because its node went away but its bus info block + // and root directory header matches that of a newly discovered device, revive the + // existing fw_device. The newly allocated fw_device becomes obsolete instead. + // + // serialize config_rom access. + scoped_guard(rwsem_read, &fw_device_rwsem) { + found = device_find_child(card->device, (void *)device->config_rom, + compare_configuration_rom); + } + if (found) { + struct fw_device *reused = fw_device(found); + + if (atomic_cmpxchg(&reused->state, + FW_DEVICE_GONE, + FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { + // serialize node access + scoped_guard(spinlock_irq, &card->lock) { + struct fw_node *current_node = device->node; + struct fw_node *obsolete_node = reused->node; + + device->node = obsolete_node; + device->node->data = device; + reused->node = current_node; + reused->node->data = reused; + + reused->max_speed = device->max_speed; + reused->node_id = current_node->node_id; + smp_wmb(); /* update node_id before generation */ + reused->generation = card->generation; + reused->config_rom_retries = 0; + fw_notice(card, "rediscovered device %s\n", + dev_name(found)); + + reused->workfn = fw_device_update; + fw_schedule_device_work(reused, 0); + + if (current_node == card->root_node) + fw_schedule_bm_work(card, 0); + } - return; + put_device(found); + fw_device_release(&device->device); + + return; + } + + put_device(found); } device_initialize(&device->device); fw_device_get(device); - down_write(&fw_device_rwsem); - minor = idr_alloc(&fw_device_idr, device, 0, 1 << MINORBITS, - GFP_KERNEL); - up_write(&fw_device_rwsem); - if (minor < 0) + // The index of allocated entry is used for minor identifier of device node. + ret = xa_alloc(&fw_device_xa, &minor, device, XA_LIMIT(0, MINORMASK), GFP_KERNEL); + if (ret < 0) goto error; device->device.bus = &fw_bus_type; @@ -1165,11 +1149,9 @@ static void fw_device_init(struct work_struct *work) return; error_with_cdev: - down_write(&fw_device_rwsem); - idr_remove(&fw_device_idr, minor); - up_write(&fw_device_rwsem); + xa_erase(&fw_device_xa, minor); error: - fw_device_put(device); /* fw_device_idr's reference */ + fw_device_put(device); // fw_device_xa's reference. put_device(&device->device); /* our reference */ } |