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

PCI: Make settable sysfs attributes more consistent

PCI devices have three settable boolean attributes, enable,
broken_parity_status, and msi_bus.

The store functions for these would silently interpret "0x01" as false,
"1llogical" as true, and "true" would be (silently!) ignored and do
nothing.

This is inconsistent with typical sysfs handling of settable attributes,
and just plain doesn't make much sense.

So, use strict_strtoul(), which was created for this purpose. The store
functions will treat a value of 0 as false, non-zero as true, and return
-EINVAL for a parse failure.

Additionally, is_enabled_store() and msi_bus_store() return -EPERM if
CAP_SYS_ADMIN is lacking, rather than silently doing nothing. This is more
typical behavior for sysfs attributes that need a capability.

And msi_bus_store() will only print the "forced subordinate bus ..."
warning if the MSI flag was actually forced to a different value.

Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

authored by

Trent Piepho and committed by
Jesse Barnes
92425a40 1684f5dd

+28 -20
+28 -20
drivers/pci/pci-sysfs.c
··· 58 58 const char *buf, size_t count) 59 59 { 60 60 struct pci_dev *pdev = to_pci_dev(dev); 61 - ssize_t consumed = -EINVAL; 61 + unsigned long val; 62 62 63 - if ((count > 0) && (*buf == '0' || *buf == '1')) { 64 - pdev->broken_parity_status = *buf == '1' ? 1 : 0; 65 - consumed = count; 66 - } 67 - return consumed; 63 + if (strict_strtoul(buf, 0, &val) < 0) 64 + return -EINVAL; 65 + 66 + pdev->broken_parity_status = !!val; 67 + 68 + return count; 68 69 } 69 70 70 71 static ssize_t local_cpus_show(struct device *dev, ··· 134 133 struct device_attribute *attr, const char *buf, 135 134 size_t count) 136 135 { 137 - ssize_t result = -EINVAL; 138 136 struct pci_dev *pdev = to_pci_dev(dev); 137 + unsigned long val; 138 + ssize_t result = strict_strtoul(buf, 0, &val); 139 + 140 + if (result < 0) 141 + return result; 139 142 140 143 /* this can crash the machine when done on the "wrong" device */ 141 144 if (!capable(CAP_SYS_ADMIN)) 142 - return count; 145 + return -EPERM; 143 146 144 - if (*buf == '0') { 147 + if (!val) { 145 148 if (atomic_read(&pdev->enable_cnt) != 0) 146 149 pci_disable_device(pdev); 147 150 else 148 151 result = -EIO; 149 - } else if (*buf == '1') 152 + } else 150 153 result = pci_enable_device(pdev); 151 154 152 155 return result < 0 ? result : count; ··· 190 185 const char *buf, size_t count) 191 186 { 192 187 struct pci_dev *pdev = to_pci_dev(dev); 188 + unsigned long val; 189 + 190 + if (strict_strtoul(buf, 0, &val) < 0) 191 + return -EINVAL; 193 192 194 193 /* bad things may happen if the no_msi flag is changed 195 194 * while some drivers are loaded */ 196 195 if (!capable(CAP_SYS_ADMIN)) 197 - return count; 196 + return -EPERM; 198 197 198 + /* Maybe pci devices without subordinate busses shouldn't even have this 199 + * attribute in the first place? */ 199 200 if (!pdev->subordinate) 200 201 return count; 201 202 202 - if (*buf == '0') { 203 - pdev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; 204 - dev_warn(&pdev->dev, "forced subordinate bus to not support MSI," 205 - " bad things could happen.\n"); 206 - } 203 + /* Is the flag going to change, or keep the value it already had? */ 204 + if (!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) ^ 205 + !!val) { 206 + pdev->subordinate->bus_flags ^= PCI_BUS_FLAGS_NO_MSI; 207 207 208 - if (*buf == '1') { 209 - pdev->subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI; 210 - dev_warn(&pdev->dev, "forced subordinate bus to support MSI," 211 - " bad things could happen.\n"); 208 + dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI," 209 + " bad things could happen\n", val ? "" : " not"); 212 210 } 213 211 214 212 return count;