Skip to content

Commit 0d6dd47

Browse files
committed
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
2 parents a31980d + 6b61731 commit 0d6dd47

7 files changed

Lines changed: 266 additions & 226 deletions

File tree

drivers/firewire/core-card.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,8 +704,8 @@ static int dummy_enable_phys_dma(struct fw_card *card,
704704
return -ENODEV;
705705
}
706706

707-
static struct fw_iso_context *dummy_allocate_iso_context(struct fw_card *card,
708-
int type, int channel, size_t header_size)
707+
static struct fw_iso_context *dummy_allocate_iso_context(struct fw_card *card, int type,
708+
int channel, size_t header_size, size_t header_storage_size)
709709
{
710710
return ERR_PTR(-ENODEV);
711711
}

drivers/firewire/core-cdev.c

Lines changed: 26 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ struct client {
6363
u64 bus_reset_closure;
6464

6565
struct fw_iso_context *iso_context;
66+
struct mutex iso_context_mutex;
6667
u64 iso_closure;
6768
struct fw_iso_buffer buffer;
6869
unsigned long vm_start;
69-
bool buffer_is_mapped;
7070

7171
struct list_head phy_receiver_link;
7272
u64 phy_receiver_closure;
@@ -306,6 +306,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
306306
INIT_LIST_HEAD(&client->phy_receiver_link);
307307
INIT_LIST_HEAD(&client->link);
308308
kref_init(&client->kref);
309+
mutex_init(&client->iso_context_mutex);
309310

310311
file->private_data = client;
311312

@@ -1025,25 +1026,10 @@ static enum dma_data_direction iso_dma_direction(struct fw_iso_context *context)
10251026
return DMA_FROM_DEVICE;
10261027
}
10271028

1028-
static struct fw_iso_context *fw_iso_mc_context_create(struct fw_card *card,
1029-
fw_iso_mc_callback_t callback,
1030-
void *callback_data)
1031-
{
1032-
struct fw_iso_context *ctx;
1033-
1034-
ctx = fw_iso_context_create(card, FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL,
1035-
0, 0, 0, NULL, callback_data);
1036-
if (!IS_ERR(ctx))
1037-
ctx->callback.mc = callback;
1038-
1039-
return ctx;
1040-
}
1041-
10421029
static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
10431030
{
10441031
struct fw_cdev_create_iso_context *a = &arg->create_iso_context;
10451032
struct fw_iso_context *context;
1046-
union fw_iso_callback cb;
10471033
int ret;
10481034

10491035
BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT ||
@@ -1055,59 +1041,52 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
10551041
case FW_ISO_CONTEXT_TRANSMIT:
10561042
if (a->speed > SCODE_3200 || a->channel > 63)
10571043
return -EINVAL;
1058-
1059-
cb.sc = iso_callback;
10601044
break;
10611045

10621046
case FW_ISO_CONTEXT_RECEIVE:
10631047
if (a->header_size < 4 || (a->header_size & 3) ||
10641048
a->channel > 63)
10651049
return -EINVAL;
1066-
1067-
cb.sc = iso_callback;
10681050
break;
10691051

10701052
case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
1071-
cb.mc = iso_mc_callback;
10721053
break;
10731054

10741055
default:
10751056
return -EINVAL;
10761057
}
10771058

10781059
if (a->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL)
1079-
context = fw_iso_mc_context_create(client->device->card, cb.mc,
1080-
client);
1060+
context = fw_iso_mc_context_create(client->device->card, iso_mc_callback, client);
10811061
else
1082-
context = fw_iso_context_create(client->device->card, a->type,
1083-
a->channel, a->speed,
1084-
a->header_size, cb.sc, client);
1062+
context = fw_iso_context_create(client->device->card, a->type, a->channel, a->speed,
1063+
a->header_size, iso_callback, client);
10851064
if (IS_ERR(context))
10861065
return PTR_ERR(context);
10871066
if (client->version < FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW)
1088-
context->drop_overflow_headers = true;
1067+
context->flags |= FW_ISO_CONTEXT_FLAG_DROP_OVERFLOW_HEADERS;
10891068

10901069
// We only support one context at this time.
1091-
guard(spinlock_irq)(&client->lock);
1092-
1093-
if (client->iso_context != NULL) {
1094-
fw_iso_context_destroy(context);
1095-
1096-
return -EBUSY;
1097-
}
1098-
if (!client->buffer_is_mapped) {
1099-
ret = fw_iso_buffer_map_dma(&client->buffer,
1100-
client->device->card,
1101-
iso_dma_direction(context));
1102-
if (ret < 0) {
1070+
scoped_guard(mutex, &client->iso_context_mutex) {
1071+
if (client->iso_context != NULL) {
11031072
fw_iso_context_destroy(context);
11041073

1105-
return ret;
1074+
return -EBUSY;
1075+
}
1076+
// The DMA mapping operation is available if the buffer is already allocated by
1077+
// mmap(2) system call. If not, it is delegated to the system call.
1078+
if (client->buffer.pages && !client->buffer.dma_addrs) {
1079+
ret = fw_iso_buffer_map_dma(&client->buffer, client->device->card,
1080+
iso_dma_direction(context));
1081+
if (ret < 0) {
1082+
fw_iso_context_destroy(context);
1083+
1084+
return ret;
1085+
}
11061086
}
1107-
client->buffer_is_mapped = true;
1087+
client->iso_closure = a->closure;
1088+
client->iso_context = context;
11081089
}
1109-
client->iso_closure = a->closure;
1110-
client->iso_context = context;
11111090

11121091
a->handle = 0;
11131092

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

1829-
scoped_guard(spinlock_irq, &client->lock) {
1808+
scoped_guard(mutex, &client->iso_context_mutex) {
1809+
// The direction of DMA can be determined if the isochronous context is already
1810+
// allocated. If not, the DMA mapping operation is postponed after the allocation.
18301811
if (client->iso_context) {
18311812
ret = fw_iso_buffer_map_dma(&client->buffer, client->device->card,
18321813
iso_dma_direction(client->iso_context));
18331814
if (ret < 0)
18341815
goto fail;
1835-
client->buffer_is_mapped = true;
18361816
}
18371817
}
18381818

@@ -1879,6 +1859,7 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
18791859

18801860
if (client->iso_context)
18811861
fw_iso_context_destroy(client->iso_context);
1862+
mutex_destroy(&client->iso_context_mutex);
18821863

18831864
if (client->buffer.pages)
18841865
fw_iso_buffer_destroy(&client->buffer, client->device->card);

drivers/firewire/core-iso.c

Lines changed: 55 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -30,48 +30,57 @@
3030

3131
int fw_iso_buffer_alloc(struct fw_iso_buffer *buffer, int page_count)
3232
{
33-
int i;
33+
struct page **page_array __free(kfree) = kcalloc(page_count, sizeof(page_array[0]), GFP_KERNEL);
3434

35-
buffer->page_count = 0;
36-
buffer->page_count_mapped = 0;
37-
buffer->pages = kmalloc_array(page_count, sizeof(buffer->pages[0]),
38-
GFP_KERNEL);
39-
if (buffer->pages == NULL)
35+
if (!page_array)
4036
return -ENOMEM;
4137

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

49+
buffer->page_count = page_count;
50+
buffer->pages = no_free_ptr(page_array);
51+
5352
return 0;
5453
}
5554

5655
int fw_iso_buffer_map_dma(struct fw_iso_buffer *buffer, struct fw_card *card,
5756
enum dma_data_direction direction)
5857
{
59-
dma_addr_t address;
58+
dma_addr_t *dma_addrs __free(kfree) = kcalloc(buffer->page_count, sizeof(dma_addrs[0]),
59+
GFP_KERNEL);
6060
int i;
6161

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

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

70-
set_page_private(buffer->pages[i], address);
74+
dma_addrs[i] = dma_addr;
7175
}
72-
buffer->page_count_mapped = i;
73-
if (i < buffer->page_count)
76+
if (i < buffer->page_count) {
77+
while (i-- > 0)
78+
dma_unmap_page(card->device, dma_addrs[i], PAGE_SIZE, buffer->direction);
7479
return -ENOMEM;
80+
}
81+
82+
buffer->direction = direction;
83+
buffer->dma_addrs = no_free_ptr(dma_addrs);
7584

7685
return 0;
7786
}
@@ -96,58 +105,57 @@ EXPORT_SYMBOL(fw_iso_buffer_init);
96105
void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer,
97106
struct fw_card *card)
98107
{
99-
int i;
100-
dma_addr_t address;
108+
if (buffer->dma_addrs) {
109+
for (int i = 0; i < buffer->page_count; ++i) {
110+
dma_addr_t dma_addr = buffer->dma_addrs[i];
111+
dma_unmap_page(card->device, dma_addr, PAGE_SIZE, buffer->direction);
112+
}
113+
kfree(buffer->dma_addrs);
114+
buffer->dma_addrs = NULL;
115+
}
101116

102-
for (i = 0; i < buffer->page_count_mapped; i++) {
103-
address = page_private(buffer->pages[i]);
104-
dma_unmap_page(card->device, address,
105-
PAGE_SIZE, buffer->direction);
117+
if (buffer->pages) {
118+
release_pages(buffer->pages, buffer->page_count);
119+
kfree(buffer->pages);
120+
buffer->pages = NULL;
106121
}
107-
for (i = 0; i < buffer->page_count; i++)
108-
__free_page(buffer->pages[i]);
109122

110-
kfree(buffer->pages);
111-
buffer->pages = NULL;
112123
buffer->page_count = 0;
113-
buffer->page_count_mapped = 0;
114124
}
115125
EXPORT_SYMBOL(fw_iso_buffer_destroy);
116126

117127
/* Convert DMA address to offset into virtually contiguous buffer. */
118128
size_t fw_iso_buffer_lookup(struct fw_iso_buffer *buffer, dma_addr_t completed)
119129
{
120-
size_t i;
121-
dma_addr_t address;
122-
ssize_t offset;
123-
124-
for (i = 0; i < buffer->page_count; i++) {
125-
address = page_private(buffer->pages[i]);
126-
offset = (ssize_t)completed - (ssize_t)address;
130+
for (int i = 0; i < buffer->page_count; i++) {
131+
dma_addr_t dma_addr = buffer->dma_addrs[i];
132+
ssize_t offset = (ssize_t)completed - (ssize_t)dma_addr;
127133
if (offset > 0 && offset <= PAGE_SIZE)
128134
return (i << PAGE_SHIFT) + offset;
129135
}
130136

131137
return 0;
132138
}
133139

134-
struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
135-
int type, int channel, int speed, size_t header_size,
136-
fw_iso_callback_t callback, void *callback_data)
140+
struct fw_iso_context *__fw_iso_context_create(struct fw_card *card, int type, int channel,
141+
int speed, size_t header_size, size_t header_storage_size,
142+
union fw_iso_callback callback, void *callback_data)
137143
{
138144
struct fw_iso_context *ctx;
139145

140-
ctx = card->driver->allocate_iso_context(card,
141-
type, channel, header_size);
146+
ctx = card->driver->allocate_iso_context(card, type, channel, header_size,
147+
header_storage_size);
142148
if (IS_ERR(ctx))
143149
return ctx;
144150

145151
ctx->card = card;
146152
ctx->type = type;
147153
ctx->channel = channel;
148154
ctx->speed = speed;
155+
ctx->flags = 0;
149156
ctx->header_size = header_size;
150-
ctx->callback.sc = callback;
157+
ctx->header_storage_size = header_storage_size;
158+
ctx->callback = callback;
151159
ctx->callback_data = callback_data;
152160

153161
trace_isoc_outbound_allocate(ctx, channel, speed);
@@ -156,7 +164,7 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
156164

157165
return ctx;
158166
}
159-
EXPORT_SYMBOL(fw_iso_context_create);
167+
EXPORT_SYMBOL(__fw_iso_context_create);
160168

161169
void fw_iso_context_destroy(struct fw_iso_context *ctx)
162170
{

drivers/firewire/core.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ struct fw_card_driver {
100100
void (*write_csr)(struct fw_card *card, int csr_offset, u32 value);
101101

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

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

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

176+
static inline struct fw_iso_context *fw_iso_mc_context_create(struct fw_card *card,
177+
fw_iso_mc_callback_t callback, void *callback_data)
178+
{
179+
union fw_iso_callback cb = { .mc = callback };
180+
181+
return __fw_iso_context_create(card, FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL, 0, 0, 0, 0, cb,
182+
callback_data);
183+
}
184+
175185

176186
/* -topology */
177187

0 commit comments

Comments
 (0)