X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fpci%2Fpci-sysfs.c;h=7a94076752d047125d4b72b876afeb28c034a344;hb=refs%2Fheads%2Fvserver;hp=6ca0061137a6937891fb1757699e010abec9d688;hpb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;p=linux-2.6.git diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 6ca006113..7a9407675 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -15,7 +15,6 @@ */ -#include #include #include #include @@ -29,7 +28,7 @@ static int sysfs_initialized; /* = 0 */ /* show configuration fields */ #define pci_config_attr(field, format_string) \ static ssize_t \ -field##_show(struct device *dev, char *buf) \ +field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ { \ struct pci_dev *pdev; \ \ @@ -44,36 +43,65 @@ pci_config_attr(subsystem_device, "0x%04x\n"); pci_config_attr(class, "0x%06x\n"); pci_config_attr(irq, "%u\n"); -static ssize_t local_cpus_show(struct device *dev, char *buf) +static ssize_t broken_parity_status_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + return sprintf (buf, "%u\n", pdev->broken_parity_status); +} + +static ssize_t broken_parity_status_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + ssize_t consumed = -EINVAL; + + if ((count > 0) && (*buf == '0' || *buf == '1')) { + pdev->broken_parity_status = *buf == '1' ? 1 : 0; + consumed = count; + } + return consumed; +} + +static ssize_t local_cpus_show(struct device *dev, + struct device_attribute *attr, char *buf) { - cpumask_t mask = pcibus_to_cpumask(to_pci_dev(dev)->bus); - int len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask); + cpumask_t mask; + int len; + + mask = pcibus_to_cpumask(to_pci_dev(dev)->bus); + len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask); strcat(buf,"\n"); return 1+len; } /* show resources */ static ssize_t -resource_show(struct device * dev, char * buf) +resource_show(struct device * dev, struct device_attribute *attr, char * buf) { struct pci_dev * pci_dev = to_pci_dev(dev); char * str = buf; int i; int max = 7; + resource_size_t start, end; if (pci_dev->subordinate) max = DEVICE_COUNT_RESOURCE; for (i = 0; i < max; i++) { - str += sprintf(str,"0x%016lx 0x%016lx 0x%016lx\n", - pci_resource_start(pci_dev,i), - pci_resource_end(pci_dev,i), - pci_resource_flags(pci_dev,i)); + struct resource *res = &pci_dev->resource[i]; + pci_resource_to_user(pci_dev, i, res, &start, &end); + str += sprintf(str,"0x%016llx 0x%016llx 0x%016llx\n", + (unsigned long long)start, + (unsigned long long)end, + (unsigned long long)res->flags); } return (str - buf); } -static ssize_t modalias_show(struct device *dev, char *buf) +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -84,6 +112,78 @@ static ssize_t modalias_show(struct device *dev, char *buf) (u8)(pci_dev->class)); } +static ssize_t is_enabled_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + ssize_t result = -EINVAL; + struct pci_dev *pdev = to_pci_dev(dev); + + /* this can crash the machine when done on the "wrong" device */ + if (!capable(CAP_SYS_ADMIN)) + return count; + + if (*buf == '0') { + if (atomic_read(&pdev->enable_cnt) != 0) + pci_disable_device(pdev); + else + result = -EIO; + } else if (*buf == '1') + result = pci_enable_device(pdev); + + return result < 0 ? result : count; +} + +static ssize_t is_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev; + + pdev = to_pci_dev (dev); + return sprintf (buf, "%u\n", atomic_read(&pdev->enable_cnt)); +} + +static ssize_t +msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + if (!pdev->subordinate) + return 0; + + return sprintf (buf, "%u\n", + !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI)); +} + +static ssize_t +msi_bus_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + /* bad things may happen if the no_msi flag is changed + * while some drivers are loaded */ + if (!capable(CAP_SYS_ADMIN)) + return count; + + if (!pdev->subordinate) + return count; + + if (*buf == '0') { + pdev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; + dev_warn(&pdev->dev, "forced subordinate bus to not support MSI," + " bad things could happen.\n"); + } + + if (*buf == '1') { + pdev->subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI; + dev_warn(&pdev->dev, "forced subordinate bus to support MSI," + " bad things could happen.\n"); + } + + return count; +} + struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource), __ATTR_RO(vendor), @@ -94,6 +194,10 @@ struct device_attribute pci_dev_attrs[] = { __ATTR_RO(irq), __ATTR_RO(local_cpus), __ATTR_RO(modalias), + __ATTR(enable, 0600, is_enabled_show, is_enabled_store), + __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), + broken_parity_status_show,broken_parity_status_store), + __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), __ATTR_NULL, }; @@ -123,7 +227,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) if ((off & 1) && size) { u8 val; - pci_read_config_byte(dev, off, &val); + pci_user_read_config_byte(dev, off, &val); data[off - init_off] = val; off++; size--; @@ -131,7 +235,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) if ((off & 3) && size > 2) { u16 val; - pci_read_config_word(dev, off, &val); + pci_user_read_config_word(dev, off, &val); data[off - init_off] = val & 0xff; data[off - init_off + 1] = (val >> 8) & 0xff; off += 2; @@ -140,7 +244,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) while (size > 3) { u32 val; - pci_read_config_dword(dev, off, &val); + pci_user_read_config_dword(dev, off, &val); data[off - init_off] = val & 0xff; data[off - init_off + 1] = (val >> 8) & 0xff; data[off - init_off + 2] = (val >> 16) & 0xff; @@ -151,7 +255,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) if (size >= 2) { u16 val; - pci_read_config_word(dev, off, &val); + pci_user_read_config_word(dev, off, &val); data[off - init_off] = val & 0xff; data[off - init_off + 1] = (val >> 8) & 0xff; off += 2; @@ -160,7 +264,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) if (size > 0) { u8 val; - pci_read_config_byte(dev, off, &val); + pci_user_read_config_byte(dev, off, &val); data[off - init_off] = val; off++; --size; @@ -185,7 +289,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) } if ((off & 1) && size) { - pci_write_config_byte(dev, off, data[off - init_off]); + pci_user_write_config_byte(dev, off, data[off - init_off]); off++; size--; } @@ -193,7 +297,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) if ((off & 3) && size > 2) { u16 val = data[off - init_off]; val |= (u16) data[off - init_off + 1] << 8; - pci_write_config_word(dev, off, val); + pci_user_write_config_word(dev, off, val); off += 2; size -= 2; } @@ -203,7 +307,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) val |= (u32) data[off - init_off + 1] << 8; val |= (u32) data[off - init_off + 2] << 16; val |= (u32) data[off - init_off + 3] << 24; - pci_write_config_dword(dev, off, val); + pci_user_write_config_dword(dev, off, val); off += 4; size -= 4; } @@ -211,13 +315,13 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) if (size >= 2) { u16 val = data[off - init_off]; val |= (u16) data[off - init_off + 1] << 8; - pci_write_config_word(dev, off, val); + pci_user_write_config_word(dev, off, val); off += 2; size -= 2; } if (size) { - pci_write_config_byte(dev, off, data[off - init_off]); + pci_user_write_config_byte(dev, off, data[off - init_off]); off++; --size; } @@ -313,73 +417,94 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, struct device, kobj)); struct resource *res = (struct resource *)attr->private; enum pci_mmap_state mmap_type; + resource_size_t start, end; + int i; - vma->vm_pgoff += res->start >> PAGE_SHIFT; + for (i = 0; i < PCI_ROM_RESOURCE; i++) + if (res == &pdev->resource[i]) + break; + if (i >= PCI_ROM_RESOURCE) + return -ENODEV; + + /* pci_mmap_page_range() expects the same kind of entry as coming + * from /proc/bus/pci/ which is a "user visible" value. If this is + * different from the resource itself, arch will do necessary fixup. + */ + pci_resource_to_user(pdev, i, res, &start, &end); + vma->vm_pgoff += start >> PAGE_SHIFT; mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; return pci_mmap_page_range(pdev, vma, mmap_type, 0); } /** - * pci_create_resource_files - create resource files in sysfs for @dev - * @dev: dev in question + * pci_remove_resource_files - cleanup resource files + * @dev: dev to cleanup * - * Walk the resources in @dev creating files for each resource available. + * If we created resource files for @dev, remove them from sysfs and + * free their resources. */ static void -pci_create_resource_files(struct pci_dev *pdev) +pci_remove_resource_files(struct pci_dev *pdev) { int i; - /* Expose the PCI resources from this device as files */ for (i = 0; i < PCI_ROM_RESOURCE; i++) { struct bin_attribute *res_attr; - /* skip empty resources */ - if (!pci_resource_len(pdev, i)) - continue; - - res_attr = kmalloc(sizeof(*res_attr) + 10, GFP_ATOMIC); + res_attr = pdev->res_attr[i]; if (res_attr) { - memset(res_attr, 0, sizeof(*res_attr) + 10); - pdev->res_attr[i] = res_attr; - /* Allocated above after the res_attr struct */ - res_attr->attr.name = (char *)(res_attr + 1); - sprintf(res_attr->attr.name, "resource%d", i); - res_attr->size = pci_resource_len(pdev, i); - res_attr->attr.mode = S_IRUSR | S_IWUSR; - res_attr->attr.owner = THIS_MODULE; - res_attr->mmap = pci_mmap_resource; - res_attr->private = &pdev->resource[i]; - sysfs_create_bin_file(&pdev->dev.kobj, res_attr); + sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); + kfree(res_attr); } } } /** - * pci_remove_resource_files - cleanup resource files - * @dev: dev to cleanup + * pci_create_resource_files - create resource files in sysfs for @dev + * @dev: dev in question * - * If we created resource files for @dev, remove them from sysfs and - * free their resources. + * Walk the resources in @dev creating files for each resource available. */ -static void -pci_remove_resource_files(struct pci_dev *pdev) +static int pci_create_resource_files(struct pci_dev *pdev) { int i; + int retval; + /* Expose the PCI resources from this device as files */ for (i = 0; i < PCI_ROM_RESOURCE; i++) { struct bin_attribute *res_attr; - res_attr = pdev->res_attr[i]; + /* skip empty resources */ + if (!pci_resource_len(pdev, i)) + continue; + + /* allocate attribute structure, piggyback attribute name */ + res_attr = kzalloc(sizeof(*res_attr) + 10, GFP_ATOMIC); if (res_attr) { - sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); - kfree(res_attr); + char *res_attr_name = (char *)(res_attr + 1); + + pdev->res_attr[i] = res_attr; + sprintf(res_attr_name, "resource%d", i); + res_attr->attr.name = res_attr_name; + res_attr->attr.mode = S_IRUSR | S_IWUSR; + res_attr->attr.owner = THIS_MODULE; + res_attr->size = pci_resource_len(pdev, i); + res_attr->mmap = pci_mmap_resource; + res_attr->private = &pdev->resource[i]; + retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); + if (retval) { + pci_remove_resource_files(pdev); + return retval; + } + } else { + return -ENOMEM; } } + return 0; } #else /* !HAVE_PCI_MMAP */ -static inline void pci_create_resource_files(struct pci_dev *dev) { return; } +static inline int pci_create_resource_files(struct pci_dev *dev) { return 0; } static inline void pci_remove_resource_files(struct pci_dev *dev) { return; } #endif /* HAVE_PCI_MMAP */ @@ -464,25 +589,29 @@ static struct bin_attribute pcie_config_attr = { .write = pci_write_config, }; -int pci_create_sysfs_dev_files (struct pci_dev *pdev) +int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) { + struct bin_attribute *rom_attr = NULL; + int retval; + if (!sysfs_initialized) return -EACCES; if (pdev->cfg_size < 4096) - sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); + retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); else - sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); + retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); + if (retval) + goto err; - pci_create_resource_files(pdev); + retval = pci_create_resource_files(pdev); + if (retval) + goto err_bin_file; /* If the device has a ROM, try to expose it in sysfs. */ if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) { - struct bin_attribute *rom_attr; - - rom_attr = kmalloc(sizeof(*rom_attr), GFP_ATOMIC); + rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC); if (rom_attr) { - memset(rom_attr, 0x00, sizeof(*rom_attr)); pdev->rom_attr = rom_attr; rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE); rom_attr->attr.name = "rom"; @@ -490,13 +619,28 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev) rom_attr->attr.owner = THIS_MODULE; rom_attr->read = pci_read_rom; rom_attr->write = pci_write_rom; - sysfs_create_bin_file(&pdev->dev.kobj, rom_attr); + retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr); + if (retval) + goto err_rom; + } else { + retval = -ENOMEM; + goto err_bin_file; } } /* add platform-specific attributes */ pcibios_add_platform_entries(pdev); - + return 0; + +err_rom: + kfree(rom_attr); +err_bin_file: + if (pdev->cfg_size < 4096) + sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); + else + sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); +err: + return retval; } /** @@ -507,6 +651,9 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev) */ void pci_remove_sysfs_dev_files(struct pci_dev *pdev) { + if (!sysfs_initialized) + return; + if (pdev->cfg_size < 4096) sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); else @@ -525,10 +672,14 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev) static int __init pci_sysfs_init(void) { struct pci_dev *pdev = NULL; - + int retval; + sysfs_initialized = 1; - for_each_pci_dev(pdev) - pci_create_sysfs_dev_files(pdev); + for_each_pci_dev(pdev) { + retval = pci_create_sysfs_dev_files(pdev); + if (retval) + return retval; + } return 0; }