diff options
author | Dan Williams <dan.j.williams@intel.com> | 2007-07-09 14:56:42 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2007-07-13 11:06:13 -0400 |
commit | d379b01e9087a582d58f4b678208a4f8d8376fe7 (patch) | |
tree | 155920bca93c18afba66b9d5acfecd359d5bec65 | |
parent | 7405f74badf46b5d023c5d2b670b4471525f6c91 (diff) |
dmaengine: make clients responsible for managing channels
The current implementation assumes that a channel will only be used by one
client at a time. In order to enable channel sharing the dmaengine core is
changed to a model where clients subscribe to channel-available-events.
Instead of tracking how many channels a client wants and how many it has
received the core just broadcasts the available channels and lets the
clients optionally take a reference. The core learns about the clients'
needs at dma_event_callback time.
In support of multiple operation types, clients can specify a capability
mask to only be notified of channels that satisfy a certain set of
capabilities.
Changelog:
* removed DMA_TX_ARRAY_INIT, no longer needed
* dma_client_chan_free -> dma_chan_release: switch to global reference
counting only at device unregistration time, before it was also happening
at client unregistration time
* clients now return dma_state_client to dmaengine (ack, dup, nak)
* checkpatch.pl fixes
* fixup merge with git-ioat
Cc: Chris Leech <christopher.leech@intel.com>
Signed-off-by: Shannon Nelson <shannon.nelson@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Acked-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/dma/dmaengine.c | 217 | ||||
-rw-r--r-- | drivers/dma/ioatdma.c | 1 | ||||
-rw-r--r-- | drivers/dma/ioatdma.h | 3 | ||||
-rw-r--r-- | include/linux/dmaengine.h | 58 | ||||
-rw-r--r-- | net/core/dev.c | 112 |
5 files changed, 224 insertions, 167 deletions
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 404cc7b6e705..82489923af09 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c | |||
@@ -37,11 +37,11 @@ | |||
37 | * Each device has a channels list, which runs unlocked but is never modified | 37 | * Each device has a channels list, which runs unlocked but is never modified |
38 | * once the device is registered, it's just setup by the driver. | 38 | * once the device is registered, it's just setup by the driver. |
39 | * | 39 | * |
40 | * Each client has a channels list, it's only modified under the client->lock | 40 | * Each client is responsible for keeping track of the channels it uses. See |
41 | * and in an RCU callback, so it's safe to read under rcu_read_lock(). | 41 | * the definition of dma_event_callback in dmaengine.h. |
42 | * | 42 | * |
43 | * Each device has a kref, which is initialized to 1 when the device is | 43 | * Each device has a kref, which is initialized to 1 when the device is |
44 | * registered. A kref_put is done for each class_device registered. When the | 44 | * registered. A kref_get is done for each class_device registered. When the |
45 | * class_device is released, the coresponding kref_put is done in the release | 45 | * class_device is released, the coresponding kref_put is done in the release |
46 | * method. Every time one of the device's channels is allocated to a client, | 46 | * method. Every time one of the device's channels is allocated to a client, |
47 | * a kref_get occurs. When the channel is freed, the coresponding kref_put | 47 | * a kref_get occurs. When the channel is freed, the coresponding kref_put |
@@ -51,10 +51,12 @@ | |||
51 | * references to finish. | 51 | * references to finish. |
52 | * | 52 | * |
53 | * Each channel has an open-coded implementation of Rusty Russell's "bigref," | 53 | * Each channel has an open-coded implementation of Rusty Russell's "bigref," |
54 | * with a kref and a per_cpu local_t. A single reference is set when on an | 54 | * with a kref and a per_cpu local_t. A dma_chan_get is called when a client |
55 | * ADDED event, and removed with a REMOVE event. Net DMA client takes an | 55 | * signals that it wants to use a channel, and dma_chan_put is called when |
56 | * extra reference per outstanding transaction. The relase function does a | 56 | * a channel is removed or a client using it is unregesitered. A client can |
57 | * kref_put on the device. -ChrisL | 57 | * take extra references per outstanding transaction, as is the case with |
58 | * the NET DMA client. The release function does a kref_put on the device. | ||
59 | * -ChrisL, DanW | ||
58 | */ | 60 | */ |
59 | 61 | ||
60 | #include <linux/init.h> | 62 | #include <linux/init.h> |
@@ -102,8 +104,19 @@ static ssize_t show_bytes_transferred(struct class_device *cd, char *buf) | |||
102 | static ssize_t show_in_use(struct class_device *cd, char *buf) | 104 | static ssize_t show_in_use(struct class_device *cd, char *buf) |
103 | { | 105 | { |
104 | struct dma_chan *chan = container_of(cd, struct dma_chan, class_dev); | 106 | struct dma_chan *chan = container_of(cd, struct dma_chan, class_dev); |
107 | int in_use = 0; | ||
108 | |||
109 | if (unlikely(chan->slow_ref) && | ||
110 | atomic_read(&chan->refcount.refcount) > 1) | ||
111 | in_use = 1; | ||
112 | else { | ||
113 | if (local_read(&(per_cpu_ptr(chan->local, | ||
114 | get_cpu())->refcount)) > 0) | ||
115 | in_use = 1; | ||
116 | put_cpu(); | ||
117 | } | ||
105 | 118 | ||
106 | return sprintf(buf, "%d\n", (chan->client ? 1 : 0)); | 119 | return sprintf(buf, "%d\n", in_use); |
107 | } | 120 | } |
108 | 121 | ||
109 | static struct class_device_attribute dma_class_attrs[] = { | 122 | static struct class_device_attribute dma_class_attrs[] = { |
@@ -129,42 +142,53 @@ static struct class dma_devclass = { | |||
129 | 142 | ||
130 | /* --- client and device registration --- */ | 143 | /* --- client and device registration --- */ |
131 | 144 | ||
145 | #define dma_chan_satisfies_mask(chan, mask) \ | ||
146 | __dma_chan_satisfies_mask((chan), &(mask)) | ||
147 | static int | ||
148 | __dma_chan_satisfies_mask(struct dma_chan *chan, dma_cap_mask_t *want) | ||
149 | { | ||
150 | dma_cap_mask_t has; | ||
151 | |||
152 | bitmap_and(has.bits, want->bits, chan->device->cap_mask.bits, | ||
153 | DMA_TX_TYPE_END); | ||
154 | return bitmap_equal(want->bits, has.bits, DMA_TX_TYPE_END); | ||
155 | } | ||
156 | |||
132 | /** | 157 | /** |
133 | * dma_client_chan_alloc - try to allocate a channel to a client | 158 | * dma_client_chan_alloc - try to allocate channels to a client |
134 | * @client: &dma_client | 159 | * @client: &dma_client |
135 | * | 160 | * |
136 | * Called with dma_list_mutex held. | 161 | * Called with dma_list_mutex held. |
137 | */ | 162 | */ |
138 | static struct dma_chan *dma_client_chan_alloc(struct dma_client *client) | 163 | static void dma_client_chan_alloc(struct dma_client *client) |
139 | { | 164 | { |
140 | struct dma_device *device; | 165 | struct dma_device *device; |
141 | struct dma_chan *chan; | 166 | struct dma_chan *chan; |
142 | unsigned long flags; | ||
143 | int desc; /* allocated descriptor count */ | 167 | int desc; /* allocated descriptor count */ |
168 | enum dma_state_client ack; | ||
144 | 169 | ||
145 | /* Find a channel, any DMA engine will do */ | 170 | /* Find a channel */ |
146 | list_for_each_entry(device, &dma_device_list, global_node) { | 171 | list_for_each_entry(device, &dma_device_list, global_node) |
147 | list_for_each_entry(chan, &device->channels, device_node) { | 172 | list_for_each_entry(chan, &device->channels, device_node) { |
148 | if (chan->client) | 173 | if (!dma_chan_satisfies_mask(chan, client->cap_mask)) |
149 | continue; | 174 | continue; |
150 | 175 | ||
151 | desc = chan->device->device_alloc_chan_resources(chan); | 176 | desc = chan->device->device_alloc_chan_resources(chan); |
152 | if (desc >= 0) { | 177 | if (desc >= 0) { |
153 | kref_get(&device->refcount); | 178 | ack = client->event_callback(client, |
154 | kref_init(&chan->refcount); | 179 | chan, |
155 | chan->slow_ref = 0; | 180 | DMA_RESOURCE_AVAILABLE); |
156 | INIT_RCU_HEAD(&chan->rcu); | 181 | |
157 | chan->client = client; | 182 | /* we are done once this client rejects |
158 | spin_lock_irqsave(&client->lock, flags); | 183 | * an available resource |
159 | list_add_tail_rcu(&chan->client_node, | 184 | */ |
160 | &client->channels); | 185 | if (ack == DMA_ACK) { |
161 | spin_unlock_irqrestore(&client->lock, flags); | 186 | dma_chan_get(chan); |
162 | return chan; | 187 | kref_get(&device->refcount); |
188 | } else if (ack == DMA_NAK) | ||
189 | return; | ||
163 | } | 190 | } |
164 | } | 191 | } |
165 | } | ||
166 | |||
167 | return NULL; | ||
168 | } | 192 | } |
169 | 193 | ||
170 | enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) | 194 | enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) |
@@ -193,7 +217,6 @@ void dma_chan_cleanup(struct kref *kref) | |||
193 | { | 217 | { |
194 | struct dma_chan *chan = container_of(kref, struct dma_chan, refcount); | 218 | struct dma_chan *chan = container_of(kref, struct dma_chan, refcount); |
195 | chan->device->device_free_chan_resources(chan); | 219 | chan->device->device_free_chan_resources(chan); |
196 | chan->client = NULL; | ||
197 | kref_put(&chan->device->refcount, dma_async_device_cleanup); | 220 | kref_put(&chan->device->refcount, dma_async_device_cleanup); |
198 | } | 221 | } |
199 | EXPORT_SYMBOL(dma_chan_cleanup); | 222 | EXPORT_SYMBOL(dma_chan_cleanup); |
@@ -209,7 +232,7 @@ static void dma_chan_free_rcu(struct rcu_head *rcu) | |||
209 | kref_put(&chan->refcount, dma_chan_cleanup); | 232 | kref_put(&chan->refcount, dma_chan_cleanup); |
210 | } | 233 | } |
211 | 234 | ||
212 | static void dma_client_chan_free(struct dma_chan *chan) | 235 | static void dma_chan_release(struct dma_chan *chan) |
213 | { | 236 | { |
214 | atomic_add(0x7FFFFFFF, &chan->refcount.refcount); | 237 | atomic_add(0x7FFFFFFF, &chan->refcount.refcount); |
215 | chan->slow_ref = 1; | 238 | chan->slow_ref = 1; |
@@ -217,70 +240,57 @@ static void dma_client_chan_free(struct dma_chan *chan) | |||
217 | } | 240 | } |
218 | 241 | ||
219 | /** | 242 | /** |
220 | * dma_chans_rebalance - reallocate channels to clients | 243 | * dma_chans_notify_available - broadcast available channels to the clients |
221 | * | ||
222 | * When the number of DMA channel in the system changes, | ||
223 | * channels need to be rebalanced among clients. | ||
224 | */ | 244 | */ |
225 | static void dma_chans_rebalance(void) | 245 | static void dma_clients_notify_available(void) |
226 | { | 246 | { |
227 | struct dma_client *client; | 247 | struct dma_client *client; |
228 | struct dma_chan *chan; | ||
229 | unsigned long flags; | ||
230 | 248 | ||
231 | mutex_lock(&dma_list_mutex); | 249 | mutex_lock(&dma_list_mutex); |
232 | 250 | ||
233 | list_for_each_entry(client, &dma_client_list, global_node) { | 251 | list_for_each_entry(client, &dma_client_list, global_node) |
234 | while (client->chans_desired > client->chan_count) { | 252 | dma_client_chan_alloc(client); |
235 | chan = dma_client_chan_alloc(client); | ||
236 | if (!chan) | ||
237 | break; | ||
238 | client->chan_count++; | ||
239 | client->event_callback(client, | ||
240 | chan, | ||
241 | DMA_RESOURCE_ADDED); | ||
242 | } | ||
243 | while (client->chans_desired < client->chan_count) { | ||
244 | spin_lock_irqsave(&client->lock, flags); | ||
245 | chan = list_entry(client->channels.next, | ||
246 | struct dma_chan, | ||
247 | client_node); | ||
248 | list_del_rcu(&chan->client_node); | ||
249 | spin_unlock_irqrestore(&client->lock, flags); | ||
250 | client->chan_count--; | ||
251 | client->event_callback(client, | ||
252 | chan, | ||
253 | DMA_RESOURCE_REMOVED); | ||
254 | dma_client_chan_free(chan); | ||
255 | } | ||
256 | } | ||
257 | 253 | ||
258 | mutex_unlock(&dma_list_mutex); | 254 | mutex_unlock(&dma_list_mutex); |
259 | } | 255 | } |
260 | 256 | ||
261 | /** | 257 | /** |
262 | * dma_async_client_register - allocate and register a &dma_client | 258 | * dma_chans_notify_available - tell the clients that a channel is going away |
263 | * @event_callback: callback for notification of channel addition/removal | 259 | * @chan: channel on its way out |
264 | */ | 260 | */ |
265 | struct dma_client *dma_async_client_register(dma_event_callback event_callback) | 261 | static void dma_clients_notify_removed(struct dma_chan *chan) |
266 | { | 262 | { |
267 | struct dma_client *client; | 263 | struct dma_client *client; |
264 | enum dma_state_client ack; | ||
268 | 265 | ||
269 | client = kzalloc(sizeof(*client), GFP_KERNEL); | 266 | mutex_lock(&dma_list_mutex); |
270 | if (!client) | 267 | |
271 | return NULL; | 268 | list_for_each_entry(client, &dma_client_list, global_node) { |
269 | ack = client->event_callback(client, chan, | ||
270 | DMA_RESOURCE_REMOVED); | ||
271 | |||
272 | /* client was holding resources for this channel so | ||
273 | * free it | ||
274 | */ | ||
275 | if (ack == DMA_ACK) { | ||
276 | dma_chan_put(chan); | ||
277 | kref_put(&chan->device->refcount, | ||
278 | dma_async_device_cleanup); | ||
279 | } | ||
280 | } | ||
272 | 281 | ||
273 | INIT_LIST_HEAD(&client->channels); | 282 | mutex_unlock(&dma_list_mutex); |
274 | spin_lock_init(&client->lock); | 283 | } |
275 | client->chans_desired = 0; | ||
276 | client->chan_count = 0; | ||
277 | client->event_callback = event_callback; | ||
278 | 284 | ||
285 | /** | ||
286 | * dma_async_client_register - register a &dma_client | ||
287 | * @client: ptr to a client structure with valid 'event_callback' and 'cap_mask' | ||
288 | */ | ||
289 | void dma_async_client_register(struct dma_client *client) | ||
290 | { | ||
279 | mutex_lock(&dma_list_mutex); | 291 | mutex_lock(&dma_list_mutex); |
280 | list_add_tail(&client->global_node, &dma_client_list); | 292 | list_add_tail(&client->global_node, &dma_client_list); |
281 | mutex_unlock(&dma_list_mutex); | 293 | mutex_unlock(&dma_list_mutex); |
282 | |||
283 | return client; | ||
284 | } | 294 | } |
285 | EXPORT_SYMBOL(dma_async_client_register); | 295 | EXPORT_SYMBOL(dma_async_client_register); |
286 | 296 | ||
@@ -292,40 +302,42 @@ EXPORT_SYMBOL(dma_async_client_register); | |||
292 | */ | 302 | */ |
293 | void dma_async_client_unregister(struct dma_client *client) | 303 | void dma_async_client_unregister(struct dma_client *client) |
294 | { | 304 | { |
305 | struct dma_device *device; | ||
295 | struct dma_chan *chan; | 306 | struct dma_chan *chan; |
307 | enum dma_state_client ack; | ||
296 | 308 | ||
297 | if (!client) | 309 | if (!client) |
298 | return; | 310 | return; |
299 | 311 | ||
300 | rcu_read_lock(); | ||
301 | list_for_each_entry_rcu(chan, &client->channels, client_node) | ||
302 | dma_client_chan_free(chan); | ||
303 | rcu_read_unlock(); | ||
304 | |||
305 | mutex_lock(&dma_list_mutex); | 312 | mutex_lock(&dma_list_mutex); |
313 | /* free all channels the client is holding */ | ||
314 | list_for_each_entry(device, &dma_device_list, global_node) | ||
315 | list_for_each_entry(chan, &device->channels, device_node) { | ||
316 | ack = client->event_callback(client, chan, | ||
317 | DMA_RESOURCE_REMOVED); | ||
318 | |||
319 | if (ack == DMA_ACK) { | ||
320 | dma_chan_put(chan); | ||
321 | kref_put(&chan->device->refcount, | ||
322 | dma_async_device_cleanup); | ||
323 | } | ||
324 | } | ||
325 | |||
306 | list_del(&client->global_node); | 326 | list_del(&client->global_node); |
307 | mutex_unlock(&dma_list_mutex); | 327 | mutex_unlock(&dma_list_mutex); |
308 | |||
309 | kfree(client); | ||
310 | dma_chans_rebalance(); | ||
311 | } | 328 | } |
312 | EXPORT_SYMBOL(dma_async_client_unregister); | 329 | EXPORT_SYMBOL(dma_async_client_unregister); |
313 | 330 | ||
314 | /** | 331 | /** |
315 | * dma_async_client_chan_request - request DMA channels | 332 | * dma_async_client_chan_request - send all available channels to the |
316 | * @client: &dma_client | 333 | * client that satisfy the capability mask |
317 | * @number: count of DMA channels requested | 334 | * @client - requester |
318 | * | ||
319 | * Clients call dma_async_client_chan_request() to specify how many | ||
320 | * DMA channels they need, 0 to free all currently allocated. | ||
321 | * The resulting allocations/frees are indicated to the client via the | ||
322 | * event callback. | ||
323 | */ | 335 | */ |
324 | void dma_async_client_chan_request(struct dma_client *client, | 336 | void dma_async_client_chan_request(struct dma_client *client) |
325 | unsigned int number) | ||
326 | { | 337 | { |
327 | client->chans_desired = number; | 338 | mutex_lock(&dma_list_mutex); |
328 | dma_chans_rebalance(); | 339 | dma_client_chan_alloc(client); |
340 | mutex_unlock(&dma_list_mutex); | ||
329 | } | 341 | } |
330 | EXPORT_SYMBOL(dma_async_client_chan_request); | 342 | EXPORT_SYMBOL(dma_async_client_chan_request); |
331 | 343 | ||
@@ -386,13 +398,16 @@ int dma_async_device_register(struct dma_device *device) | |||
386 | } | 398 | } |
387 | 399 | ||
388 | kref_get(&device->refcount); | 400 | kref_get(&device->refcount); |
401 | kref_init(&chan->refcount); | ||
402 | chan->slow_ref = 0; | ||
403 | INIT_RCU_HEAD(&chan->rcu); | ||
389 | } | 404 | } |
390 | 405 | ||
391 | mutex_lock(&dma_list_mutex); | 406 | mutex_lock(&dma_list_mutex); |
392 | list_add_tail(&device->global_node, &dma_device_list); | 407 | list_add_tail(&device->global_node, &dma_device_list); |
393 | mutex_unlock(&dma_list_mutex); | 408 | mutex_unlock(&dma_list_mutex); |
394 | 409 | ||
395 | dma_chans_rebalance(); | 410 | dma_clients_notify_available(); |
396 | 411 | ||
397 | return 0; | 412 | return 0; |
398 | 413 | ||
@@ -428,26 +443,16 @@ static void dma_async_device_cleanup(struct kref *kref) | |||
428 | void dma_async_device_unregister(struct dma_device *device) | 443 | void dma_async_device_unregister(struct dma_device *device) |
429 | { | 444 | { |
430 | struct dma_chan *chan; | 445 | struct dma_chan *chan; |
431 | unsigned long flags; | ||
432 | 446 | ||
433 | mutex_lock(&dma_list_mutex); | 447 | mutex_lock(&dma_list_mutex); |
434 | list_del(&device->global_node); | 448 | list_del(&device->global_node); |
435 | mutex_unlock(&dma_list_mutex); | 449 | mutex_unlock(&dma_list_mutex); |
436 | 450 | ||
437 | list_for_each_entry(chan, &device->channels, device_node) { | 451 | list_for_each_entry(chan, &device->channels, device_node) { |
438 | if (chan->client) { | 452 | dma_clients_notify_removed(chan); |
439 | spin_lock_irqsave(&chan->client->lock, flags); | ||
440 | list_del(&chan->client_node); | ||
441 | chan->client->chan_count--; | ||
442 | spin_unlock_irqrestore(&chan->client->lock, flags); | ||
443 | chan->client->event_callback(chan->client, | ||
444 | chan, | ||
445 | DMA_RESOURCE_REMOVED); | ||
446 | dma_client_chan_free(chan); | ||
447 | } | ||
448 | class_device_unregister(&chan->class_dev); | 453 | class_device_unregister(&chan->class_dev); |
454 | dma_chan_release(chan); | ||
449 | } | 455 | } |
450 | dma_chans_rebalance(); | ||
451 | 456 | ||
452 | kref_put(&device->refcount, dma_async_device_cleanup); | 457 | kref_put(&device->refcount, dma_async_device_cleanup); |
453 | wait_for_completion(&device->done); | 458 | wait_for_completion(&device->done); |
diff --git a/drivers/dma/ioatdma.c b/drivers/dma/ioatdma.c index 171044930282..81810b3042f1 100644 --- a/drivers/dma/ioatdma.c +++ b/drivers/dma/ioatdma.c | |||
@@ -72,7 +72,6 @@ static int enumerate_dma_channels(struct ioat_device *device) | |||
72 | INIT_LIST_HEAD(&ioat_chan->used_desc); | 72 | INIT_LIST_HEAD(&ioat_chan->used_desc); |
73 | /* This should be made common somewhere in dmaengine.c */ | 73 | /* This should be made common somewhere in dmaengine.c */ |
74 | ioat_chan->common.device = &device->common; | 74 | ioat_chan->common.device = &device->common; |
75 | ioat_chan->common.client = NULL; | ||
76 | list_add_tail(&ioat_chan->common.device_node, | 75 | list_add_tail(&ioat_chan->common.device_node, |
77 | &device->common.channels); | 76 | &device->common.channels); |
78 | } | 77 | } |
diff --git a/drivers/dma/ioatdma.h b/drivers/dma/ioatdma.h index d3f69bb15fa0..d3726478031a 100644 --- a/drivers/dma/ioatdma.h +++ b/drivers/dma/ioatdma.h | |||
@@ -30,9 +30,6 @@ | |||
30 | 30 | ||
31 | #define IOAT_LOW_COMPLETION_MASK 0xffffffc0 | 31 | #define IOAT_LOW_COMPLETION_MASK 0xffffffc0 |
32 | 32 | ||
33 | extern struct list_head dma_device_list; | ||
34 | extern struct list_head dma_client_list; | ||
35 | |||
36 | /** | 33 | /** |
37 | * struct ioat_device - internal representation of a IOAT device | 34 | * struct ioat_device - internal representation of a IOAT device |
38 | * @pdev: PCI-Express device | 35 | * @pdev: PCI-Express device |
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 3de1cf71031a..a3b6035b6c86 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h | |||
@@ -29,20 +29,32 @@ | |||
29 | #include <linux/dma-mapping.h> | 29 | #include <linux/dma-mapping.h> |
30 | 30 | ||
31 | /** | 31 | /** |
32 | * enum dma_event - resource PNP/power managment events | 32 | * enum dma_state - resource PNP/power managment state |
33 | * @DMA_RESOURCE_SUSPEND: DMA device going into low power state | 33 | * @DMA_RESOURCE_SUSPEND: DMA device going into low power state |
34 | * @DMA_RESOURCE_RESUME: DMA device returning to full power | 34 | * @DMA_RESOURCE_RESUME: DMA device returning to full power |
35 | * @DMA_RESOURCE_ADDED: DMA device added to the system | 35 | * @DMA_RESOURCE_AVAILABLE: DMA device available to the system |
36 | * @DMA_RESOURCE_REMOVED: DMA device removed from the system | 36 | * @DMA_RESOURCE_REMOVED: DMA device removed from the system |
37 | */ | 37 | */ |
38 | enum dma_event { | 38 | enum dma_state { |
39 | DMA_RESOURCE_SUSPEND, | 39 | DMA_RESOURCE_SUSPEND, |
40 | DMA_RESOURCE_RESUME, | 40 | DMA_RESOURCE_RESUME, |
41 | DMA_RESOURCE_ADDED, | 41 | DMA_RESOURCE_AVAILABLE, |
42 | DMA_RESOURCE_REMOVED, | 42 | DMA_RESOURCE_REMOVED, |
43 | }; | 43 | }; |
44 | 44 | ||
45 | /** | 45 | /** |
46 | * enum dma_state_client - state of the channel in the client | ||
47 | * @DMA_ACK: client would like to use, or was using this channel | ||
48 | * @DMA_DUP: client has already seen this channel, or is not using this channel | ||
49 | * @DMA_NAK: client does not want to see any more channels | ||
50 | */ | ||
51 | enum dma_state_client { | ||
52 | DMA_ACK, | ||
53 | DMA_DUP, | ||
54 | DMA_NAK, | ||
55 | }; | ||
56 | |||
57 | /** | ||
46 | * typedef dma_cookie_t - an opaque DMA cookie | 58 | * typedef dma_cookie_t - an opaque DMA cookie |
47 | * | 59 | * |
48 | * if dma_cookie_t is >0 it's a DMA request cookie, <0 it's an error code | 60 | * if dma_cookie_t is >0 it's a DMA request cookie, <0 it's an error code |
@@ -104,7 +116,6 @@ struct dma_chan_percpu { | |||
104 | 116 | ||
105 | /** | 117 | /** |
106 | * struct dma_chan - devices supply DMA channels, clients use them | 118 | * struct dma_chan - devices supply DMA channels, clients use them |
107 | * @client: ptr to the client user of this chan, will be %NULL when unused | ||
108 | * @device: ptr to the dma device who supplies this channel, always !%NULL | 119 | * @device: ptr to the dma device who supplies this channel, always !%NULL |
109 | * @cookie: last cookie value returned to client | 120 | * @cookie: last cookie value returned to client |
110 | * @chan_id: channel ID for sysfs | 121 | * @chan_id: channel ID for sysfs |
@@ -112,12 +123,10 @@ struct dma_chan_percpu { | |||
112 | * @refcount: kref, used in "bigref" slow-mode | 123 | * @refcount: kref, used in "bigref" slow-mode |
113 | * @slow_ref: indicates that the DMA channel is free | 124 | * @slow_ref: indicates that the DMA channel is free |
114 | * @rcu: the DMA channel's RCU head | 125 | * @rcu: the DMA channel's RCU head |
115 | * @client_node: used to add this to the client chan list | ||
116 | * @device_node: used to add this to the device chan list | 126 | * @device_node: used to add this to the device chan list |
117 | * @local: per-cpu pointer to a struct dma_chan_percpu | 127 | * @local: per-cpu pointer to a struct dma_chan_percpu |
118 | */ | 128 | */ |
119 | struct dma_chan { | 129 | struct dma_chan { |
120 | struct dma_client *client; | ||
121 | struct dma_device *device; | 130 | struct dma_device *device; |
122 | dma_cookie_t cookie; | 131 | dma_cookie_t cookie; |
123 | 132 | ||
@@ -129,11 +138,11 @@ struct dma_chan { | |||
129 | int slow_ref; | 138 | int slow_ref; |
130 | struct rcu_head rcu; | 139 | struct rcu_head rcu; |
131 | 140 | ||
132 | struct list_head client_node; | ||
133 | struct list_head device_node; | 141 | struct list_head device_node; |
134 | struct dma_chan_percpu *local; | 142 | struct dma_chan_percpu *local; |
135 | }; | 143 | }; |
136 | 144 | ||
145 | |||
137 | void dma_chan_cleanup(struct kref *kref); | 146 | void dma_chan_cleanup(struct kref *kref); |
138 | 147 | ||
139 | static inline void dma_chan_get(struct dma_chan *chan) | 148 | static inline void dma_chan_get(struct dma_chan *chan) |
@@ -158,26 +167,31 @@ static inline void dma_chan_put(struct dma_chan *chan) | |||
158 | 167 | ||
159 | /* | 168 | /* |
160 | * typedef dma_event_callback - function pointer to a DMA event callback | 169 | * typedef dma_event_callback - function pointer to a DMA event callback |
170 | * For each channel added to the system this routine is called for each client. | ||
171 | * If the client would like to use the channel it returns '1' to signal (ack) | ||
172 | * the dmaengine core to take out a reference on the channel and its | ||
173 | * corresponding device. A client must not 'ack' an available channel more | ||
174 | * than once. When a channel is removed all clients are notified. If a client | ||
175 | * is using the channel it must 'ack' the removal. A client must not 'ack' a | ||
176 | * removed channel more than once. | ||
177 | * @client - 'this' pointer for the client context | ||
178 | * @chan - channel to be acted upon | ||
179 | * @state - available or removed | ||
161 | */ | 180 | */ |
162 | typedef void (*dma_event_callback) (struct dma_client *client, | 181 | struct dma_client; |
163 | struct dma_chan *chan, enum dma_event event); | 182 | typedef enum dma_state_client (*dma_event_callback) (struct dma_client *client, |
183 | struct dma_chan *chan, enum dma_state state); | ||
164 | 184 | ||
165 | /** | 185 | /** |
166 | * struct dma_client - info on the entity making use of DMA services | 186 | * struct dma_client - info on the entity making use of DMA services |
167 | * @event_callback: func ptr to call when something happens | 187 | * @event_callback: func ptr to call when something happens |
168 | * @chan_count: number of chans allocated | 188 | * @cap_mask: only return channels that satisfy the requested capabilities |
169 | * @chans_desired: number of chans requested. Can be +/- chan_count | 189 | * a value of zero corresponds to any capability |
170 | * @lock: protects access to the channels list | ||
171 | * @channels: the list of DMA channels allocated | ||
172 | * @global_node: list_head for global dma_client_list | 190 | * @global_node: list_head for global dma_client_list |
173 | */ | 191 | */ |
174 | struct dma_client { | 192 | struct dma_client { |
175 | dma_event_callback event_callback; | 193 | dma_event_callback event_callback; |
176 | unsigned int chan_count; | 194 | dma_cap_mask_t cap_mask; |
177 | unsigned int chans_desired; | ||
178 | |||
179 | spinlock_t lock; | ||
180 | struct list_head channels; | ||
181 | struct list_head global_node; | 195 | struct list_head global_node; |
182 | }; | 196 | }; |
183 | 197 | ||
@@ -285,10 +299,9 @@ struct dma_device { | |||
285 | 299 | ||
286 | /* --- public DMA engine API --- */ | 300 | /* --- public DMA engine API --- */ |
287 | 301 | ||
288 | struct dma_client *dma_async_client_register(dma_event_callback event_callback); | 302 | void dma_async_client_register(struct dma_client *client); |
289 | void dma_async_client_unregister(struct dma_client *client); | 303 | void dma_async_client_unregister(struct dma_client *client); |
290 | void dma_async_client_chan_request(struct dma_client *client, | 304 | void dma_async_client_chan_request(struct dma_client *client); |
291 | unsigned int number); | ||
292 | dma_cookie_t dma_async_memcpy_buf_to_buf(struct dma_chan *chan, | 305 | dma_cookie_t dma_async_memcpy_buf_to_buf(struct dma_chan *chan, |
293 | void *dest, void *src, size_t len); | 306 | void *dest, void *src, size_t len); |
294 | dma_cookie_t dma_async_memcpy_buf_to_pg(struct dma_chan *chan, | 307 | dma_cookie_t dma_async_memcpy_buf_to_pg(struct dma_chan *chan, |
@@ -299,7 +312,6 @@ dma_cookie_t dma_async_memcpy_pg_to_pg(struct dma_chan *chan, | |||
299 | void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx, | 312 | void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx, |
300 | struct dma_chan *chan); | 313 | struct dma_chan *chan); |
301 | 314 | ||
302 | |||
303 | static inline void | 315 | static inline void |
304 | async_tx_ack(struct dma_async_tx_descriptor *tx) | 316 | async_tx_ack(struct dma_async_tx_descriptor *tx) |
305 | { | 317 | { |
diff --git a/net/core/dev.c b/net/core/dev.c index ee051bb398a0..835202fb34c4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -151,9 +151,22 @@ static struct list_head ptype_base[16] __read_mostly; /* 16 way hashed list */ | |||
151 | static struct list_head ptype_all __read_mostly; /* Taps */ | 151 | static struct list_head ptype_all __read_mostly; /* Taps */ |
152 | 152 | ||
153 | #ifdef CONFIG_NET_DMA | 153 | #ifdef CONFIG_NET_DMA |
154 | static struct dma_client *net_dma_client; | 154 | struct net_dma { |
155 | static unsigned int net_dma_count; | 155 | struct dma_client client; |
156 | static spinlock_t net_dma_event_lock; | 156 | spinlock_t lock; |
157 | cpumask_t channel_mask; | ||
158 | struct dma_chan *channels[NR_CPUS]; | ||
159 | }; | ||
160 | |||
161 | static enum dma_state_client | ||
162 | netdev_dma_event(struct dma_client *client, struct dma_chan *chan, | ||
163 | enum dma_state state); | ||
164 | |||
165 | static struct net_dma net_dma = { | ||
166 | .client = { | ||
167 | .event_callback = netdev_dma_event, | ||
168 | }, | ||
169 | }; | ||
157 | #endif | 170 | #endif |
158 | 171 | ||
159 | /* | 172 | /* |
@@ -2015,12 +2028,13 @@ out: | |||
2015 | * There may not be any more sk_buffs coming right now, so push | 2028 | * There may not be any more sk_buffs coming right now, so push |
2016 | * any pending DMA copies to hardware | 2029 | * any pending DMA copies to hardware |
2017 | */ | 2030 | */ |
2018 | if (net_dma_client) { | 2031 | if (!cpus_empty(net_dma.channel_mask)) { |
2019 | struct dma_chan *chan; | 2032 | int chan_idx; |
2020 | rcu_read_lock(); | 2033 | for_each_cpu_mask(chan_idx, net_dma.channel_mask) { |
2021 | list_for_each_entry_rcu(chan, &net_dma_client->channels, client_node) | 2034 | struct dma_chan *chan = net_dma.channels[chan_idx]; |
2022 | dma_async_memcpy_issue_pending(chan); | 2035 | if (chan) |
2023 | rcu_read_unlock(); | 2036 | dma_async_memcpy_issue_pending(chan); |
2037 | } | ||
2024 | } | 2038 | } |
2025 | #endif | 2039 | #endif |
2026 | return; | 2040 | return; |
@@ -3563,12 +3577,13 @@ static int dev_cpu_callback(struct notifier_block *nfb, | |||
3563 | * This is called when the number of channels allocated to the net_dma_client | 3577 | * This is called when the number of channels allocated to the net_dma_client |
3564 | * changes. The net_dma_client tries to have one DMA channel per CPU. | 3578 | * changes. The net_dma_client tries to have one DMA channel per CPU. |
3565 | */ | 3579 | */ |
3566 | static void net_dma_rebalance(void) | 3580 | |
3581 | static void net_dma_rebalance(struct net_dma *net_dma) | ||
3567 | { | 3582 | { |
3568 | unsigned int cpu, i, n; | 3583 | unsigned int cpu, i, n, chan_idx; |
3569 | struct dma_chan *chan; | 3584 | struct dma_chan *chan; |
3570 | 3585 | ||
3571 | if (net_dma_count == 0) { | 3586 | if (cpus_empty(net_dma->channel_mask)) { |
3572 | for_each_online_cpu(cpu) | 3587 | for_each_online_cpu(cpu) |
3573 | rcu_assign_pointer(per_cpu(softnet_data, cpu).net_dma, NULL); | 3588 | rcu_assign_pointer(per_cpu(softnet_data, cpu).net_dma, NULL); |
3574 | return; | 3589 | return; |
@@ -3577,10 +3592,12 @@ static void net_dma_rebalance(void) | |||
3577 | i = 0; | 3592 | i = 0; |
3578 | cpu = first_cpu(cpu_online_map); | 3593 | cpu = first_cpu(cpu_online_map); |
3579 | 3594 | ||
3580 | rcu_read_lock(); | 3595 | for_each_cpu_mask(chan_idx, net_dma->channel_mask) { |
3581 | list_for_each_entry(chan, &net_dma_client->channels, client_node) { | 3596 | chan = net_dma->channels[chan_idx]; |
3582 | n = ((num_online_cpus() / net_dma_count) | 3597 | |
3583 | + (i < (num_online_cpus() % net_dma_count) ? 1 : 0)); | 3598 | n = ((num_online_cpus() / cpus_weight(net_dma->channel_mask)) |
3599 | + (i < (num_online_cpus() % | ||
3600 | cpus_weight(net_dma->channel_mask)) ? 1 : 0)); | ||
3584 | 3601 | ||
3585 | while(n) { | 3602 | while(n) { |
3586 | per_cpu(softnet_data, cpu).net_dma = chan; | 3603 | per_cpu(softnet_data, cpu).net_dma = chan; |
@@ -3589,7 +3606,6 @@ static void net_dma_rebalance(void) | |||
3589 | } | 3606 | } |
3590 | i++; | 3607 | i++; |
3591 | } | 3608 | } |
3592 | rcu_read_unlock(); | ||
3593 | } | 3609 | } |
3594 | 3610 | ||
3595 | /** | 3611 | /** |
@@ -3598,23 +3614,53 @@ static void net_dma_rebalance(void) | |||
3598 | * @chan: DMA channel for the event | 3614 | * @chan: DMA channel for the event |
3599 | * @event: event type | 3615 | * @event: event type |
3600 | */ | 3616 | */ |
3601 | static void netdev_dma_event(struct dma_client *client, struct dma_chan *chan, | 3617 | static enum dma_state_client |
3602 | enum dma_event event) | 3618 | netdev_dma_event(struct dma_client *client, struct dma_chan *chan, |
3603 | { | 3619 | enum dma_state state) |
3604 | spin_lock(&net_dma_event_lock); | 3620 | { |
3605 | switch (event) { | 3621 | int i, found = 0, pos = -1; |
3606 | case DMA_RESOURCE_ADDED: | 3622 | struct net_dma *net_dma = |
3607 | net_dma_count++; | 3623 | container_of(client, struct net_dma, client); |
3608 | net_dma_rebalance(); | 3624 | enum dma_state_client ack = DMA_DUP; /* default: take no action */ |
3625 | |||
3626 | spin_lock(&net_dma->lock); | ||
3627 | switch (state) { | ||
3628 | case DMA_RESOURCE_AVAILABLE: | ||
3629 | for (i = 0; i < NR_CPUS; i++) | ||
3630 | if (net_dma->channels[i] == chan) { | ||
3631 | found = 1; | ||
3632 | break; | ||
3633 | } else if (net_dma->channels[i] == NULL && pos < 0) | ||
3634 | pos = i; | ||
3635 | |||
3636 | if (!found && pos >= 0) { | ||
3637 | ack = DMA_ACK; | ||
3638 | net_dma->channels[pos] = chan; | ||
3639 | cpu_set(pos, net_dma->channel_mask); | ||
3640 | net_dma_rebalance(net_dma); | ||
3641 | } | ||
3609 | break; | 3642 | break; |
3610 | case DMA_RESOURCE_REMOVED: | 3643 | case DMA_RESOURCE_REMOVED: |
3611 | net_dma_count--; | 3644 | for (i = 0; i < NR_CPUS; i++) |
3612 | net_dma_rebalance(); | 3645 | if (net_dma->channels[i] == chan) { |
3646 | found = 1; | ||
3647 | pos = i; | ||
3648 | break; | ||
3649 | } | ||
3650 | |||
3651 | if (found) { | ||
3652 | ack = DMA_ACK; | ||
3653 | cpu_clear(pos, net_dma->channel_mask); | ||
3654 | net_dma->channels[i] = NULL; | ||
3655 | net_dma_rebalance(net_dma); | ||
3656 | } | ||
3613 | break; | 3657 | break; |
3614 | default: | 3658 | default: |
3615 | break; | 3659 | break; |
3616 | } | 3660 | } |
3617 | spin_unlock(&net_dma_event_lock); | 3661 | spin_unlock(&net_dma->lock); |
3662 | |||
3663 | return ack; | ||
3618 | } | 3664 | } |
3619 | 3665 | ||
3620 | /** | 3666 | /** |
@@ -3622,12 +3668,10 @@ static void netdev_dma_event(struct dma_client *client, struct dma_chan *chan, | |||
3622 | */ | 3668 | */ |
3623 | static int __init netdev_dma_register(void) | 3669 | static int __init netdev_dma_register(void) |
3624 | { | 3670 | { |
3625 | spin_lock_init(&net_dma_event_lock); | 3671 | spin_lock_init(&net_dma.lock); |
3626 | net_dma_client = dma_async_client_register(netdev_dma_event); | 3672 | dma_cap_set(DMA_MEMCPY, net_dma.client.cap_mask); |
3627 | if (net_dma_client == NULL) | 3673 | dma_async_client_register(&net_dma.client); |
3628 | return -ENOMEM; | 3674 | dma_async_client_chan_request(&net_dma.client); |
3629 | |||
3630 | dma_async_client_chan_request(net_dma_client, num_online_cpus()); | ||
3631 | return 0; | 3675 | return 0; |
3632 | } | 3676 | } |
3633 | 3677 | ||