PCI设备:
coherent_dma_mask :用来分配连续一致性dma。
dma_mask:在dma_map_single->dma_map_page,dma_capable用。
先看默认设置。后续可以通过pci_set_dma_mask/pci_set_consistent_dma_mask分别设置
设置:
int pci_setup_device(struct pci_dev *dev)
{
u32 class;
u16 cmd;
u8 hdr_type;
int pos = 0;
struct pci_bus_region region;
struct resource *res;
hdr_type = pci_hdr_type(dev);
dev->sysdata = dev->bus->sysdata;
dev->dev.parent = dev->bus->bridge;
dev->dev.bus = &pci_bus_type;
dev->hdr_type = hdr_type & 0x7f;
dev->multifunction = !!(hdr_type & 0x80);
dev->error_state = pci_channel_io_normal;
set_pcie_port_type(dev);
pci_dev_assign_slot(dev);
/*
* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer)
* set this higher, assuming the system even supports it.
*/
dev->dma_mask = 0xffffffff; //默认设置4G
....
}
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
{
int ret;
pci_configure_device(dev);
device_initialize(&dev->dev);
dev->dev.release = pci_release_dev;
set_dev_node(&dev->dev, pcibus_to_node(bus));
dev->dev.dma_mask = &dev->dma_mask;
dev->dev.dma_parms = &dev->dma_parms;
dev->dev.coherent_dma_mask = 0xffffffffull; //默认设置4G
......
}
platform设备:acpi创建platform_device_info 数据,创建platform_device
drivers/acpi/acpi_platform.c:
struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
struct property_entry *properties)
{
struct platform_device *pdev = NULL;
struct platform_device_info pdevinfo;
struct resource_entry *rentry;
struct list_head resource_list;
struct resource *resources = NULL;
int count;
/* If the ACPI node already has a physical device attached, skip it. */
if (adev->physical_node_count)
return NULL;
if (!acpi_match_device_ids(adev, forbidden_id_list))
return ERR_PTR(-EINVAL);
INIT_LIST_HEAD(&resource_list);
count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
if (count < 0) {
return NULL;
} else if (count > 0) {
resources = kcalloc(count, sizeof(struct resource),
GFP_KERNEL);
if (!resources) {
dev_err(&adev->dev, "No memory for resources\n");
acpi_dev_free_resource_list(&resource_list);
return ERR_PTR(-ENOMEM);
}
count = 0;
list_for_each_entry(rentry, &resource_list, node)
acpi_platform_fill_resource(adev, rentry->res,
&resources[count++]);
acpi_dev_free_resource_list(&resource_list);
}
memset(&pdevinfo, 0, sizeof(pdevinfo));
/*
* If the ACPI node has a parent and that parent has a physical device
* attached to it, that physical device should be the parent of the
* platform device we are about to create.
*/
pdevinfo.parent = adev->parent ?
acpi_get_first_physical_node(adev->parent) : NULL;
pdevinfo.name = dev_name(&adev->dev);
pdevinfo.id = -1;
pdevinfo.res = resources;
pdevinfo.num_res = count;
pdevinfo.fwnode = acpi_fwnode_handle(adev);
pdevinfo.properties = properties;
if (acpi_dma_supported(adev))
pdevinfo.dma_mask = DMA_BIT_MASK(32); //dma_mask设置
else
pdevinfo.dma_mask = 0;
pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev))
dev_err(&adev->dev, "platform device creation failed: %ld\n",
PTR_ERR(pdev));
else {
set_dev_node(&pdev->dev, acpi_get_node(adev->handle));
dev_dbg(&adev->dev, "created platform device %s\n",
dev_name(&pdev->dev));
}
kfree(resources);
return pdev;
}
EXPORT_SYMBOL_GPL(acpi_create_platform_device);
acpi_dma_supported:判断acpi设备是否支持dma
drivers/acpi/scan.c:
bool acpi_dma_supported(struct acpi_device *adev)
{
if (!adev)
return false;
if (adev->flags.cca_seen)
return true;
/*
* Per ACPI 6.0 sec 6.2.17, assume devices can do cache-coherent
* DMA on "Intel platforms". Presumably that includes all x86 and
* ia64, and other arches will set CONFIG_ACPI_CCA_REQUIRED=y.
*/
if (!IS_ENABLED(CONFIG_ACPI_CCA_REQUIRED))
return true;
return false;
}
设置
cca_seen:
static void acpi_init_coherency(struct acpi_device *adev)
{
unsigned long long cca = 0;
acpi_status status;
struct acpi_device *parent = adev->parent;
if (parent && parent->flags.cca_seen) {
/*
* From ACPI spec, OSPM will ignore _CCA if an ancestor
* already saw one.
*/
adev->flags.cca_seen = 1;
cca = parent->flags.coherent_dma;
} else {
status = acpi_evaluate_integer(adev->handle, "_CCA", //ACPI表设备属性_CCA
NULL, &cca);
if (ACPI_SUCCESS(status))
adev->flags.cca_seen = 1;
else if (!IS_ENABLED(CONFIG_ACPI_CCA_REQUIRED))
/*
* If architecture does not specify that _CCA is
* required for DMA-able devices (e.g. x86),
* we default to _CCA=1.
*/
cca = 1;
else
acpi_handle_debug(adev->handle,
"ACPI device is missing _CCA.\n");
}
adev->flags.coherent_dma = cca;
}
比如飞腾GMAC网卡:
DSDT表:
Device (ETH1)
{
Name (_HID, "PHYT0004") // _HID: Hardware ID
Name (_CID, "FTGM0001") // _CID: Compatible ID
Name (_UID, One) // _UID: Unique ID
Name (_CCA, One) // _CCA: Cache Coherency Attribute //一致性内存
Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings
{
Memory32Fixed (ReadWrite,
0x28210000, // Address Base
0x00002000, // Address Length
)
Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, )
{
0x00000052,
}
})
Name (_DSD, Package (0x02) // _DSD: Device-Specific Data
{
ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
Package (0x0F)
{
Package (0x02)
{
"interrupt-names",
"macirq"
},
.....
platform_dma_configure会通过acpi_get_dma_attr获取dev_dma_attr类型并调用初始化函数acpi_dma_configure
int platform_dma_configure(struct device *dev)
{
enum dev_dma_attr attr;
int ret = 0;
if (dev->of_node) {
ret = of_dma_configure(dev, dev->of_node, true);
} else if (has_acpi_companion(dev)) {
attr = acpi_get_dma_attr(to_acpi_device_node(dev->fwnode));
ret = acpi_dma_configure(dev, attr);
}
return ret;
}
enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
{
if (!acpi_dma_supported(adev))
return DEV_DMA_NOT_SUPPORTED;
if (adev->flags.coherent_dma)
return DEV_DMA_COHERENT;
else
return DEV_DMA_NON_COHERENT;
}
drivers/base/platform.c:
struct platform_device *platform_device_register_full(
const struct platform_device_info *pdevinfo)
{
int ret = -ENOMEM;
struct platform_device *pdev;
pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id);
if (!pdev)
goto err_alloc;
pdev->dev.parent = pdevinfo->parent;
pdev->dev.fwnode = pdevinfo->fwnode;
//设置dma_mask
if (pdevinfo->dma_mask) {
/*
* This memory isn't freed when the device is put,
* I don't have a nice idea for that though. Conceptually
* dma_mask in struct device should not be a pointer.
* See http://thread.gmane.org/gmane.linux.kernel.pci/9081
*/
pdev->dev.dma_mask =
kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL);
if (!pdev->dev.dma_mask)
goto err;
kmemleak_ignore(pdev->dev.dma_mask);
*pdev->dev.dma_mask = pdevinfo->dma_mask;
pdev->dev.coherent_dma_mask = pdevinfo->dma_mask;
}
ret = platform_device_add_resources(pdev,
pdevinfo->res, pdevinfo->num_res);
if (ret)
goto err;
ret = platform_device_add_data(pdev,
pdevinfo->data, pdevinfo->size_data);
if (ret)
goto err;
if (pdevinfo->properties) {
ret = platform_device_add_properties(pdev,
pdevinfo->properties);
if (ret)
goto err;
}
ret = platform_device_add(pdev);
if (ret) {
err:
ACPI_COMPANION_SET(&pdev->dev, NULL);
kfree(pdev->dev.dma_mask);
err_alloc:
platform_device_put(pdev);
return ERR_PTR(ret);
}
return pdev;
}
使用:
dma_alloc_attrs->swiotlb_alloc->dma_direct_alloc:
void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t gfp, unsigned long attrs)
{
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
int page_order = get_order(size);
struct page *page = NULL;
void *ret;
/* we always manually zero the memory once we are done: */
gfp &= ~__GFP_ZERO;
/* GFP_DMA32 and GFP_DMA are no ops without the corresponding zones: */
if (dev->coherent_dma_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
gfp |= GFP_DMA;
if (dev->coherent_dma_mask <= DMA_BIT_MASK(32) && !(gfp & GFP_DMA))
gfp |= GFP_DMA32;
.....
}
pci设置驱动会设置实际的dma_mask:
[ 8.785389] ===dma_set_mask 0000:05:00.0 mask ffffffffffffffff
[ 8.791302] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.10.0-with-dc-g1c56f5c219bc-dirty #46
[ 8.799725] Hardware name: PHYTIUM LTD D2000/D2000, BIOS
[ 8.805197] Call trace:
[ 8.807637] dump_backtrace+0x0/0x1b4
[ 8.811288] show_stack+0x24/0x70
[ 8.814592] dump_stack+0xd0/0x12c
[ 8.817982] dma_set_mask+0x44/0x80
[ 8.821459] ahci_init_one+0x780/0xd10
[ 8.825197] local_pci_probe+0x4c/0xc0
[ 8.828934] pci_device_probe+0x120/0x1c0
[ 8.832932] really_probe+0xec/0x494
[ 8.836495] driver_probe_device+0x64/0xcc
[ 8.840578] device_driver_attach+0xcc/0xd4
[ 8.844748] __driver_attach+0x90/0x130
[ 8.848571] bus_for_each_dev+0x7c/0xe0
[ 8.852395] driver_attach+0x30/0x40
[ 8.855957] bus_add_driver+0x114/0x200
[ 8.859780] driver_register+0x84/0x140
[ 8.863603] __pci_register_driver+0x50/0x5c
[ 8.867861] ahci_pci_driver_init+0x30/0x3c
[ 8.872032] do_one_initcall+0x50/0x260
[ 8.875855] kernel_init_freeable+0x22c/0x2c0
[ 8.880201] kernel_init+0x20/0x124
[ 8.883676] ret_from_fork+0x10/0x30
[ 8.887243] ===dma_set_coherent_mask 0000:05:00.0 mask ffffffffffffffff
[ 8.893933] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.10.0-with-dc-g1c56f5c219bc-dirty #46
[ 8.902355] Hardware name: PHYTIUM LTD D2000/D2000, BIOS
[ 8.907826] Call trace:
[ 8.910260] dump_backtrace+0x0/0x1b4
[ 8.913910] show_stack+0x24/0x70
[ 8.917212] dump_stack+0xd0/0x12c
[ 8.920602] dma_set_coherent_mask+0x44/0x74
[ 8.924859] ahci_init_one+0x968/0xd10
[ 8.928595] local_pci_probe+0x4c/0xc0
[ 8.932331] pci_device_probe+0x120/0x1c0
[ 8.936327] really_probe+0xec/0x494
[ 8.939890] driver_probe_device+0x64/0xcc