summaryrefslogtreecommitdiff
path: root/drivers/firewire/core-cdev.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-11 11:32:06 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-11 11:32:06 -0800
commit0d6dd4738dbcc32b60c0c0c1388d41e171b76845 (patch)
treea7b906b75970113aa33d2346e3dc0bf382f6b6b2 /drivers/firewire/core-cdev.c
parenta31980dba7b957df21fd99d158dd0be516825676 (diff)
parent6b617317e5bc95e9962a712314ae0c4b7a4d5cc3 (diff)
Merge tag 'firewire-updates-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394
Pull firewire updates from Takashi Sakamoto: - Refactor page allocation dedicated to 1394 OHCI IR/IT/AR DMA contexts Although 1394 OHCI specification does not impose any restriction on the memory size dedicated to these DMA contexts, 1394 OHCI PCI driver allocates pages for convenience when mapping them into either kernel space or userspace VMA. The driver previously used dma_alloc_pages() for both page allocation and mapping creation, even though this kernel API is rarely used. Following discussions questioning the page-oriented kernel API in the DMA layer, the driver has been refactored to avoid using this API. In addition, the use of private members in the allocated pages has been removed following long-standing concern. - Allocate variable-sized buffer for isochronous context header 1394 OHCI PCI driver previously allocated a single page for isochronous context header. As a result, the buffer size for the header was fixed to PAGE_SIZE, which imposed a limitation on IEC 61883-1/6 packet streaming engine. Consequently, the ALSA PCM devices provided by drivers for audio and music units in IEEE 1394 bus were constrained in the maximum size of buffer period (64 ms in most cases). This limitation is resolved by dynamically allocating the header buffer with an arbitrary size. * tag 'firewire-updates-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394: ALSA: firewire: remove PCM buffer size constraint from isoc context header firewire: core: add fw_iso_context_create() variant with header storage size firewire: core: provide isoc header buffer size outside card driver firewire: ohci: allocate isoc context header by kvmalloc() firewire: core: add flags member for isochronous context structure firewire: ohci: use cleanup helper for isoc context header allocation firewire: ohci: code refactoring to use union for isoc multiple channel state firewire: ohci: refactor isoc single-channel state using a union firewire: core: add function variants for isochronous context creation firewire: ohci: fix index of pages for dma address to 1394 OHCI IT context firewire: ohci: stop using page private to store DMA mapping address firewire: ohci: split page allocation from dma mapping firewire: ohci: use MAX macro to guarantee minimum count of pages for AR contexts firewire: core: stop using page private to store DMA mapping address firewire: core: use common kernel API to allocate and release a batch of pages firewire: core: code refactoring with cleanup function for isoc pages firewire: core: use mutex instead of spinlock for client isochronous context firewire: core: move private function declaration from public header to internal header
Diffstat (limited to 'drivers/firewire/core-cdev.c')
-rw-r--r--drivers/firewire/core-cdev.c71
1 files changed, 26 insertions, 45 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 49dc1612c691..9e964fdd175c 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -63,10 +63,10 @@ struct client {
u64 bus_reset_closure;
struct fw_iso_context *iso_context;
+ struct mutex iso_context_mutex;
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;
@@ -306,6 +306,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
INIT_LIST_HEAD(&client->phy_receiver_link);
INIT_LIST_HEAD(&client->link);
kref_init(&client->kref);
+ mutex_init(&client->iso_context_mutex);
file->private_data = client;
@@ -1025,25 +1026,10 @@ static enum dma_data_direction iso_dma_direction(struct fw_iso_context *context)
return DMA_FROM_DEVICE;
}
-static struct fw_iso_context *fw_iso_mc_context_create(struct fw_card *card,
- fw_iso_mc_callback_t callback,
- void *callback_data)
-{
- struct fw_iso_context *ctx;
-
- ctx = fw_iso_context_create(card, FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL,
- 0, 0, 0, NULL, callback_data);
- if (!IS_ERR(ctx))
- ctx->callback.mc = callback;
-
- return ctx;
-}
-
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;
- union fw_iso_callback cb;
int ret;
BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT ||
@@ -1055,20 +1041,15 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
case FW_ISO_CONTEXT_TRANSMIT:
if (a->speed > SCODE_3200 || a->channel > 63)
return -EINVAL;
-
- cb.sc = iso_callback;
break;
case FW_ISO_CONTEXT_RECEIVE:
if (a->header_size < 4 || (a->header_size & 3) ||
a->channel > 63)
return -EINVAL;
-
- cb.sc = iso_callback;
break;
case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
- cb.mc = iso_mc_callback;
break;
default:
@@ -1076,38 +1057,36 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
}
if (a->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL)
- context = fw_iso_mc_context_create(client->device->card, cb.mc,
- client);
+ context = fw_iso_mc_context_create(client->device->card, iso_mc_callback, client);
else
- context = fw_iso_context_create(client->device->card, a->type,
- a->channel, a->speed,
- a->header_size, cb.sc, client);
+ context = fw_iso_context_create(client->device->card, a->type, a->channel, a->speed,
+ a->header_size, iso_callback, client);
if (IS_ERR(context))
return PTR_ERR(context);
if (client->version < FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW)
- context->drop_overflow_headers = true;
+ context->flags |= FW_ISO_CONTEXT_FLAG_DROP_OVERFLOW_HEADERS;
// We only support one context at this time.
- guard(spinlock_irq)(&client->lock);
-
- if (client->iso_context != NULL) {
- 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) {
+ scoped_guard(mutex, &client->iso_context_mutex) {
+ if (client->iso_context != NULL) {
fw_iso_context_destroy(context);
- return ret;
+ return -EBUSY;
+ }
+ // The DMA mapping operation is available if the buffer is already allocated by
+ // mmap(2) system call. If not, it is delegated to the system call.
+ if (client->buffer.pages && !client->buffer.dma_addrs) {
+ ret = fw_iso_buffer_map_dma(&client->buffer, client->device->card,
+ iso_dma_direction(context));
+ if (ret < 0) {
+ fw_iso_context_destroy(context);
+
+ return ret;
+ }
}
- client->buffer_is_mapped = true;
+ client->iso_closure = a->closure;
+ client->iso_context = context;
}
- client->iso_closure = a->closure;
- client->iso_context = context;
a->handle = 0;
@@ -1826,13 +1805,14 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
if (ret < 0)
return ret;
- scoped_guard(spinlock_irq, &client->lock) {
+ scoped_guard(mutex, &client->iso_context_mutex) {
+ // The direction of DMA can be determined if the isochronous context is already
+ // allocated. If not, the DMA mapping operation is postponed after the allocation.
if (client->iso_context) {
ret = fw_iso_buffer_map_dma(&client->buffer, client->device->card,
iso_dma_direction(client->iso_context));
if (ret < 0)
goto fail;
- client->buffer_is_mapped = true;
}
}
@@ -1879,6 +1859,7 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
if (client->iso_context)
fw_iso_context_destroy(client->iso_context);
+ mutex_destroy(&client->iso_context_mutex);
if (client->buffer.pages)
fw_iso_buffer_destroy(&client->buffer, client->device->card);