Re: [BISECTED]: Black screen on 7.0-rc1

From: Linus Torvalds

Date: Mon Feb 23 2026 - 19:25:54 EST


On Mon, 23 Feb 2026 at 13:29, Harald Arnesen <linux@xxxxxxxxxxx> wrote:
>
> >
> > ba268514ea14 ("rust: devres: fix race condition due to nesting")
>
> Boots fine.
>
> > And please double-check the first parent too (ie commit 1c2b4a4c2bcb:
>
> Also boots fine.

Ok. I do think it's likely timing-related, and Danilo seems to have a
theory on *why* the driver core merge might be changing timing
reliably.

So my guess would be that it hits the merge because something that got
merged earlier had issues, and the driver core merge then exposes
those issues due to changed timing or ordering.

That's purely a guess.

> > In fact, I would expect it to be related to one (or both) of
> >
> > f2ae92780ab9 ("firewire: ohci: split page allocation from dma mapping")
> > 993ab48006b3 ("firewire: ohci: stop using page private to store
> > DMA mapping address")
>
> Should I try reverting one or both of these?

Yes. From a quick check, they revert cleanly (revert 993ab48006b3
first) and the result seems to compile cleanly too.

So that would be a quick check to see if that oops you see is due to
those two changes.

Now, the reason I picked on those two commits is that they are the
ones that touch ar_context_init() directly, and that's where that oops
was, and so they seemed the most obvious very targeted thing to try.

But obviously the oops could be due to changes in the callers - the
same way the driver-core upcates seem to be needed to trigger this.

So it might be worth testing reverting all the firewire changes, just
to not pick two random commits out of there. That gets a trivial
conflict with the alloc helpers, so here's a patch to try.

And due to all the noise with the "get_random_u32 called with
crng_init=0" noise, it's obviously also *very* possible that none of
this has anything at all to do with the firewire changes at all, and
there were earlier oops reports that just were flushed out.

So it may be completely unfair to pick on any of the firewire changes,
but it's the only real clue I have right now judging by your kernel
messages in the failure case.

Linus
drivers/firewire/core-card.c | 4 +-
drivers/firewire/core-cdev.c | 71 ++++++++-----
drivers/firewire/core-iso.c | 103 +++++++++----------
drivers/firewire/core.h | 14 +--
drivers/firewire/ohci.c | 234 ++++++++++++++++++------------------------
include/linux/firewire.h | 36 ++-----
sound/firewire/amdtp-stream.c | 31 ++++--
7 files changed, 226 insertions(+), 267 deletions(-)

diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index a754c6366b97..0462d7b9e547 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -704,8 +704,8 @@ static int dummy_enable_phys_dma(struct fw_card *card,
return -ENODEV;
}

-static struct fw_iso_context *dummy_allocate_iso_context(struct fw_card *card, int type,
- int channel, size_t header_size, size_t header_storage_size)
+static struct fw_iso_context *dummy_allocate_iso_context(struct fw_card *card,
+ int type, int channel, size_t header_size)
{
return ERR_PTR(-ENODEV);
}
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index f791db4c8dff..f7232ff6a130 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,7 +306,6 @@ 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;

@@ -1026,10 +1025,25 @@ 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 ||
@@ -1041,15 +1055,20 @@ 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:
@@ -1057,36 +1076,38 @@ 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, iso_mc_callback, client);
+ context = fw_iso_mc_context_create(client->device->card, cb.mc,
+ client);
else
- context = fw_iso_context_create(client->device->card, a->type, a->channel, a->speed,
- a->header_size, iso_callback, client);
+ context = fw_iso_context_create(client->device->card, a->type,
+ a->channel, a->speed,
+ a->header_size, cb.sc, client);
if (IS_ERR(context))
return PTR_ERR(context);
if (client->version < FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW)
- context->flags |= FW_ISO_CONTEXT_FLAG_DROP_OVERFLOW_HEADERS;
+ context->drop_overflow_headers = true;

// We only support one context at this time.
- scoped_guard(mutex, &client->iso_context_mutex) {
- if (client->iso_context != NULL) {
+ 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) {
fw_iso_context_destroy(context);

- return -EBUSY;
+ return ret;
}
- // 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->iso_closure = a->closure;
- client->iso_context = context;
+ client->buffer_is_mapped = true;
}
+ client->iso_closure = a->closure;
+ client->iso_context = context;

a->handle = 0;

@@ -1805,14 +1826,13 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
if (ret < 0)
return ret;

- 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.
+ scoped_guard(spinlock_irq, &client->lock) {
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;
}
}

@@ -1859,7 +1879,6 @@ 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);
diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c
index d2b40a1a565e..a67493862c85 100644
--- a/drivers/firewire/core-iso.c
+++ b/drivers/firewire/core-iso.c
@@ -30,58 +30,48 @@

int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count)
{
- struct page **page_array __free(kfree) = kzalloc_objs(page_array[0],
- page_count);
+ int i;

- if (!page_array)
+ buffer->page_count = 0;
+ buffer->page_count_mapped = 0;
+ buffer->pages = kmalloc_array(page_count, sizeof(buffer->pages[0]),
+ GFP_KERNEL);
+ if (buffer->pages == NULL)
return -ENOMEM;

- // Retrieve noncontiguous pages. The descriptors for 1394 OHCI isochronous DMA contexts
- // have a set of address and length per each, while the reason to use pages is the
- // convenience to map them into virtual address space of user process.
- unsigned long nr_populated = alloc_pages_bulk(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO,
- page_count, page_array);
- if (nr_populated != page_count) {
- // Assuming the above call fills page_array sequentially from the beginning.
- release_pages(page_array, nr_populated);
+ for (i = 0; i < page_count; i++) {
+ buffer->pages[i] = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
+ if (buffer->pages[i] == NULL)
+ break;
+ }
+ buffer->page_count = i;
+ if (i < page_count) {
+ fw_iso_buffer_destroy(buffer, NULL);
return -ENOMEM;
}

- buffer->page_count = page_count;
- buffer->pages = no_free_ptr(page_array);
-
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 *dma_addrs __free(kfree) = kzalloc_objs(dma_addrs[0],
- buffer->page_count);
+ dma_addr_t address;
int i;

- if (!dma_addrs)
- return -ENOMEM;
+ buffer->direction = direction;

- // Retrieve DMA mapping addresses for the pages. They are not contiguous. Maintain the cache
- // coherency for the pages by hand.
for (i = 0; i < buffer->page_count; i++) {
- // The dma_map_phys() with a physical address per page is available here, instead.
- dma_addr_t dma_addr = dma_map_page(card->device, buffer->pages[i], 0, PAGE_SIZE,
- direction);
- if (dma_mapping_error(card->device, dma_addr))
+ address = dma_map_page(card->device, buffer->pages[i],
+ 0, PAGE_SIZE, direction);
+ if (dma_mapping_error(card->device, address))
break;

- dma_addrs[i] = dma_addr;
+ set_page_private(buffer->pages[i], address);
}
- if (i < buffer->page_count) {
- while (i-- > 0)
- dma_unmap_page(card->device, dma_addrs[i], PAGE_SIZE, buffer->direction);
+ buffer->page_count_mapped = i;
+ if (i < buffer->page_count)
return -ENOMEM;
- }
-
- buffer->direction = direction;
- buffer->dma_addrs = no_free_ptr(dma_addrs);

return 0;
}
@@ -106,31 +96,34 @@ EXPORT_SYMBOL(fw_iso_buffer_init);
void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer,
struct fw_card *card)
{
- if (buffer->dma_addrs) {
- for (int i = 0; i < buffer->page_count; ++i) {
- dma_addr_t dma_addr = buffer->dma_addrs[i];
- dma_unmap_page(card->device, dma_addr, PAGE_SIZE, buffer->direction);
- }
- kfree(buffer->dma_addrs);
- buffer->dma_addrs = NULL;
- }
+ int i;
+ dma_addr_t address;

- if (buffer->pages) {
- release_pages(buffer->pages, buffer->page_count);
- kfree(buffer->pages);
- buffer->pages = NULL;
+ 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);
}
+ 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);

/* Convert DMA address to offset into virtually contiguous buffer. */
size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed)
{
- for (int i = 0; i < buffer->page_count; i++) {
- dma_addr_t dma_addr = buffer->dma_addrs[i];
- ssize_t offset = (ssize_t)completed - (ssize_t)dma_addr;
+ size_t i;
+ dma_addr_t address;
+ ssize_t offset;
+
+ for (i = 0; i < buffer->page_count; i++) {
+ address = page_private(buffer->pages[i]);
+ offset = (ssize_t)completed - (ssize_t)address;
if (offset > 0 && offset <= PAGE_SIZE)
return (i << PAGE_SHIFT) + offset;
}
@@ -138,14 +131,14 @@ size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed)
return 0;
}

-struct fw_iso_context *__fw_iso_context_create(struct fw_card *card, int type, int channel,
- int speed, size_t header_size, size_t header_storage_size,
- union fw_iso_callback callback, void *callback_data)
+struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
+ int type, int channel, int speed, size_t header_size,
+ fw_iso_callback_t callback, void *callback_data)
{
struct fw_iso_context *ctx;

- ctx = card->driver->allocate_iso_context(card, type, channel, header_size,
- header_storage_size);
+ ctx = card->driver->allocate_iso_context(card,
+ type, channel, header_size);
if (IS_ERR(ctx))
return ctx;

@@ -153,10 +146,8 @@ struct fw_iso_context *__fw_iso_context_create(struct fw_card *card, int type, i
ctx->type = type;
ctx->channel = channel;
ctx->speed = speed;
- ctx->flags = 0;
ctx->header_size = header_size;
- ctx->header_storage_size = header_storage_size;
- ctx->callback = callback;
+ ctx->callback.sc = callback;
ctx->callback_data = callback_data;

trace_isoc_outbound_allocate(ctx, channel, speed);
@@ -165,7 +156,7 @@ struct fw_iso_context *__fw_iso_context_create(struct fw_card *card, int type, i

return ctx;
}
-EXPORT_SYMBOL(__fw_iso_context_create);
+EXPORT_SYMBOL(fw_iso_context_create);

void fw_iso_context_destroy(struct fw_iso_context *ctx)
{
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index 8b49d7480c37..41fb39d9a4e6 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -100,8 +100,8 @@ struct fw_card_driver {
void (*write_csr)(struct fw_card *card, int csr_offset, u32 value);

struct fw_iso_context *
- (*allocate_iso_context)(struct fw_card *card, int type, int channel, size_t header_size,
- size_t header_storage_size);
+ (*allocate_iso_context)(struct fw_card *card,
+ int type, int channel, size_t header_size);
void (*free_iso_context)(struct fw_iso_context *ctx);

int (*start_iso)(struct fw_iso_context *ctx,
@@ -166,22 +166,12 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event);
int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count);
int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card,
enum dma_data_direction direction);
-size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed);

static inline void fw_iso_context_init_work(struct fw_iso_context *ctx, work_func_t func)
{
INIT_WORK(&ctx->work, func);
}

-static inline struct fw_iso_context *fw_iso_mc_context_create(struct fw_card *card,
- fw_iso_mc_callback_t callback, void *callback_data)
-{
- union fw_iso_callback cb = { .mc = callback };
-
- return __fw_iso_context_create(card, FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL, 0, 0, 0, 0, cb,
- callback_data);
-}
-

/* -topology */

diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 1c868c1e4a49..e3e78dc42530 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -86,7 +86,7 @@ struct descriptor {
#define AR_BUFFER_SIZE (32*1024)
#define AR_BUFFERS_MIN DIV_ROUND_UP(AR_BUFFER_SIZE, PAGE_SIZE)
/* we need at least two pages for proper list management */
-#define AR_BUFFERS MAX(2, AR_BUFFERS_MIN)
+#define AR_BUFFERS (AR_BUFFERS_MIN >= 2 ? AR_BUFFERS_MIN : 2)

#define MAX_ASYNC_PAYLOAD 4096
#define MAX_AR_PACKET_SIZE (16 + MAX_ASYNC_PAYLOAD + 4)
@@ -96,7 +96,6 @@ struct ar_context {
struct fw_ohci *ohci;
struct page *pages[AR_BUFFERS];
void *buffer;
- dma_addr_t dma_addrs[AR_BUFFERS];
struct descriptor *descriptors;
dma_addr_t descriptors_bus;
void *pointer;
@@ -168,20 +167,14 @@ struct at_context {
struct iso_context {
struct fw_iso_context base;
struct context context;
+ void *header;
+ size_t header_length;
unsigned long flushing_completions;
+ u32 mc_buffer_bus;
+ u16 mc_completed;
+ u16 last_timestamp;
u8 sync;
u8 tags;
- union {
- struct {
- u16 last_timestamp;
- size_t header_length;
- void *header;
- } sc;
- struct {
- u32 buffer_bus;
- u16 completed;
- } mc;
- };
};

#define CONFIG_ROM_SIZE (CSR_CONFIG_ROM_END - CSR_CONFIG_ROM)
@@ -520,6 +513,11 @@ static int ohci_update_phy_reg(struct fw_card *card, int addr,
return update_phy_reg(ohci, addr, clear_bits, set_bits);
}

+static inline dma_addr_t ar_buffer_bus(struct ar_context *ctx, unsigned int i)
+{
+ return page_private(ctx->pages[i]);
+}
+
static void ar_context_link_page(struct ar_context *ctx, unsigned int index)
{
struct descriptor *d;
@@ -541,22 +539,18 @@ static void ar_context_link_page(struct ar_context *ctx, unsigned int index)
static void ar_context_release(struct ar_context *ctx)
{
struct device *dev = ctx->ohci->card.device;
+ unsigned int i;

if (!ctx->buffer)
return;

- for (int i = 0; i < AR_BUFFERS; ++i) {
- dma_addr_t dma_addr = ctx->dma_addrs[i];
- if (dma_addr)
- dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE);
- }
- memset(ctx->dma_addrs, 0, sizeof(ctx->dma_addrs));
-
vunmap(ctx->buffer);
- ctx->buffer = NULL;

- release_pages(ctx->pages, AR_BUFFERS);
- memset(ctx->pages, 0, sizeof(ctx->pages));
+ for (i = 0; i < AR_BUFFERS; i++) {
+ if (ctx->pages[i])
+ dma_free_pages(dev, PAGE_SIZE, ctx->pages[i],
+ ar_buffer_bus(ctx, i), DMA_FROM_DEVICE);
+ }
}

static void ar_context_abort(struct ar_context *ctx, const char *error_msg)
@@ -649,12 +643,14 @@ static void ar_sync_buffers_for_cpu(struct ar_context *ctx,

i = ar_first_buffer_index(ctx);
while (i != end_buffer_index) {
- dma_sync_single_for_cpu(ctx->ohci->card.device, ctx->dma_addrs[i], PAGE_SIZE,
- DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu(ctx->ohci->card.device,
+ ar_buffer_bus(ctx, i),
+ PAGE_SIZE, DMA_FROM_DEVICE);
i = ar_next_buffer_index(i);
}
if (end_buffer_offset > 0)
- dma_sync_single_for_cpu(ctx->ohci->card.device, ctx->dma_addrs[i],
+ dma_sync_single_for_cpu(ctx->ohci->card.device,
+ ar_buffer_bus(ctx, i),
end_buffer_offset, DMA_FROM_DEVICE);
}

@@ -795,8 +791,9 @@ static void ar_recycle_buffers(struct ar_context *ctx, unsigned int end_buffer)

i = ar_first_buffer_index(ctx);
while (i != end_buffer) {
- dma_sync_single_for_device(ctx->ohci->card.device, ctx->dma_addrs[i], PAGE_SIZE,
- DMA_FROM_DEVICE);
+ dma_sync_single_for_device(ctx->ohci->card.device,
+ ar_buffer_bus(ctx, i),
+ PAGE_SIZE, DMA_FROM_DEVICE);
ar_context_link_page(ctx, i);
i = ar_next_buffer_index(i);
}
@@ -848,57 +845,31 @@ static int ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci,
{
struct device *dev = ohci->card.device;
unsigned int i;
+ dma_addr_t dma_addr;
struct page *pages[AR_BUFFERS + AR_WRAPAROUND_PAGES];
- dma_addr_t dma_addrs[AR_BUFFERS];
- void *vaddr;
struct descriptor *d;

ctx->regs = regs;
ctx->ohci = ohci;
INIT_WORK(&ctx->work, ohci_ar_context_work);

- // Retrieve noncontiguous pages. The descriptors for 1394 OHCI AR DMA contexts have a set
- // of address and length per each. The reason to use pages is to construct contiguous
- // address range in kernel virtual address space.
- unsigned long nr_populated = alloc_pages_bulk(GFP_KERNEL | GFP_DMA32, AR_BUFFERS, pages);
-
- if (nr_populated != AR_BUFFERS) {
- release_pages(pages, nr_populated);
- return -ENOMEM;
- }
-
- // Map the pages into contiguous kernel virtual addresses so that the packet data
- // across the pages can be referred as being contiguous, especially across the last
- // and first pages.
- for (i = 0; i < AR_WRAPAROUND_PAGES; i++)
- pages[AR_BUFFERS + i] = pages[i];
- vaddr = vmap(pages, ARRAY_SIZE(pages), VM_MAP, PAGE_KERNEL);
- if (!vaddr) {
- release_pages(pages, nr_populated);
- return -ENOMEM;
- }
-
- // Retrieve DMA mapping addresses for the pages. They are not contiguous. Maintain the cache
- // coherency for the pages by hand.
for (i = 0; i < AR_BUFFERS; i++) {
- // The dma_map_phys() with a physical address per page is available here, instead.
- dma_addr_t dma_addr = dma_map_page(dev, pages[i], 0, PAGE_SIZE, DMA_FROM_DEVICE);
- if (dma_mapping_error(dev, dma_addr))
- break;
- dma_addrs[i] = dma_addr;
- dma_sync_single_for_device(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE);
- }
- if (i < AR_BUFFERS) {
- while (i-- > 0)
- dma_unmap_page(dev, dma_addrs[i], PAGE_SIZE, DMA_FROM_DEVICE);
- vunmap(vaddr);
- release_pages(pages, nr_populated);
- return -ENOMEM;
+ ctx->pages[i] = dma_alloc_pages(dev, PAGE_SIZE, &dma_addr,
+ DMA_FROM_DEVICE, GFP_KERNEL);
+ if (!ctx->pages[i])
+ goto out_of_memory;
+ set_page_private(ctx->pages[i], dma_addr);
+ dma_sync_single_for_device(dev, dma_addr, PAGE_SIZE,
+ DMA_FROM_DEVICE);
}

- memcpy(ctx->dma_addrs, dma_addrs, sizeof(ctx->dma_addrs));
- ctx->buffer = vaddr;
- memcpy(ctx->pages, pages, sizeof(ctx->pages));
+ for (i = 0; i < AR_BUFFERS; i++)
+ pages[i] = ctx->pages[i];
+ for (i = 0; i < AR_WRAPAROUND_PAGES; i++)
+ pages[AR_BUFFERS + i] = ctx->pages[i];
+ ctx->buffer = vmap(pages, ARRAY_SIZE(pages), VM_MAP, PAGE_KERNEL);
+ if (!ctx->buffer)
+ goto out_of_memory;

ctx->descriptors = ohci->misc_buffer + descriptors_offset;
ctx->descriptors_bus = ohci->misc_buffer_bus + descriptors_offset;
@@ -909,12 +880,17 @@ static int ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci,
d->control = cpu_to_le16(DESCRIPTOR_INPUT_MORE |
DESCRIPTOR_STATUS |
DESCRIPTOR_BRANCH_ALWAYS);
- d->data_address = cpu_to_le32(ctx->dma_addrs[i]);
+ d->data_address = cpu_to_le32(ar_buffer_bus(ctx, i));
d->branch_address = cpu_to_le32(ctx->descriptors_bus +
ar_next_buffer_index(i) * sizeof(struct descriptor));
}

return 0;
+
+out_of_memory:
+ ar_context_release(ctx);
+
+ return -ENOMEM;
}

static void ar_context_run(struct ar_context *ctx)
@@ -2741,28 +2717,29 @@ static void ohci_write_csr(struct fw_card *card, int csr_offset, u32 value)

static void flush_iso_completions(struct iso_context *ctx, enum fw_iso_context_completions_cause cause)
{
- trace_isoc_inbound_single_completions(&ctx->base, ctx->sc.last_timestamp, cause,
- ctx->sc.header, ctx->sc.header_length);
- trace_isoc_outbound_completions(&ctx->base, ctx->sc.last_timestamp, cause, ctx->sc.header,
- ctx->sc.header_length);
+ trace_isoc_inbound_single_completions(&ctx->base, ctx->last_timestamp, cause, ctx->header,
+ ctx->header_length);
+ trace_isoc_outbound_completions(&ctx->base, ctx->last_timestamp, cause, ctx->header,
+ ctx->header_length);

- ctx->base.callback.sc(&ctx->base, ctx->sc.last_timestamp, ctx->sc.header_length,
- ctx->sc.header, ctx->base.callback_data);
- ctx->sc.header_length = 0;
+ ctx->base.callback.sc(&ctx->base, ctx->last_timestamp,
+ ctx->header_length, ctx->header,
+ ctx->base.callback_data);
+ ctx->header_length = 0;
}

static void copy_iso_headers(struct iso_context *ctx, const u32 *dma_hdr)
{
u32 *ctx_hdr;

- if (ctx->sc.header_length + ctx->base.header_size > ctx->base.header_storage_size) {
- if (ctx->base.flags & FW_ISO_CONTEXT_FLAG_DROP_OVERFLOW_HEADERS)
+ if (ctx->header_length + ctx->base.header_size > PAGE_SIZE) {
+ if (ctx->base.drop_overflow_headers)
return;
flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_HEADER_OVERFLOW);
}

- ctx_hdr = ctx->sc.header + ctx->sc.header_length;
- ctx->sc.last_timestamp = (u16)le32_to_cpu((__force __le32)dma_hdr[0]);
+ ctx_hdr = ctx->header + ctx->header_length;
+ ctx->last_timestamp = (u16)le32_to_cpu((__force __le32)dma_hdr[0]);

/*
* The two iso header quadlets are byteswapped to little
@@ -2775,7 +2752,7 @@ static void copy_iso_headers(struct iso_context *ctx, const u32 *dma_hdr)
ctx_hdr[1] = swab32(dma_hdr[0]); /* timestamp */
if (ctx->base.header_size > 8)
memcpy(&ctx_hdr[2], &dma_hdr[2], ctx->base.header_size - 8);
- ctx->sc.header_length += ctx->base.header_size;
+ ctx->header_length += ctx->base.header_size;
}

static int handle_ir_packet_per_buffer(struct context *context,
@@ -2828,8 +2805,8 @@ static int handle_ir_buffer_fill(struct context *context,
buffer_dma = le32_to_cpu(last->data_address);

if (completed > 0) {
- ctx->mc.buffer_bus = buffer_dma;
- ctx->mc.completed = completed;
+ ctx->mc_buffer_bus = buffer_dma;
+ ctx->mc_completed = completed;
}

if (res_count != 0)
@@ -2848,7 +2825,7 @@ static int handle_ir_buffer_fill(struct context *context,
ctx->base.callback.mc(&ctx->base,
buffer_dma + completed,
ctx->base.callback_data);
- ctx->mc.completed = 0;
+ ctx->mc_completed = 0;
}

return 1;
@@ -2857,16 +2834,17 @@ static int handle_ir_buffer_fill(struct context *context,
static void flush_ir_buffer_fill(struct iso_context *ctx)
{
dma_sync_single_range_for_cpu(ctx->context.ohci->card.device,
- ctx->mc.buffer_bus & PAGE_MASK,
- ctx->mc.buffer_bus & ~PAGE_MASK,
- ctx->mc.completed, DMA_FROM_DEVICE);
+ ctx->mc_buffer_bus & PAGE_MASK,
+ ctx->mc_buffer_bus & ~PAGE_MASK,
+ ctx->mc_completed, DMA_FROM_DEVICE);

- trace_isoc_inbound_multiple_completions(&ctx->base, ctx->mc.completed,
+ trace_isoc_inbound_multiple_completions(&ctx->base, ctx->mc_completed,
FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH);

- ctx->base.callback.mc(&ctx->base, ctx->mc.buffer_bus + ctx->mc.completed,
+ ctx->base.callback.mc(&ctx->base,
+ ctx->mc_buffer_bus + ctx->mc_completed,
ctx->base.callback_data);
- ctx->mc.completed = 0;
+ ctx->mc_completed = 0;
}

static inline void sync_it_packet_for_cpu(struct context *context,
@@ -2924,18 +2902,18 @@ static int handle_it_packet(struct context *context,

sync_it_packet_for_cpu(context, d);

- if (ctx->sc.header_length + 4 > ctx->base.header_storage_size) {
- if (ctx->base.flags & FW_ISO_CONTEXT_FLAG_DROP_OVERFLOW_HEADERS)
+ if (ctx->header_length + 4 > PAGE_SIZE) {
+ if (ctx->base.drop_overflow_headers)
return 1;
flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_HEADER_OVERFLOW);
}

- ctx_hdr = ctx->sc.header + ctx->sc.header_length;
- ctx->sc.last_timestamp = le16_to_cpu(last->res_count);
+ ctx_hdr = ctx->header + ctx->header_length;
+ ctx->last_timestamp = le16_to_cpu(last->res_count);
/* Present this value as big-endian to match the receive code */
*ctx_hdr = cpu_to_be32((le16_to_cpu(pd->transfer_status) << 16) |
le16_to_cpu(pd->res_count));
- ctx->sc.header_length += 4;
+ ctx->header_length += 4;

if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS))
flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_INTERRUPT);
@@ -2954,11 +2932,10 @@ static void set_multichannel_mask(struct fw_ohci *ohci, u64 channels)
ohci->mc_channels = channels;
}

-static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, int type, int channel,
- size_t header_size, size_t header_storage_size)
+static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
+ int type, int channel, size_t header_size)
{
struct fw_ohci *ohci = fw_ohci(card);
- void *header __free(kvfree) = NULL;
struct iso_context *ctx;
descriptor_callback_t callback;
u64 *channels;
@@ -3013,29 +2990,26 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, in
}

memset(ctx, 0, sizeof(*ctx));
-
- if (type != FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) {
- ctx->sc.header_length = 0;
- header = kvmalloc(header_storage_size, GFP_KERNEL);
- if (!header) {
- ret = -ENOMEM;
- goto out;
- }
+ ctx->header_length = 0;
+ ctx->header = (void *) __get_free_page(GFP_KERNEL);
+ if (ctx->header == NULL) {
+ ret = -ENOMEM;
+ goto out;
}
-
ret = context_init(&ctx->context, ohci, regs, callback);
if (ret < 0)
- goto out;
+ goto out_with_header;
fw_iso_context_init_work(&ctx->base, ohci_isoc_context_work);

- if (type != FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) {
- ctx->sc.header = no_free_ptr(header);
- } else {
+ if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) {
set_multichannel_mask(ohci, 0);
- ctx->mc.completed = 0;
+ ctx->mc_completed = 0;
}

return &ctx->base;
+
+ out_with_header:
+ free_page((unsigned long)ctx->header);
out:
scoped_guard(spinlock_irq, &ohci->lock) {
switch (type) {
@@ -3135,11 +3109,7 @@ static void ohci_free_iso_context(struct fw_iso_context *base)

ohci_stop_iso(base);
context_release(&ctx->context);
-
- if (base->type != FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) {
- kvfree(ctx->sc.header);
- ctx->sc.header = NULL;
- }
+ free_page((unsigned long)ctx->header);

guard(spinlock_irqsave)(&ohci->lock);

@@ -3214,7 +3184,7 @@ static int queue_iso_transmit(struct iso_context *ctx,
struct descriptor *d, *last, *pd;
struct fw_iso_packet *p;
__le32 *header;
- dma_addr_t d_bus;
+ dma_addr_t d_bus, page_bus;
u32 z, header_z, payload_z, irq;
u32 payload_index, payload_end_index, next_page_index;
int page, end_page, i, length, offset;
@@ -3284,11 +3254,11 @@ static int queue_iso_transmit(struct iso_context *ctx,
min(next_page_index, payload_end_index) - payload_index;
pd[i].req_count = cpu_to_le16(length);

- dma_addr_t dma_addr = buffer->dma_addrs[page];
- pd[i].data_address = cpu_to_le32(dma_addr + offset);
+ page_bus = page_private(buffer->pages[page]);
+ pd[i].data_address = cpu_to_le32(page_bus + offset);

dma_sync_single_range_for_device(ctx->context.ohci->card.device,
- dma_addr, offset, length,
+ page_bus, offset, length,
DMA_TO_DEVICE);

payload_index += length;
@@ -3317,7 +3287,7 @@ static int queue_iso_packet_per_buffer(struct iso_context *ctx,
{
struct device *device = ctx->context.ohci->card.device;
struct descriptor *d, *pd;
- dma_addr_t d_bus;
+ dma_addr_t d_bus, page_bus;
u32 z, header_z, rest;
int i, j, length;
int page, offset, packet_count, header_size, payload_per_buffer;
@@ -3367,10 +3337,10 @@ static int queue_iso_packet_per_buffer(struct iso_context *ctx,
pd->res_count = pd->req_count;
pd->transfer_status = 0;

- dma_addr_t dma_addr = buffer->dma_addrs[page];
- pd->data_address = cpu_to_le32(dma_addr + offset);
+ page_bus = page_private(buffer->pages[page]);
+ pd->data_address = cpu_to_le32(page_bus + offset);

- dma_sync_single_range_for_device(device, dma_addr,
+ dma_sync_single_range_for_device(device, page_bus,
offset, length,
DMA_FROM_DEVICE);

@@ -3397,7 +3367,7 @@ static int queue_iso_buffer_fill(struct iso_context *ctx,
unsigned long payload)
{
struct descriptor *d;
- dma_addr_t d_bus;
+ dma_addr_t d_bus, page_bus;
int page, offset, rest, z, i, length;

page = payload >> PAGE_SHIFT;
@@ -3430,11 +3400,11 @@ static int queue_iso_buffer_fill(struct iso_context *ctx,
d->res_count = d->req_count;
d->transfer_status = 0;

- dma_addr_t dma_addr = buffer->dma_addrs[page];
- d->data_address = cpu_to_le32(dma_addr + offset);
+ page_bus = page_private(buffer->pages[page]);
+ d->data_address = cpu_to_le32(page_bus + offset);

dma_sync_single_range_for_device(ctx->context.ohci->card.device,
- dma_addr, offset, length,
+ page_bus, offset, length,
DMA_FROM_DEVICE);

rest -= length;
@@ -3487,11 +3457,11 @@ static int ohci_flush_iso_completions(struct fw_iso_context *base)
switch (base->type) {
case FW_ISO_CONTEXT_TRANSMIT:
case FW_ISO_CONTEXT_RECEIVE:
- if (ctx->sc.header_length != 0)
+ if (ctx->header_length != 0)
flush_iso_completions(ctx, FW_ISO_CONTEXT_COMPLETIONS_CAUSE_FLUSH);
break;
case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
- if (ctx->mc.completed != 0)
+ if (ctx->mc_completed != 0)
flush_ir_buffer_fill(ctx);
break;
default:
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index 986d712e4d94..6143b7d28eac 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -526,13 +526,14 @@ struct fw_iso_packet {
struct fw_iso_buffer {
enum dma_data_direction direction;
struct page **pages;
- dma_addr_t *dma_addrs;
int page_count;
+ int page_count_mapped;
};

int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
int page_count, enum dma_data_direction direction);
void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card);
+size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed);

struct fw_iso_context;
typedef void (*fw_iso_callback_t)(struct fw_iso_context *context,
@@ -546,26 +547,21 @@ union fw_iso_callback {
fw_iso_mc_callback_t mc;
};

-enum fw_iso_context_flag {
- FW_ISO_CONTEXT_FLAG_DROP_OVERFLOW_HEADERS = BIT(0),
-};
-
struct fw_iso_context {
struct fw_card *card;
struct work_struct work;
int type;
int channel;
int speed;
- int flags;
+ bool drop_overflow_headers;
size_t header_size;
- size_t header_storage_size;
union fw_iso_callback callback;
void *callback_data;
};

-struct fw_iso_context *__fw_iso_context_create(struct fw_card *card, int type, int channel,
- int speed, size_t header_size, size_t header_storage_size,
- union fw_iso_callback callback, void *callback_data);
+struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
+ int type, int channel, int speed, size_t header_size,
+ fw_iso_callback_t callback, void *callback_data);
int fw_iso_context_set_channels(struct fw_iso_context *ctx, u64 *channels);
int fw_iso_context_queue(struct fw_iso_context *ctx,
struct fw_iso_packet *packet,
@@ -574,26 +570,6 @@ int fw_iso_context_queue(struct fw_iso_context *ctx,
void fw_iso_context_queue_flush(struct fw_iso_context *ctx);
int fw_iso_context_flush_completions(struct fw_iso_context *ctx);

-static inline struct fw_iso_context *fw_iso_context_create(struct fw_card *card, int type,
- int channel, int speed, size_t header_size, fw_iso_callback_t callback,
- void *callback_data)
-{
- union fw_iso_callback cb = { .sc = callback };
-
- return __fw_iso_context_create(card, type, channel, speed, header_size, PAGE_SIZE, cb,
- callback_data);
-}
-
-static inline struct fw_iso_context *fw_iso_context_create_with_header_storage_size(
- struct fw_card *card, int type, int channel, int speed, size_t header_size,
- size_t header_storage_size, fw_iso_callback_t callback, void *callback_data)
-{
- union fw_iso_callback cb = { .sc = callback };
-
- return __fw_iso_context_create(card, type, channel, speed, header_size, header_storage_size,
- cb, callback_data);
-}
-
/**
* fw_iso_context_schedule_flush_completions() - schedule work item to process isochronous context.
* @ctx: the isochronous context
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index e97721f80f65..4f51f2ad9f65 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -191,6 +191,8 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime)
{
struct snd_pcm_hardware *hw = &runtime->hw;
+ unsigned int ctx_header_size;
+ unsigned int maximum_usec_per_period;
int err;

hw->info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -210,6 +212,21 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
hw->period_bytes_max = hw->period_bytes_min * 2048;
hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;

+ // Linux driver for 1394 OHCI controller voluntarily flushes isoc
+ // context when total size of accumulated context header reaches
+ // PAGE_SIZE. This kicks work for the isoc context and brings
+ // callback in the middle of scheduled interrupts.
+ // Although AMDTP streams in the same domain use the same events per
+ // IRQ, use the largest size of context header between IT/IR contexts.
+ // Here, use the value of context header in IR context is for both
+ // contexts.
+ if (!(s->flags & CIP_NO_HEADER))
+ ctx_header_size = IR_CTX_HEADER_SIZE_CIP;
+ else
+ ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP;
+ maximum_usec_per_period = USEC_PER_SEC * PAGE_SIZE /
+ CYCLES_PER_SECOND / ctx_header_size;
+
// In IEC 61883-6, one isoc packet can transfer events up to the value
// of syt interval. This comes from the interval of isoc cycle. As 1394
// OHCI controller can generate hardware IRQ per isoc packet, the
@@ -222,10 +239,9 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
// Due to the above protocol design, the minimum PCM frames per
// interrupt should be double of the value of syt interval, thus it is
// 250 usec.
- // There is no reason, but up to 250 msec to avoid consuming resources so much.
err = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
- 250, USEC_PER_SEC / 4);
+ 250, maximum_usec_per_period);
if (err < 0)
goto end;

@@ -245,7 +261,6 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0)
goto end;
-
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
apply_constraint_to_size, NULL,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
@@ -1700,9 +1715,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
} else {
dir = DMA_TO_DEVICE;
type = FW_ISO_CONTEXT_TRANSMIT;
- // Although no effect for IT context, this value is required to compute the size
- // of header storage correctly.
- ctx_header_size = sizeof(__be32);
+ ctx_header_size = 0; // No effect for IT context.
}
max_ctx_payload_size = amdtp_stream_get_max_ctx_payload_size(s);

@@ -1711,9 +1724,9 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
return err;
s->queue_size = queue_size;

- s->context = fw_iso_context_create_with_header_storage_size(
- fw_parent_device(s->unit)->card, type, channel, speed, ctx_header_size,
- ctx_header_size * queue_size, amdtp_stream_first_callback, s);
+ s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
+ type, channel, speed, ctx_header_size,
+ amdtp_stream_first_callback, s);
if (IS_ERR(s->context)) {
err = PTR_ERR(s->context);
if (err == -EBUSY)