aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma/dmaengine.c
diff options
context:
space:
mode:
authorBrice Goglin <Brice.Goglin@inria.fr>2013-08-19 05:43:35 -0400
committerDan Williams <djbw@fb.com>2013-08-23 01:57:51 -0400
commitc4d27c4d024f5440497106bb2ae15e9e60f7099c (patch)
treeca3d2f41a9399ea48636ce95128742cf6b1cd3fd /drivers/dma/dmaengine.c
parent71ea148370f8b6c745a8a42f6fd983cf5ebade18 (diff)
dmaengine: make dma_channel_rebalance() NUMA aware
dma_channel_rebalance() currently distributes channels by processor ID. These IDs often change with the BIOS, and the order isn't related to the DMA channel list (related to PCI bus ids). * On my SuperMicro dual E5 machine, first socket has processor IDs [0-7] (and [16-23] for hyperthreads), second socket has [8-15]+[24-31] => channels are properly allocated to local CPUs. * On Dells R720 with same processors, first socket has even processor IDs, second socket has odd numbers => half the processors get channels on the remote socket, causing cross-NUMA traffic and lower DMA performance. Change nth_chan() to return the channel with min table_count and in the NUMA node of the given CPU, if any. If none, the (non-local) channel with min table_count is returned. nth_chan() is therefore renamed into min_chan() since we don't iterate until the nth channel anymore. In practice, the behavior is the same because first channels are taken first and are then ignored because they got an additional reference. The new code has a slightly higher complexity since we always scan the entire list of channels for finding the minimal table_count (instead of stopping after N chans), and because we check whether the CPU is in the DMA device locality mask. Overall we still have time complexity = number of chans x number of processors. This rebalance is rarely used, so this won't hurt. On the above SuperMicro machine, channels are still allocated the same. On the Dells, there are no locality issue anymore (MEMCPY channel X goes to processor X and to its hyperthread sibling). Signed-off-by: Brice Goglin <Brice.Goglin@inria.fr> Signed-off-by: Dan Williams <djbw@fb.com>
Diffstat (limited to 'drivers/dma/dmaengine.c')
-rw-r--r--drivers/dma/dmaengine.c55
1 files changed, 27 insertions, 28 deletions
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 9e56745f87bf..e428cf2a458b 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -376,20 +376,30 @@ void dma_issue_pending_all(void)
376EXPORT_SYMBOL(dma_issue_pending_all); 376EXPORT_SYMBOL(dma_issue_pending_all);
377 377
378/** 378/**
379 * nth_chan - returns the nth channel of the given capability 379 * dma_chan_is_local - returns true if the channel is in the same numa-node as the cpu
380 */
381static bool dma_chan_is_local(struct dma_chan *chan, int cpu)
382{
383 int node = dev_to_node(chan->device->dev);
384 return node == -1 || cpumask_test_cpu(cpu, cpumask_of_node(node));
385}
386
387/**
388 * min_chan - returns the channel with min count and in the same numa-node as the cpu
380 * @cap: capability to match 389 * @cap: capability to match
381 * @n: nth channel desired 390 * @cpu: cpu index which the channel should be close to
382 * 391 *
383 * Defaults to returning the channel with the desired capability and the 392 * If some channels are close to the given cpu, the one with the lowest
384 * lowest reference count when 'n' cannot be satisfied. Must be called 393 * reference count is returned. Otherwise, cpu is ignored and only the
385 * under dma_list_mutex. 394 * reference count is taken into account.
395 * Must be called under dma_list_mutex.
386 */ 396 */
387static struct dma_chan *nth_chan(enum dma_transaction_type cap, int n) 397static struct dma_chan *min_chan(enum dma_transaction_type cap, int cpu)
388{ 398{
389 struct dma_device *device; 399 struct dma_device *device;
390 struct dma_chan *chan; 400 struct dma_chan *chan;
391 struct dma_chan *ret = NULL;
392 struct dma_chan *min = NULL; 401 struct dma_chan *min = NULL;
402 struct dma_chan *localmin = NULL;
393 403
394 list_for_each_entry(device, &dma_device_list, global_node) { 404 list_for_each_entry(device, &dma_device_list, global_node) {
395 if (!dma_has_cap(cap, device->cap_mask) || 405 if (!dma_has_cap(cap, device->cap_mask) ||
@@ -398,27 +408,22 @@ static struct dma_chan *nth_chan(enum dma_transaction_type cap, int n)
398 list_for_each_entry(chan, &device->channels, device_node) { 408 list_for_each_entry(chan, &device->channels, device_node) {
399 if (!chan->client_count) 409 if (!chan->client_count)
400 continue; 410 continue;
401 if (!min) 411 if (!min || chan->table_count < min->table_count)
402 min = chan;
403 else if (chan->table_count < min->table_count)
404 min = chan; 412 min = chan;
405 413
406 if (n-- == 0) { 414 if (dma_chan_is_local(chan, cpu))
407 ret = chan; 415 if (!localmin ||
408 break; /* done */ 416 chan->table_count < localmin->table_count)
409 } 417 localmin = chan;
410 } 418 }
411 if (ret)
412 break; /* done */
413 } 419 }
414 420
415 if (!ret) 421 chan = localmin ? localmin : min;
416 ret = min;
417 422
418 if (ret) 423 if (chan)
419 ret->table_count++; 424 chan->table_count++;
420 425
421 return ret; 426 return chan;
422} 427}
423 428
424/** 429/**
@@ -435,7 +440,6 @@ static void dma_channel_rebalance(void)
435 struct dma_device *device; 440 struct dma_device *device;
436 int cpu; 441 int cpu;
437 int cap; 442 int cap;
438 int n;
439 443
440 /* undo the last distribution */ 444 /* undo the last distribution */
441 for_each_dma_cap_mask(cap, dma_cap_mask_all) 445 for_each_dma_cap_mask(cap, dma_cap_mask_all)
@@ -454,14 +458,9 @@ static void dma_channel_rebalance(void)
454 return; 458 return;
455 459
456 /* redistribute available channels */ 460 /* redistribute available channels */
457 n = 0;
458 for_each_dma_cap_mask(cap, dma_cap_mask_all) 461 for_each_dma_cap_mask(cap, dma_cap_mask_all)
459 for_each_online_cpu(cpu) { 462 for_each_online_cpu(cpu) {
460 if (num_possible_cpus() > 1) 463 chan = min_chan(cap, cpu);
461 chan = nth_chan(cap, n++);
462 else
463 chan = nth_chan(cap, -1);
464
465 per_cpu_ptr(channel_table[cap], cpu)->chan = chan; 464 per_cpu_ptr(channel_table[cap], cpu)->chan = chan;
466 } 465 }
467} 466}