在smmu使能之前系统中的device都是用的swiotlb。那smmu使能之后设备如何切换到smmu呢 这个时候会分两种情况
第一种是如果设备在smmu使能之前已经开始使用dma了,那smmu只能时就要通知设备从swiotlb切换到smmu。
static int __init __iommu_dma_init(void)
{
int ret;
ret = iommu_dma_init();
if (!ret)
ret = register_iommu_dma_ops_notifier(&platform_bus_type);
if (!ret)
ret = register_iommu_dma_ops_notifier(&amba_bustype);
#ifdef CONFIG_PCI
if (!ret)
ret = register_iommu_dma_ops_notifier(&pci_bus_type);
#endif
return ret;
}
arch_initcall(__iommu_dma_init);
可见也是只管三个总线的设备platform_bus_type/amba_bustype/pci_bus_type
其中iommu_dma_init 只是通过kmem_cache_create申请一个cache而已
static int __init register_iommu_dma_ops_notifier(struct bus_type *bus)
{
struct notifier_block *nb = kzalloc(sizeof(*nb), GFP_KERNEL);
int ret;
if (!nb)
return -ENOMEM;
nb->notifier_call = __iommu_attach_notifier;
ret = bus_register_notifier(bus, nb);
if (ret) {
pr_warn("Failed to register DMA domain notifier; IOMMU DMA ops unavailable on bus '%s'\n",
bus->name);
kfree(nb);
}
return ret;
}
register_iommu_dma_ops_notifier 中申请一个notifier_block *nb,并将callback函数设定为__iommu_attach_notifier,然后通过bus_register_notifier 来遍历platform_bus_type/amba_bustype/pci_bus_type上的每个设备,分别调用__iommu_attach_notifier。
int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
{
return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
}
从bus_type->p->bus_notifier 可以得到blocking_notifier_head *nh,也就是每个bus 中的所有的notifier 是组成一个链表的。
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
struct notifier_block *n)
{
int ret;
/*
* This code gets used during boot-up, when task switching is
* not yet working and interrupts must remain disabled. At
* such times we must not call down_write().
*/
if (unlikely(system_state == SYSTEM_BOOTING))
return notifier_chain_register(&nh->head, n);
down_write(&nh->rwsem);
ret = notifier_chain_register(&nh->head, n);
up_write(&nh->rwsem);
return ret;
}
在blocking_notifier_chain_register中要判断一下当前系统的状态是不是SYSTEM_BOOTING,一般这种情况不满足,然后调用notifier_chain_register,从bus->p->bus_notifier 中得到notifier header的nh->head
static int notifier_chain_register(struct notifier_block **nl,
struct notifier_block *n)
{
while ((*nl) != NULL) {
if (n->priority > (*nl)->priority)
break;
nl = &((*nl)->next);
}
n->next = *nl;
rcu_assign_pointer(*nl, n);
return 0;
}
最终还在while循环中判断当前待调用notifier的优先级是否最高,不是的话,遍历链表将当前notifier 插入list中,最后通过rcu_assign_pointer 修改指针.
第二种情况是smmu已经使能后,设备如何切换到smmu上
这种情况主要在arm_smmu_device_probe 中处理
#ifdef CONFIG_PCI
if (pci_bus_type.iommu_ops != &arm_smmu_ops) {
pci_request_acs();
ret = bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
if (ret)
return ret;
}
#endif
#ifdef CONFIG_ARM_AMBA
if (amba_bustype.iommu_ops != &arm_smmu_ops) {
ret = bus_set_iommu(&amba_bustype, &arm_smmu_ops);
if (ret)
return ret;
}
#endif
if (platform_bus_type.iommu_ops != &arm_smmu_ops) {
ret = bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
if (ret)
return ret;
}
同样是这三个总线
int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops)
{
int err;
if (bus->iommu_ops != NULL)
return -EBUSY;
bus->iommu_ops = ops;
/* Do IOMMU specific setup for this bus-type */
err = iommu_bus_init(bus, ops);
if (err)
bus->iommu_ops = NULL;
return err;
}
如果bus->iommu_ops 不是null,就说明已经调用过这个函数了,不用重复调用,一般情况下系统有多个smmmu arm_smmu_device_probe 会被调用多次,但是bus_set_iommu 只能被调用一次.
static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops)
{
int err;
struct notifier_block *nb;
struct iommu_callback_data cb = {
.ops = ops,
};
nb = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
if (!nb)
return -ENOMEM;
nb->notifier_call = iommu_bus_notifier;
err = bus_register_notifier(bus, nb);
if (err)
goto out_free;
err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group);
if (err)
goto out_err;
return 0;
out_err:
/* Clean up */
bus_for_each_dev(bus, NULL, &cb, remove_iommu_group);
bus_unregister_notifier(bus, nb);
out_free:
kfree(nb);
return err;
}
这个函数主要做了两件事
第一个是在smmu使能之后的设备使用smmu注册notifier bus_register_notifier
第二个是为当前已有的设备调用add_iommu_group
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus || !bus->p)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
而add_iommu_group->add_device(arm_smmu_add_device)->device_group->iommu_group_add_device 等整个flow.
这样当device 初始化的时候会调用iommu_bus_notifier。从iommu_bus_notifier 中也可以看出一个device要使用smmu必须先add_device,然后得到group。而且每个group 都包含struct blocking_notifier_head *nh
第一种是如果设备在smmu使能之前已经开始使用dma了,那smmu只能时就要通知设备从swiotlb切换到smmu。
static int __init __iommu_dma_init(void)
{
int ret;
ret = iommu_dma_init();
if (!ret)
ret = register_iommu_dma_ops_notifier(&platform_bus_type);
if (!ret)
ret = register_iommu_dma_ops_notifier(&amba_bustype);
#ifdef CONFIG_PCI
if (!ret)
ret = register_iommu_dma_ops_notifier(&pci_bus_type);
#endif
return ret;
}
arch_initcall(__iommu_dma_init);
可见也是只管三个总线的设备platform_bus_type/amba_bustype/pci_bus_type
其中iommu_dma_init 只是通过kmem_cache_create申请一个cache而已
static int __init register_iommu_dma_ops_notifier(struct bus_type *bus)
{
struct notifier_block *nb = kzalloc(sizeof(*nb), GFP_KERNEL);
int ret;
if (!nb)
return -ENOMEM;
nb->notifier_call = __iommu_attach_notifier;
ret = bus_register_notifier(bus, nb);
if (ret) {
pr_warn("Failed to register DMA domain notifier; IOMMU DMA ops unavailable on bus '%s'\n",
bus->name);
kfree(nb);
}
return ret;
}
register_iommu_dma_ops_notifier 中申请一个notifier_block *nb,并将callback函数设定为__iommu_attach_notifier,然后通过bus_register_notifier 来遍历platform_bus_type/amba_bustype/pci_bus_type上的每个设备,分别调用__iommu_attach_notifier。
int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
{
return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
}
从bus_type->p->bus_notifier 可以得到blocking_notifier_head *nh,也就是每个bus 中的所有的notifier 是组成一个链表的。
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
struct notifier_block *n)
{
int ret;
/*
* This code gets used during boot-up, when task switching is
* not yet working and interrupts must remain disabled. At
* such times we must not call down_write().
*/
if (unlikely(system_state == SYSTEM_BOOTING))
return notifier_chain_register(&nh->head, n);
down_write(&nh->rwsem);
ret = notifier_chain_register(&nh->head, n);
up_write(&nh->rwsem);
return ret;
}
在blocking_notifier_chain_register中要判断一下当前系统的状态是不是SYSTEM_BOOTING,一般这种情况不满足,然后调用notifier_chain_register,从bus->p->bus_notifier 中得到notifier header的nh->head
static int notifier_chain_register(struct notifier_block **nl,
struct notifier_block *n)
{
while ((*nl) != NULL) {
if (n->priority > (*nl)->priority)
break;
nl = &((*nl)->next);
}
n->next = *nl;
rcu_assign_pointer(*nl, n);
return 0;
}
最终还在while循环中判断当前待调用notifier的优先级是否最高,不是的话,遍历链表将当前notifier 插入list中,最后通过rcu_assign_pointer 修改指针.
第二种情况是smmu已经使能后,设备如何切换到smmu上
这种情况主要在arm_smmu_device_probe 中处理
#ifdef CONFIG_PCI
if (pci_bus_type.iommu_ops != &arm_smmu_ops) {
pci_request_acs();
ret = bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
if (ret)
return ret;
}
#endif
#ifdef CONFIG_ARM_AMBA
if (amba_bustype.iommu_ops != &arm_smmu_ops) {
ret = bus_set_iommu(&amba_bustype, &arm_smmu_ops);
if (ret)
return ret;
}
#endif
if (platform_bus_type.iommu_ops != &arm_smmu_ops) {
ret = bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
if (ret)
return ret;
}
同样是这三个总线
int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops)
{
int err;
if (bus->iommu_ops != NULL)
return -EBUSY;
bus->iommu_ops = ops;
/* Do IOMMU specific setup for this bus-type */
err = iommu_bus_init(bus, ops);
if (err)
bus->iommu_ops = NULL;
return err;
}
如果bus->iommu_ops 不是null,就说明已经调用过这个函数了,不用重复调用,一般情况下系统有多个smmmu arm_smmu_device_probe 会被调用多次,但是bus_set_iommu 只能被调用一次.
static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops)
{
int err;
struct notifier_block *nb;
struct iommu_callback_data cb = {
.ops = ops,
};
nb = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
if (!nb)
return -ENOMEM;
nb->notifier_call = iommu_bus_notifier;
err = bus_register_notifier(bus, nb);
if (err)
goto out_free;
err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group);
if (err)
goto out_err;
return 0;
out_err:
/* Clean up */
bus_for_each_dev(bus, NULL, &cb, remove_iommu_group);
bus_unregister_notifier(bus, nb);
out_free:
kfree(nb);
return err;
}
这个函数主要做了两件事
第一个是在smmu使能之后的设备使用smmu注册notifier bus_register_notifier
第二个是为当前已有的设备调用add_iommu_group
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus || !bus->p)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
而add_iommu_group->add_device(arm_smmu_add_device)->device_group->iommu_group_add_device 等整个flow.
这样当device 初始化的时候会调用iommu_bus_notifier。从iommu_bus_notifier 中也可以看出一个device要使用smmu必须先add_device,然后得到group。而且每个group 都包含struct blocking_notifier_head *nh