Skip to content

Commit 0821009

Browse files
davejiangvinodkoul
authored andcommitted
dmaengine: fix channel index enumeration
When the channel register code was changed to allow hotplug operations, dynamic indexing wasn't taken into account. When channels are randomly plugged and unplugged out of order, the serial indexing breaks. Convert channel indexing to using IDA tracking in order to allow dynamic assignment. The previous code does not cause any regression bug for existing channel allocation besides idxd driver since the hotplug usage case is only used by idxd at this point. With this change, the chan->idr_ref is also not needed any longer. We can have a device with no channels registered due to hot plug. The channel device release code no longer should attempt to free the dma device id on the last channel release. Fixes: e81274c ("dmaengine: add support to dynamic register/unregister of channels") Reported-by: Yixin Zhang <[email protected]> Signed-off-by: Dave Jiang <[email protected]> Tested-by: Yixin Zhang <[email protected]> Link: https://lore.kernel.org/r/158679961260.7674.8485924270472851852.stgit@djiang5-desk3.ch.intel.com Signed-off-by: Vinod Koul <[email protected]>
1 parent 0c89446 commit 0821009

File tree

2 files changed

+28
-36
lines changed

2 files changed

+28
-36
lines changed

drivers/dma/dmaengine.c

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,6 @@ static void chan_dev_release(struct device *dev)
232232
struct dma_chan_dev *chan_dev;
233233

234234
chan_dev = container_of(dev, typeof(*chan_dev), device);
235-
if (atomic_dec_and_test(chan_dev->idr_ref)) {
236-
ida_free(&dma_ida, chan_dev->dev_id);
237-
kfree(chan_dev->idr_ref);
238-
}
239235
kfree(chan_dev);
240236
}
241237

@@ -1043,27 +1039,9 @@ static int get_dma_id(struct dma_device *device)
10431039
}
10441040

10451041
static int __dma_async_device_channel_register(struct dma_device *device,
1046-
struct dma_chan *chan,
1047-
int chan_id)
1042+
struct dma_chan *chan)
10481043
{
10491044
int rc = 0;
1050-
int chancnt = device->chancnt;
1051-
atomic_t *idr_ref;
1052-
struct dma_chan *tchan;
1053-
1054-
tchan = list_first_entry_or_null(&device->channels,
1055-
struct dma_chan, device_node);
1056-
if (!tchan)
1057-
return -ENODEV;
1058-
1059-
if (tchan->dev) {
1060-
idr_ref = tchan->dev->idr_ref;
1061-
} else {
1062-
idr_ref = kmalloc(sizeof(*idr_ref), GFP_KERNEL);
1063-
if (!idr_ref)
1064-
return -ENOMEM;
1065-
atomic_set(idr_ref, 0);
1066-
}
10671045

10681046
chan->local = alloc_percpu(typeof(*chan->local));
10691047
if (!chan->local)
@@ -1079,29 +1057,36 @@ static int __dma_async_device_channel_register(struct dma_device *device,
10791057
* When the chan_id is a negative value, we are dynamically adding
10801058
* the channel. Otherwise we are static enumerating.
10811059
*/
1082-
chan->chan_id = chan_id < 0 ? chancnt : chan_id;
1060+
mutex_lock(&device->chan_mutex);
1061+
chan->chan_id = ida_alloc(&device->chan_ida, GFP_KERNEL);
1062+
mutex_unlock(&device->chan_mutex);
1063+
if (chan->chan_id < 0) {
1064+
pr_err("%s: unable to alloc ida for chan: %d\n",
1065+
__func__, chan->chan_id);
1066+
goto err_out;
1067+
}
1068+
10831069
chan->dev->device.class = &dma_devclass;
10841070
chan->dev->device.parent = device->dev;
10851071
chan->dev->chan = chan;
1086-
chan->dev->idr_ref = idr_ref;
10871072
chan->dev->dev_id = device->dev_id;
1088-
atomic_inc(idr_ref);
10891073
dev_set_name(&chan->dev->device, "dma%dchan%d",
10901074
device->dev_id, chan->chan_id);
1091-
10921075
rc = device_register(&chan->dev->device);
10931076
if (rc)
1094-
goto err_out;
1077+
goto err_out_ida;
10951078
chan->client_count = 0;
1096-
device->chancnt = chan->chan_id + 1;
1079+
device->chancnt++;
10971080

10981081
return 0;
10991082

1083+
err_out_ida:
1084+
mutex_lock(&device->chan_mutex);
1085+
ida_free(&device->chan_ida, chan->chan_id);
1086+
mutex_unlock(&device->chan_mutex);
11001087
err_out:
11011088
free_percpu(chan->local);
11021089
kfree(chan->dev);
1103-
if (atomic_dec_return(idr_ref) == 0)
1104-
kfree(idr_ref);
11051090
return rc;
11061091
}
11071092

@@ -1110,7 +1095,7 @@ int dma_async_device_channel_register(struct dma_device *device,
11101095
{
11111096
int rc;
11121097

1113-
rc = __dma_async_device_channel_register(device, chan, -1);
1098+
rc = __dma_async_device_channel_register(device, chan);
11141099
if (rc < 0)
11151100
return rc;
11161101

@@ -1130,6 +1115,9 @@ static void __dma_async_device_channel_unregister(struct dma_device *device,
11301115
device->chancnt--;
11311116
chan->dev->chan = NULL;
11321117
mutex_unlock(&dma_list_mutex);
1118+
mutex_lock(&device->chan_mutex);
1119+
ida_free(&device->chan_ida, chan->chan_id);
1120+
mutex_unlock(&device->chan_mutex);
11331121
device_unregister(&chan->dev->device);
11341122
free_percpu(chan->local);
11351123
}
@@ -1152,7 +1140,7 @@ EXPORT_SYMBOL_GPL(dma_async_device_channel_unregister);
11521140
*/
11531141
int dma_async_device_register(struct dma_device *device)
11541142
{
1155-
int rc, i = 0;
1143+
int rc;
11561144
struct dma_chan* chan;
11571145

11581146
if (!device)
@@ -1257,9 +1245,12 @@ int dma_async_device_register(struct dma_device *device)
12571245
if (rc != 0)
12581246
return rc;
12591247

1248+
mutex_init(&device->chan_mutex);
1249+
ida_init(&device->chan_ida);
1250+
12601251
/* represent channels in sysfs. Probably want devs too */
12611252
list_for_each_entry(chan, &device->channels, device_node) {
1262-
rc = __dma_async_device_channel_register(device, chan, i++);
1253+
rc = __dma_async_device_channel_register(device, chan);
12631254
if (rc < 0)
12641255
goto err_out;
12651256
}
@@ -1334,6 +1325,7 @@ void dma_async_device_unregister(struct dma_device *device)
13341325
*/
13351326
dma_cap_set(DMA_PRIVATE, device->cap_mask);
13361327
dma_channel_rebalance();
1328+
ida_free(&dma_ida, device->dev_id);
13371329
dma_device_put(device);
13381330
mutex_unlock(&dma_list_mutex);
13391331
}

include/linux/dmaengine.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,13 +341,11 @@ struct dma_chan {
341341
* @chan: driver channel device
342342
* @device: sysfs device
343343
* @dev_id: parent dma_device dev_id
344-
* @idr_ref: reference count to gate release of dma_device dev_id
345344
*/
346345
struct dma_chan_dev {
347346
struct dma_chan *chan;
348347
struct device device;
349348
int dev_id;
350-
atomic_t *idr_ref;
351349
};
352350

353351
/**
@@ -835,6 +833,8 @@ struct dma_device {
835833
int dev_id;
836834
struct device *dev;
837835
struct module *owner;
836+
struct ida chan_ida;
837+
struct mutex chan_mutex; /* to protect chan_ida */
838838

839839
u32 src_addr_widths;
840840
u32 dst_addr_widths;

0 commit comments

Comments
 (0)