Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iommu/vt-d: Make use of IOMMU sysfs support

Register our DRHD IOMMUs, cross link devices, and provide a base set
of attributes for the IOMMU. Note that IRQ remapping support parses
the DMAR table very early in boot, well before the iommu_class can
reasonably be setup, so our registration is split between
intel_iommu_init(), which occurs later, and alloc_iommu(), which
typically occurs much earlier, but may happen at any time later
with IOMMU hot-add support.

On a typical desktop system, this provides the following (pruned):

$ find /sys | grep dmar
/sys/devices/virtual/iommu/dmar0
/sys/devices/virtual/iommu/dmar0/devices
/sys/devices/virtual/iommu/dmar0/devices/0000:00:02.0
/sys/devices/virtual/iommu/dmar0/intel-iommu
/sys/devices/virtual/iommu/dmar0/intel-iommu/cap
/sys/devices/virtual/iommu/dmar0/intel-iommu/ecap
/sys/devices/virtual/iommu/dmar0/intel-iommu/address
/sys/devices/virtual/iommu/dmar0/intel-iommu/version
/sys/devices/virtual/iommu/dmar1
/sys/devices/virtual/iommu/dmar1/devices
/sys/devices/virtual/iommu/dmar1/devices/0000:00:00.0
/sys/devices/virtual/iommu/dmar1/devices/0000:00:01.0
/sys/devices/virtual/iommu/dmar1/devices/0000:00:16.0
/sys/devices/virtual/iommu/dmar1/devices/0000:00:1a.0
/sys/devices/virtual/iommu/dmar1/devices/0000:00:1b.0
/sys/devices/virtual/iommu/dmar1/devices/0000:00:1c.0
...
/sys/devices/virtual/iommu/dmar1/intel-iommu
/sys/devices/virtual/iommu/dmar1/intel-iommu/cap
/sys/devices/virtual/iommu/dmar1/intel-iommu/ecap
/sys/devices/virtual/iommu/dmar1/intel-iommu/address
/sys/devices/virtual/iommu/dmar1/intel-iommu/version
/sys/class/iommu/dmar0
/sys/class/iommu/dmar1

(devices also link back to the dmar units)

This makes address, version, capabilities, and extended capabilities
available, just like printed on boot. I've tried not to duplicate
data that can be found in the DMAR table, with the exception of the
address, which provides an easy way to associate the sysfs device with
a DRHD entry in the DMAR. It's tempting to add scopes and RMRR data
here, but the full DMAR table is already exposed under /sys/firmware/
and therefore already provides a way for userspace to learn such
details.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>

authored by

Alex Williamson and committed by
Joerg Roedel
a5459cfe c61959ec

+120 -1
+32
Documentation/ABI/testing/sysfs-class-iommu-intel-iommu
··· 1 + What: /sys/class/iommu/<iommu>/intel-iommu/address 2 + Date: June 2014 3 + KernelVersion: 3.17 4 + Contact: Alex Williamson <alex.williamson@redhat.com> 5 + Description: 6 + Physical address of the VT-d DRHD for this IOMMU. 7 + Format: %llx. This allows association of a sysfs 8 + intel-iommu with a DMAR DRHD table entry. 9 + 10 + What: /sys/class/iommu/<iommu>/intel-iommu/cap 11 + Date: June 2014 12 + KernelVersion: 3.17 13 + Contact: Alex Williamson <alex.williamson@redhat.com> 14 + Description: 15 + The cached hardware capability register value 16 + of this DRHD unit. Format: %llx. 17 + 18 + What: /sys/class/iommu/<iommu>/intel-iommu/ecap 19 + Date: June 2014 20 + KernelVersion: 3.17 21 + Contact: Alex Williamson <alex.williamson@redhat.com> 22 + Description: 23 + The cached hardware extended capability register 24 + value of this DRHD unit. Format: %llx. 25 + 26 + What: /sys/class/iommu/<iommu>/intel-iommu/version 27 + Date: June 2014 28 + KernelVersion: 3.17 29 + Contact: Alex Williamson <alex.williamson@redhat.com> 30 + Description: 31 + The architecture version as reported from the 32 + VT-d VER_REG. Format: %d:%d, major:minor
+9
drivers/iommu/dmar.c
··· 38 38 #include <linux/tboot.h> 39 39 #include <linux/dmi.h> 40 40 #include <linux/slab.h> 41 + #include <linux/iommu.h> 41 42 #include <asm/irq_remapping.h> 42 43 #include <asm/iommu_table.h> 43 44 ··· 981 980 raw_spin_lock_init(&iommu->register_lock); 982 981 983 982 drhd->iommu = iommu; 983 + 984 + if (intel_iommu_enabled) 985 + iommu->iommu_dev = iommu_device_create(NULL, iommu, 986 + intel_iommu_groups, 987 + iommu->name); 988 + 984 989 return 0; 985 990 986 991 err_unmap: ··· 998 991 999 992 static void free_iommu(struct intel_iommu *iommu) 1000 993 { 994 + iommu_device_destroy(iommu->iommu_dev); 995 + 1001 996 if (iommu->irq) { 1002 997 free_irq(iommu->irq, iommu); 1003 998 irq_set_handler_data(iommu->irq, NULL);
+76 -1
drivers/iommu/intel-iommu.c
··· 3944 3944 .priority = 0 3945 3945 }; 3946 3946 3947 + 3948 + static ssize_t intel_iommu_show_version(struct device *dev, 3949 + struct device_attribute *attr, 3950 + char *buf) 3951 + { 3952 + struct intel_iommu *iommu = dev_get_drvdata(dev); 3953 + u32 ver = readl(iommu->reg + DMAR_VER_REG); 3954 + return sprintf(buf, "%d:%d\n", 3955 + DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver)); 3956 + } 3957 + static DEVICE_ATTR(version, S_IRUGO, intel_iommu_show_version, NULL); 3958 + 3959 + static ssize_t intel_iommu_show_address(struct device *dev, 3960 + struct device_attribute *attr, 3961 + char *buf) 3962 + { 3963 + struct intel_iommu *iommu = dev_get_drvdata(dev); 3964 + return sprintf(buf, "%llx\n", iommu->reg_phys); 3965 + } 3966 + static DEVICE_ATTR(address, S_IRUGO, intel_iommu_show_address, NULL); 3967 + 3968 + static ssize_t intel_iommu_show_cap(struct device *dev, 3969 + struct device_attribute *attr, 3970 + char *buf) 3971 + { 3972 + struct intel_iommu *iommu = dev_get_drvdata(dev); 3973 + return sprintf(buf, "%llx\n", iommu->cap); 3974 + } 3975 + static DEVICE_ATTR(cap, S_IRUGO, intel_iommu_show_cap, NULL); 3976 + 3977 + static ssize_t intel_iommu_show_ecap(struct device *dev, 3978 + struct device_attribute *attr, 3979 + char *buf) 3980 + { 3981 + struct intel_iommu *iommu = dev_get_drvdata(dev); 3982 + return sprintf(buf, "%llx\n", iommu->ecap); 3983 + } 3984 + static DEVICE_ATTR(ecap, S_IRUGO, intel_iommu_show_ecap, NULL); 3985 + 3986 + static struct attribute *intel_iommu_attrs[] = { 3987 + &dev_attr_version.attr, 3988 + &dev_attr_address.attr, 3989 + &dev_attr_cap.attr, 3990 + &dev_attr_ecap.attr, 3991 + NULL, 3992 + }; 3993 + 3994 + static struct attribute_group intel_iommu_group = { 3995 + .name = "intel-iommu", 3996 + .attrs = intel_iommu_attrs, 3997 + }; 3998 + 3999 + const struct attribute_group *intel_iommu_groups[] = { 4000 + &intel_iommu_group, 4001 + NULL, 4002 + }; 4003 + 3947 4004 int __init intel_iommu_init(void) 3948 4005 { 3949 4006 int ret = -ENODEV; ··· 4071 4014 dma_ops = &intel_dma_ops; 4072 4015 4073 4016 init_iommu_pm_ops(); 4017 + 4018 + for_each_active_iommu(iommu, drhd) 4019 + iommu->iommu_dev = iommu_device_create(NULL, iommu, 4020 + intel_iommu_groups, 4021 + iommu->name); 4074 4022 4075 4023 bus_set_iommu(&pci_bus_type, &intel_iommu_ops); 4076 4024 bus_register_notifier(&pci_bus_type, &device_nb); ··· 4420 4358 4421 4359 static int intel_iommu_add_device(struct device *dev) 4422 4360 { 4361 + struct intel_iommu *iommu; 4423 4362 struct iommu_group *group; 4424 4363 u8 bus, devfn; 4425 4364 4426 - if (!device_to_iommu(dev, &bus, &devfn)) 4365 + iommu = device_to_iommu(dev, &bus, &devfn); 4366 + if (!iommu) 4427 4367 return -ENODEV; 4368 + 4369 + iommu_device_link(iommu->iommu_dev, dev); 4428 4370 4429 4371 group = iommu_group_get_for_dev(dev); 4430 4372 ··· 4441 4375 4442 4376 static void intel_iommu_remove_device(struct device *dev) 4443 4377 { 4378 + struct intel_iommu *iommu; 4379 + u8 bus, devfn; 4380 + 4381 + iommu = device_to_iommu(dev, &bus, &devfn); 4382 + if (!iommu) 4383 + return; 4384 + 4444 4385 iommu_group_remove_device(dev); 4386 + 4387 + iommu_device_unlink(iommu->iommu_dev, dev); 4445 4388 } 4446 4389 4447 4390 static struct iommu_ops intel_iommu_ops = {
+3
include/linux/intel-iommu.h
··· 336 336 #ifdef CONFIG_IRQ_REMAP 337 337 struct ir_table *ir_table; /* Interrupt remapping info */ 338 338 #endif 339 + struct device *iommu_dev; /* IOMMU-sysfs device */ 339 340 int node; 340 341 }; 341 342 ··· 365 364 extern int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu); 366 365 367 366 extern int dmar_ir_support(void); 367 + 368 + extern const struct attribute_group *intel_iommu_groups[]; 368 369 369 370 #endif