diff options
Diffstat (limited to 'drivers/base')
| -rw-r--r-- | drivers/base/Makefile | 2 | ||||
| -rw-r--r-- | drivers/base/bus.c | 8 | ||||
| -rw-r--r-- | drivers/base/cacheinfo.c | 539 | ||||
| -rw-r--r-- | drivers/base/core.c | 38 | ||||
| -rw-r--r-- | drivers/base/cpu.c | 59 | ||||
| -rw-r--r-- | drivers/base/devcoredump.c | 56 | ||||
| -rw-r--r-- | drivers/base/firmware_class.c | 7 | ||||
| -rw-r--r-- | drivers/base/node.c | 14 | ||||
| -rw-r--r-- | drivers/base/platform.c | 22 | ||||
| -rw-r--r-- | drivers/base/topology.c | 71 |
10 files changed, 709 insertions, 107 deletions
diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 53c3fe1aeb29..527d291706e8 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile | |||
| @@ -4,7 +4,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \ | |||
| 4 | driver.o class.o platform.o \ | 4 | driver.o class.o platform.o \ |
| 5 | cpu.o firmware.o init.o map.o devres.o \ | 5 | cpu.o firmware.o init.o map.o devres.o \ |
| 6 | attribute_container.o transport_class.o \ | 6 | attribute_container.o transport_class.o \ |
| 7 | topology.o container.o property.o | 7 | topology.o container.o property.o cacheinfo.o |
| 8 | obj-$(CONFIG_DEVTMPFS) += devtmpfs.o | 8 | obj-$(CONFIG_DEVTMPFS) += devtmpfs.o |
| 9 | obj-$(CONFIG_DMA_CMA) += dma-contiguous.o | 9 | obj-$(CONFIG_DMA_CMA) += dma-contiguous.o |
| 10 | obj-y += power/ | 10 | obj-y += power/ |
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 83e910a57563..876bae5ade33 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c | |||
| @@ -254,13 +254,15 @@ static ssize_t store_drivers_probe(struct bus_type *bus, | |||
| 254 | const char *buf, size_t count) | 254 | const char *buf, size_t count) |
| 255 | { | 255 | { |
| 256 | struct device *dev; | 256 | struct device *dev; |
| 257 | int err = -EINVAL; | ||
| 257 | 258 | ||
| 258 | dev = bus_find_device_by_name(bus, NULL, buf); | 259 | dev = bus_find_device_by_name(bus, NULL, buf); |
| 259 | if (!dev) | 260 | if (!dev) |
| 260 | return -ENODEV; | 261 | return -ENODEV; |
| 261 | if (bus_rescan_devices_helper(dev, NULL) != 0) | 262 | if (bus_rescan_devices_helper(dev, NULL) == 0) |
| 262 | return -EINVAL; | 263 | err = count; |
| 263 | return count; | 264 | put_device(dev); |
| 265 | return err; | ||
| 264 | } | 266 | } |
| 265 | 267 | ||
| 266 | static struct device *next_device(struct klist_iter *i) | 268 | static struct device *next_device(struct klist_iter *i) |
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c new file mode 100644 index 000000000000..6e64563361f0 --- /dev/null +++ b/drivers/base/cacheinfo.c | |||
| @@ -0,0 +1,539 @@ | |||
| 1 | /* | ||
| 2 | * cacheinfo support - processor cache information via sysfs | ||
| 3 | * | ||
| 4 | * Based on arch/x86/kernel/cpu/intel_cacheinfo.c | ||
| 5 | * Author: Sudeep Holla <sudeep.holla@arm.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
| 12 | * kind, whether express or implied; without even the implied warranty | ||
| 13 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | */ | ||
| 19 | #include <linux/bitops.h> | ||
| 20 | #include <linux/cacheinfo.h> | ||
| 21 | #include <linux/compiler.h> | ||
| 22 | #include <linux/cpu.h> | ||
| 23 | #include <linux/device.h> | ||
| 24 | #include <linux/init.h> | ||
| 25 | #include <linux/of.h> | ||
| 26 | #include <linux/sched.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/smp.h> | ||
| 29 | #include <linux/sysfs.h> | ||
| 30 | |||
| 31 | /* pointer to per cpu cacheinfo */ | ||
| 32 | static DEFINE_PER_CPU(struct cpu_cacheinfo, ci_cpu_cacheinfo); | ||
| 33 | #define ci_cacheinfo(cpu) (&per_cpu(ci_cpu_cacheinfo, cpu)) | ||
| 34 | #define cache_leaves(cpu) (ci_cacheinfo(cpu)->num_leaves) | ||
| 35 | #define per_cpu_cacheinfo(cpu) (ci_cacheinfo(cpu)->info_list) | ||
| 36 | |||
| 37 | struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu) | ||
| 38 | { | ||
| 39 | return ci_cacheinfo(cpu); | ||
| 40 | } | ||
| 41 | |||
| 42 | #ifdef CONFIG_OF | ||
| 43 | static int cache_setup_of_node(unsigned int cpu) | ||
| 44 | { | ||
| 45 | struct device_node *np; | ||
| 46 | struct cacheinfo *this_leaf; | ||
| 47 | struct device *cpu_dev = get_cpu_device(cpu); | ||
| 48 | struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); | ||
| 49 | unsigned int index = 0; | ||
| 50 | |||
| 51 | /* skip if of_node is already populated */ | ||
| 52 | if (this_cpu_ci->info_list->of_node) | ||
| 53 | return 0; | ||
| 54 | |||
| 55 | if (!cpu_dev) { | ||
| 56 | pr_err("No cpu device for CPU %d\n", cpu); | ||
| 57 | return -ENODEV; | ||
| 58 | } | ||
| 59 | np = cpu_dev->of_node; | ||
| 60 | if (!np) { | ||
| 61 | pr_err("Failed to find cpu%d device node\n", cpu); | ||
| 62 | return -ENOENT; | ||
| 63 | } | ||
| 64 | |||
| 65 | while (np && index < cache_leaves(cpu)) { | ||
| 66 | this_leaf = this_cpu_ci->info_list + index; | ||
| 67 | if (this_leaf->level != 1) | ||
| 68 | np = of_find_next_cache_node(np); | ||
| 69 | else | ||
| 70 | np = of_node_get(np);/* cpu node itself */ | ||
| 71 | this_leaf->of_node = np; | ||
| 72 | index++; | ||
| 73 | } | ||
| 74 | return 0; | ||
| 75 | } | ||
| 76 | |||
| 77 | static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, | ||
| 78 | struct cacheinfo *sib_leaf) | ||
| 79 | { | ||
| 80 | return sib_leaf->of_node == this_leaf->of_node; | ||
| 81 | } | ||
| 82 | #else | ||
| 83 | static inline int cache_setup_of_node(unsigned int cpu) { return 0; } | ||
| 84 | static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, | ||
| 85 | struct cacheinfo *sib_leaf) | ||
| 86 | { | ||
| 87 | /* | ||
| 88 | * For non-DT systems, assume unique level 1 cache, system-wide | ||
| 89 | * shared caches for all other levels. This will be used only if | ||
| 90 | * arch specific code has not populated shared_cpu_map | ||
| 91 | */ | ||
| 92 | return !(this_leaf->level == 1); | ||
| 93 | } | ||
| 94 | #endif | ||
| 95 | |||
| 96 | static int cache_shared_cpu_map_setup(unsigned int cpu) | ||
| 97 | { | ||
| 98 | struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); | ||
| 99 | struct cacheinfo *this_leaf, *sib_leaf; | ||
| 100 | unsigned int index; | ||
| 101 | int ret; | ||
| 102 | |||
| 103 | ret = cache_setup_of_node(cpu); | ||
| 104 | if (ret) | ||
| 105 | return ret; | ||
| 106 | |||
| 107 | for (index = 0; index < cache_leaves(cpu); index++) { | ||
| 108 | unsigned int i; | ||
| 109 | |||
| 110 | this_leaf = this_cpu_ci->info_list + index; | ||
| 111 | /* skip if shared_cpu_map is already populated */ | ||
| 112 | if (!cpumask_empty(&this_leaf->shared_cpu_map)) | ||
| 113 | continue; | ||
| 114 | |||
| 115 | cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); | ||
| 116 | for_each_online_cpu(i) { | ||
| 117 | struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); | ||
| 118 | |||
| 119 | if (i == cpu || !sib_cpu_ci->info_list) | ||
| 120 | continue;/* skip if itself or no cacheinfo */ | ||
| 121 | sib_leaf = sib_cpu_ci->info_list + index; | ||
| 122 | if (cache_leaves_are_shared(this_leaf, sib_leaf)) { | ||
| 123 | cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); | ||
| 124 | cpumask_set_cpu(i, &this_leaf->shared_cpu_map); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | return 0; | ||
| 130 | } | ||
| 131 | |||
| 132 | static void cache_shared_cpu_map_remove(unsigned int cpu) | ||
| 133 | { | ||
| 134 | struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); | ||
| 135 | struct cacheinfo *this_leaf, *sib_leaf; | ||
| 136 | unsigned int sibling, index; | ||
| 137 | |||
| 138 | for (index = 0; index < cache_leaves(cpu); index++) { | ||
| 139 | this_leaf = this_cpu_ci->info_list + index; | ||
| 140 | for_each_cpu(sibling, &this_leaf->shared_cpu_map) { | ||
| 141 | struct cpu_cacheinfo *sib_cpu_ci; | ||
| 142 | |||
| 143 | if (sibling == cpu) /* skip itself */ | ||
| 144 | continue; | ||
| 145 | sib_cpu_ci = get_cpu_cacheinfo(sibling); | ||
| 146 | sib_leaf = sib_cpu_ci->info_list + index; | ||
| 147 | cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); | ||
| 148 | cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); | ||
| 149 | } | ||
| 150 | of_node_put(this_leaf->of_node); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | static void free_cache_attributes(unsigned int cpu) | ||
| 155 | { | ||
| 156 | cache_shared_cpu_map_remove(cpu); | ||
| 157 | |||
| 158 | kfree(per_cpu_cacheinfo(cpu)); | ||
| 159 | per_cpu_cacheinfo(cpu) = NULL; | ||
| 160 | } | ||
| 161 | |||
| 162 | int __weak init_cache_level(unsigned int cpu) | ||
| 163 | { | ||
| 164 | return -ENOENT; | ||
| 165 | } | ||
| 166 | |||
| 167 | int __weak populate_cache_leaves(unsigned int cpu) | ||
| 168 | { | ||
| 169 | return -ENOENT; | ||
| 170 | } | ||
| 171 | |||
| 172 | static int detect_cache_attributes(unsigned int cpu) | ||
| 173 | { | ||
| 174 | int ret; | ||
| 175 | |||
| 176 | if (init_cache_level(cpu)) | ||
| 177 | return -ENOENT; | ||
| 178 | |||
| 179 | per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu), | ||
| 180 | sizeof(struct cacheinfo), GFP_KERNEL); | ||
| 181 | if (per_cpu_cacheinfo(cpu) == NULL) | ||
| 182 | return -ENOMEM; | ||
| 183 | |||
| 184 | ret = populate_cache_leaves(cpu); | ||
| 185 | if (ret) | ||
| 186 | goto free_ci; | ||
| 187 | /* | ||
| 188 | * For systems using DT for cache hierarcy, of_node and shared_cpu_map | ||
| 189 | * will be set up here only if they are not populated already | ||
| 190 | */ | ||
| 191 | ret = cache_shared_cpu_map_setup(cpu); | ||
| 192 | if (ret) | ||
| 193 | goto free_ci; | ||
| 194 | return 0; | ||
| 195 | |||
| 196 | free_ci: | ||
| 197 | free_cache_attributes(cpu); | ||
| 198 | return ret; | ||
| 199 | } | ||
| 200 | |||
| 201 | /* pointer to cpuX/cache device */ | ||
| 202 | static DEFINE_PER_CPU(struct device *, ci_cache_dev); | ||
| 203 | #define per_cpu_cache_dev(cpu) (per_cpu(ci_cache_dev, cpu)) | ||
| 204 | |||
| 205 | static cpumask_t cache_dev_map; | ||
| 206 | |||
| 207 | /* pointer to array of devices for cpuX/cache/indexY */ | ||
| 208 | static DEFINE_PER_CPU(struct device **, ci_index_dev); | ||
| 209 | #define per_cpu_index_dev(cpu) (per_cpu(ci_index_dev, cpu)) | ||
| 210 | #define per_cache_index_dev(cpu, idx) ((per_cpu_index_dev(cpu))[idx]) | ||
| 211 | |||
| 212 | #define show_one(file_name, object) \ | ||
| 213 | static ssize_t file_name##_show(struct device *dev, \ | ||
| 214 | struct device_attribute *attr, char *buf) \ | ||
| 215 | { \ | ||
| 216 | struct cacheinfo *this_leaf = dev_get_drvdata(dev); \ | ||
| 217 | return sprintf(buf, "%u\n", this_leaf->object); \ | ||
| 218 | } | ||
| 219 | |||
| 220 | show_one(level, level); | ||
| 221 | show_one(coherency_line_size, coherency_line_size); | ||
| 222 | show_one(number_of_sets, number_of_sets); | ||
| 223 | show_one(physical_line_partition, physical_line_partition); | ||
| 224 | show_one(ways_of_associativity, ways_of_associativity); | ||
| 225 | |||
| 226 | static ssize_t size_show(struct device *dev, | ||
| 227 | struct device_attribute *attr, char *buf) | ||
| 228 | { | ||
| 229 | struct cacheinfo *this_leaf = dev_get_drvdata(dev); | ||
| 230 | |||
| 231 | return sprintf(buf, "%uK\n", this_leaf->size >> 10); | ||
| 232 | } | ||
| 233 | |||
| 234 | static ssize_t shared_cpumap_show_func(struct device *dev, bool list, char *buf) | ||
| 235 | { | ||
| 236 | struct cacheinfo *this_leaf = dev_get_drvdata(dev); | ||
| 237 | const struct cpumask *mask = &this_leaf->shared_cpu_map; | ||
| 238 | |||
| 239 | return cpumap_print_to_pagebuf(list, buf, mask); | ||
| 240 | } | ||
| 241 | |||
| 242 | static ssize_t shared_cpu_map_show(struct device *dev, | ||
| 243 | struct device_attribute *attr, char *buf) | ||
| 244 | { | ||
| 245 | return shared_cpumap_show_func(dev, false, buf); | ||
| 246 | } | ||
| 247 | |||
| 248 | static ssize_t shared_cpu_list_show(struct device *dev, | ||
| 249 | struct device_attribute *attr, char *buf) | ||
| 250 | { | ||
| 251 | return shared_cpumap_show_func(dev, true, buf); | ||
| 252 | } | ||
| 253 | |||
| 254 | static ssize_t type_show(struct device *dev, | ||
| 255 | struct device_attribute *attr, char *buf) | ||
| 256 | { | ||
| 257 | struct cacheinfo *this_leaf = dev_get_drvdata(dev); | ||
| 258 | |||
| 259 | switch (this_leaf->type) { | ||
| 260 | case CACHE_TYPE_DATA: | ||
| 261 | return sprintf(buf, "Data\n"); | ||
| 262 | case CACHE_TYPE_INST: | ||
| 263 | return sprintf(buf, "Instruction\n"); | ||
| 264 | case CACHE_TYPE_UNIFIED: | ||
| 265 | return sprintf(buf, "Unified\n"); | ||
| 266 | default: | ||
| 267 | return -EINVAL; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | static ssize_t allocation_policy_show(struct device *dev, | ||
| 272 | struct device_attribute *attr, char *buf) | ||
| 273 | { | ||
| 274 | struct cacheinfo *this_leaf = dev_get_drvdata(dev); | ||
| 275 | unsigned int ci_attr = this_leaf->attributes; | ||
| 276 | int n = 0; | ||
| 277 | |||
| 278 | if ((ci_attr & CACHE_READ_ALLOCATE) && (ci_attr & CACHE_WRITE_ALLOCATE)) | ||
| 279 | n = sprintf(buf, "ReadWriteAllocate\n"); | ||
| 280 | else if (ci_attr & CACHE_READ_ALLOCATE) | ||
| 281 | n = sprintf(buf, "ReadAllocate\n"); | ||
| 282 | else if (ci_attr & CACHE_WRITE_ALLOCATE) | ||
| 283 | n = sprintf(buf, "WriteAllocate\n"); | ||
| 284 | return n; | ||
| 285 | } | ||
| 286 | |||
| 287 | static ssize_t write_policy_show(struct device *dev, | ||
| 288 | struct device_attribute *attr, char *buf) | ||
| 289 | { | ||
| 290 | struct cacheinfo *this_leaf = dev_get_drvdata(dev); | ||
| 291 | unsigned int ci_attr = this_leaf->attributes; | ||
| 292 | int n = 0; | ||
| 293 | |||
| 294 | if (ci_attr & CACHE_WRITE_THROUGH) | ||
| 295 | n = sprintf(buf, "WriteThrough\n"); | ||
| 296 | else if (ci_attr & CACHE_WRITE_BACK) | ||
| 297 | n = sprintf(buf, "WriteBack\n"); | ||
| 298 | return n; | ||
| 299 | } | ||
| 300 | |||
| 301 | static DEVICE_ATTR_RO(level); | ||
| 302 | static DEVICE_ATTR_RO(type); | ||
| 303 | static DEVICE_ATTR_RO(coherency_line_size); | ||
| 304 | static DEVICE_ATTR_RO(ways_of_associativity); | ||
| 305 | static DEVICE_ATTR_RO(number_of_sets); | ||
| 306 | static DEVICE_ATTR_RO(size); | ||
| 307 | static DEVICE_ATTR_RO(allocation_policy); | ||
| 308 | static DEVICE_ATTR_RO(write_policy); | ||
| 309 | static DEVICE_ATTR_RO(shared_cpu_map); | ||
| 310 | static DEVICE_ATTR_RO(shared_cpu_list); | ||
| 311 | static DEVICE_ATTR_RO(physical_line_partition); | ||
| 312 | |||
| 313 | static struct attribute *cache_default_attrs[] = { | ||
| 314 | &dev_attr_type.attr, | ||
| 315 | &dev_attr_level.attr, | ||
| 316 | &dev_attr_shared_cpu_map.attr, | ||
| 317 | &dev_attr_shared_cpu_list.attr, | ||
| 318 | &dev_attr_coherency_line_size.attr, | ||
| 319 | &dev_attr_ways_of_associativity.attr, | ||
| 320 | &dev_attr_number_of_sets.attr, | ||
| 321 | &dev_attr_size.attr, | ||
| 322 | &dev_attr_allocation_policy.attr, | ||
| 323 | &dev_attr_write_policy.attr, | ||
| 324 | &dev_attr_physical_line_partition.attr, | ||
| 325 | NULL | ||
| 326 | }; | ||
| 327 | |||
| 328 | static umode_t | ||
| 329 | cache_default_attrs_is_visible(struct kobject *kobj, | ||
| 330 | struct attribute *attr, int unused) | ||
| 331 | { | ||
| 332 | struct device *dev = kobj_to_dev(kobj); | ||
| 333 | struct cacheinfo *this_leaf = dev_get_drvdata(dev); | ||
| 334 | const struct cpumask *mask = &this_leaf->shared_cpu_map; | ||
| 335 | umode_t mode = attr->mode; | ||
| 336 | |||
| 337 | if ((attr == &dev_attr_type.attr) && this_leaf->type) | ||
| 338 | return mode; | ||
| 339 | if ((attr == &dev_attr_level.attr) && this_leaf->level) | ||
| 340 | return mode; | ||
| 341 | if ((attr == &dev_attr_shared_cpu_map.attr) && !cpumask_empty(mask)) | ||
| 342 | return mode; | ||
| 343 | if ((attr == &dev_attr_shared_cpu_list.attr) && !cpumask_empty(mask)) | ||
| 344 | return mode; | ||
| 345 | if ((attr == &dev_attr_coherency_line_size.attr) && | ||
| 346 | this_leaf->coherency_line_size) | ||
| 347 | return mode; | ||
| 348 | if ((attr == &dev_attr_ways_of_associativity.attr) && | ||
| 349 | this_leaf->size) /* allow 0 = full associativity */ | ||
| 350 | return mode; | ||
| 351 | if ((attr == &dev_attr_number_of_sets.attr) && | ||
| 352 | this_leaf->number_of_sets) | ||
| 353 | return mode; | ||
| 354 | if ((attr == &dev_attr_size.attr) && this_leaf->size) | ||
| 355 | return mode; | ||
| 356 | if ((attr == &dev_attr_write_policy.attr) && | ||
| 357 | (this_leaf->attributes & CACHE_WRITE_POLICY_MASK)) | ||
| 358 | return mode; | ||
| 359 | if ((attr == &dev_attr_allocation_policy.attr) && | ||
| 360 | (this_leaf->attributes & CACHE_ALLOCATE_POLICY_MASK)) | ||
| 361 | return mode; | ||
| 362 | if ((attr == &dev_attr_physical_line_partition.attr) && | ||
| 363 | this_leaf->physical_line_partition) | ||
| 364 | return mode; | ||
| 365 | |||
| 366 | return 0; | ||
| 367 | } | ||
| 368 | |||
| 369 | static const struct attribute_group cache_default_group = { | ||
| 370 | .attrs = cache_default_attrs, | ||
| 371 | .is_visible = cache_default_attrs_is_visible, | ||
| 372 | }; | ||
| 373 | |||
| 374 | static const struct attribute_group *cache_default_groups[] = { | ||
| 375 | &cache_default_group, | ||
| 376 | NULL, | ||
| 377 | }; | ||
| 378 | |||
| 379 | static const struct attribute_group *cache_private_groups[] = { | ||
| 380 | &cache_default_group, | ||
| 381 | NULL, /* Place holder for private group */ | ||
| 382 | NULL, | ||
| 383 | }; | ||
| 384 | |||
| 385 | const struct attribute_group * | ||
| 386 | __weak cache_get_priv_group(struct cacheinfo *this_leaf) | ||
| 387 | { | ||
| 388 | return NULL; | ||
| 389 | } | ||
| 390 | |||
| 391 | static const struct attribute_group ** | ||
| 392 | cache_get_attribute_groups(struct cacheinfo *this_leaf) | ||
| 393 | { | ||
| 394 | const struct attribute_group *priv_group = | ||
| 395 | cache_get_priv_group(this_leaf); | ||
| 396 | |||
| 397 | if (!priv_group) | ||
| 398 | return cache_default_groups; | ||
| 399 | |||
| 400 | if (!cache_private_groups[1]) | ||
| 401 | cache_private_groups[1] = priv_group; | ||
| 402 | |||
| 403 | return cache_private_groups; | ||
| 404 | } | ||
| 405 | |||
| 406 | /* Add/Remove cache interface for CPU device */ | ||
| 407 | static void cpu_cache_sysfs_exit(unsigned int cpu) | ||
| 408 | { | ||
| 409 | int i; | ||
| 410 | struct device *ci_dev; | ||
| 411 | |||
| 412 | if (per_cpu_index_dev(cpu)) { | ||
| 413 | for (i = 0; i < cache_leaves(cpu); i++) { | ||
| 414 | ci_dev = per_cache_index_dev(cpu, i); | ||
| 415 | if (!ci_dev) | ||
| 416 | continue; | ||
| 417 | device_unregister(ci_dev); | ||
| 418 | } | ||
| 419 | kfree(per_cpu_index_dev(cpu)); | ||
| 420 | per_cpu_index_dev(cpu) = NULL; | ||
| 421 | } | ||
| 422 | device_unregister(per_cpu_cache_dev(cpu)); | ||
| 423 | per_cpu_cache_dev(cpu) = NULL; | ||
| 424 | } | ||
| 425 | |||
| 426 | static int cpu_cache_sysfs_init(unsigned int cpu) | ||
| 427 | { | ||
| 428 | struct device *dev = get_cpu_device(cpu); | ||
| 429 | |||
| 430 | if (per_cpu_cacheinfo(cpu) == NULL) | ||
| 431 | return -ENOENT; | ||
| 432 | |||
| 433 | per_cpu_cache_dev(cpu) = cpu_device_create(dev, NULL, NULL, "cache"); | ||
| 434 | if (IS_ERR(per_cpu_cache_dev(cpu))) | ||
| 435 | return PTR_ERR(per_cpu_cache_dev(cpu)); | ||
| 436 | |||
| 437 | /* Allocate all required memory */ | ||
| 438 | per_cpu_index_dev(cpu) = kcalloc(cache_leaves(cpu), | ||
| 439 | sizeof(struct device *), GFP_KERNEL); | ||
| 440 | if (unlikely(per_cpu_index_dev(cpu) == NULL)) | ||
| 441 | goto err_out; | ||
| 442 | |||
| 443 | return 0; | ||
| 444 | |||
| 445 | err_out: | ||
| 446 | cpu_cache_sysfs_exit(cpu); | ||
| 447 | return -ENOMEM; | ||
| 448 | } | ||
| 449 | |||
| 450 | static int cache_add_dev(unsigned int cpu) | ||
| 451 | { | ||
| 452 | unsigned int i; | ||
| 453 | int rc; | ||
| 454 | struct device *ci_dev, *parent; | ||
| 455 | struct cacheinfo *this_leaf; | ||
| 456 | struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); | ||
| 457 | const struct attribute_group **cache_groups; | ||
| 458 | |||
| 459 | rc = cpu_cache_sysfs_init(cpu); | ||
| 460 | if (unlikely(rc < 0)) | ||
| 461 | return rc; | ||
| 462 | |||
| 463 | parent = per_cpu_cache_dev(cpu); | ||
| 464 | for (i = 0; i < cache_leaves(cpu); i++) { | ||
| 465 | this_leaf = this_cpu_ci->info_list + i; | ||
| 466 | if (this_leaf->disable_sysfs) | ||
| 467 | continue; | ||
| 468 | cache_groups = cache_get_attribute_groups(this_leaf); | ||
| 469 | ci_dev = cpu_device_create(parent, this_leaf, cache_groups, | ||
| 470 | "index%1u", i); | ||
| 471 | if (IS_ERR(ci_dev)) { | ||
| 472 | rc = PTR_ERR(ci_dev); | ||
| 473 | goto err; | ||
| 474 | } | ||
| 475 | per_cache_index_dev(cpu, i) = ci_dev; | ||
| 476 | } | ||
| 477 | cpumask_set_cpu(cpu, &cache_dev_map); | ||
| 478 | |||
| 479 | return 0; | ||
| 480 | err: | ||
| 481 | cpu_cache_sysfs_exit(cpu); | ||
| 482 | return rc; | ||
| 483 | } | ||
| 484 | |||
| 485 | static void cache_remove_dev(unsigned int cpu) | ||
| 486 | { | ||
| 487 | if (!cpumask_test_cpu(cpu, &cache_dev_map)) | ||
| 488 | return; | ||
| 489 | cpumask_clear_cpu(cpu, &cache_dev_map); | ||
| 490 | |||
| 491 | cpu_cache_sysfs_exit(cpu); | ||
| 492 | } | ||
| 493 | |||
| 494 | static int cacheinfo_cpu_callback(struct notifier_block *nfb, | ||
| 495 | unsigned long action, void *hcpu) | ||
| 496 | { | ||
| 497 | unsigned int cpu = (unsigned long)hcpu; | ||
| 498 | int rc = 0; | ||
| 499 | |||
| 500 | switch (action & ~CPU_TASKS_FROZEN) { | ||
| 501 | case CPU_ONLINE: | ||
| 502 | rc = detect_cache_attributes(cpu); | ||
| 503 | if (!rc) | ||
| 504 | rc = cache_add_dev(cpu); | ||
| 505 | break; | ||
| 506 | case CPU_DEAD: | ||
| 507 | cache_remove_dev(cpu); | ||
| 508 | if (per_cpu_cacheinfo(cpu)) | ||
| 509 | free_cache_attributes(cpu); | ||
| 510 | break; | ||
| 511 | } | ||
| 512 | return notifier_from_errno(rc); | ||
| 513 | } | ||
| 514 | |||
| 515 | static int __init cacheinfo_sysfs_init(void) | ||
| 516 | { | ||
| 517 | int cpu, rc = 0; | ||
| 518 | |||
| 519 | cpu_notifier_register_begin(); | ||
| 520 | |||
| 521 | for_each_online_cpu(cpu) { | ||
| 522 | rc = detect_cache_attributes(cpu); | ||
| 523 | if (rc) | ||
| 524 | goto out; | ||
| 525 | rc = cache_add_dev(cpu); | ||
| 526 | if (rc) { | ||
| 527 | free_cache_attributes(cpu); | ||
| 528 | pr_err("error populating cacheinfo..cpu%d\n", cpu); | ||
| 529 | goto out; | ||
| 530 | } | ||
| 531 | } | ||
| 532 | __hotcpu_notifier(cacheinfo_cpu_callback, 0); | ||
| 533 | |||
| 534 | out: | ||
| 535 | cpu_notifier_register_done(); | ||
| 536 | return rc; | ||
| 537 | } | ||
| 538 | |||
| 539 | device_initcall(cacheinfo_sysfs_init); | ||
diff --git a/drivers/base/core.c b/drivers/base/core.c index 842d04707de6..97e2baf6e5d8 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
| @@ -1021,18 +1021,6 @@ int device_add(struct device *dev) | |||
| 1021 | if (error) | 1021 | if (error) |
| 1022 | goto attrError; | 1022 | goto attrError; |
| 1023 | 1023 | ||
| 1024 | if (MAJOR(dev->devt)) { | ||
| 1025 | error = device_create_file(dev, &dev_attr_dev); | ||
| 1026 | if (error) | ||
| 1027 | goto ueventattrError; | ||
| 1028 | |||
| 1029 | error = device_create_sys_dev_entry(dev); | ||
| 1030 | if (error) | ||
| 1031 | goto devtattrError; | ||
| 1032 | |||
| 1033 | devtmpfs_create_node(dev); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | error = device_add_class_symlinks(dev); | 1024 | error = device_add_class_symlinks(dev); |
| 1037 | if (error) | 1025 | if (error) |
| 1038 | goto SymlinkError; | 1026 | goto SymlinkError; |
| @@ -1047,6 +1035,18 @@ int device_add(struct device *dev) | |||
| 1047 | goto DPMError; | 1035 | goto DPMError; |
| 1048 | device_pm_add(dev); | 1036 | device_pm_add(dev); |
| 1049 | 1037 | ||
| 1038 | if (MAJOR(dev->devt)) { | ||
| 1039 | error = device_create_file(dev, &dev_attr_dev); | ||
| 1040 | if (error) | ||
| 1041 | goto DevAttrError; | ||
| 1042 | |||
| 1043 | error = device_create_sys_dev_entry(dev); | ||
| 1044 | if (error) | ||
| 1045 | goto SysEntryError; | ||
| 1046 | |||
| 1047 | devtmpfs_create_node(dev); | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | /* Notify clients of device addition. This call must come | 1050 | /* Notify clients of device addition. This call must come |
| 1051 | * after dpm_sysfs_add() and before kobject_uevent(). | 1051 | * after dpm_sysfs_add() and before kobject_uevent(). |
| 1052 | */ | 1052 | */ |
| @@ -1076,6 +1076,12 @@ int device_add(struct device *dev) | |||
| 1076 | done: | 1076 | done: |
| 1077 | put_device(dev); | 1077 | put_device(dev); |
| 1078 | return error; | 1078 | return error; |
| 1079 | SysEntryError: | ||
| 1080 | if (MAJOR(dev->devt)) | ||
| 1081 | device_remove_file(dev, &dev_attr_dev); | ||
| 1082 | DevAttrError: | ||
| 1083 | device_pm_remove(dev); | ||
| 1084 | dpm_sysfs_remove(dev); | ||
| 1079 | DPMError: | 1085 | DPMError: |
| 1080 | bus_remove_device(dev); | 1086 | bus_remove_device(dev); |
| 1081 | BusError: | 1087 | BusError: |
| @@ -1083,14 +1089,6 @@ done: | |||
| 1083 | AttrsError: | 1089 | AttrsError: |
| 1084 | device_remove_class_symlinks(dev); | 1090 | device_remove_class_symlinks(dev); |
| 1085 | SymlinkError: | 1091 | SymlinkError: |
| 1086 | if (MAJOR(dev->devt)) | ||
| 1087 | devtmpfs_delete_node(dev); | ||
| 1088 | if (MAJOR(dev->devt)) | ||
| 1089 | device_remove_sys_dev_entry(dev); | ||
| 1090 | devtattrError: | ||
| 1091 | if (MAJOR(dev->devt)) | ||
| 1092 | device_remove_file(dev, &dev_attr_dev); | ||
| 1093 | ueventattrError: | ||
| 1094 | device_remove_file(dev, &dev_attr_uevent); | 1092 | device_remove_file(dev, &dev_attr_uevent); |
| 1095 | attrError: | 1093 | attrError: |
| 1096 | kobject_uevent(&dev->kobj, KOBJ_REMOVE); | 1094 | kobject_uevent(&dev->kobj, KOBJ_REMOVE); |
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 006b1bc5297d..f829a4c71749 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c | |||
| @@ -207,11 +207,8 @@ static ssize_t show_cpus_attr(struct device *dev, | |||
| 207 | char *buf) | 207 | char *buf) |
| 208 | { | 208 | { |
| 209 | struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr); | 209 | struct cpu_attr *ca = container_of(attr, struct cpu_attr, attr); |
| 210 | int n = cpulist_scnprintf(buf, PAGE_SIZE-2, *(ca->map)); | ||
| 211 | 210 | ||
| 212 | buf[n++] = '\n'; | 211 | return cpumap_print_to_pagebuf(true, buf, *ca->map); |
| 213 | buf[n] = '\0'; | ||
| 214 | return n; | ||
| 215 | } | 212 | } |
| 216 | 213 | ||
| 217 | #define _CPU_ATTR(name, map) \ | 214 | #define _CPU_ATTR(name, map) \ |
| @@ -366,6 +363,60 @@ struct device *get_cpu_device(unsigned cpu) | |||
| 366 | } | 363 | } |
| 367 | EXPORT_SYMBOL_GPL(get_cpu_device); | 364 | EXPORT_SYMBOL_GPL(get_cpu_device); |
| 368 | 365 | ||
| 366 | static void device_create_release(struct device *dev) | ||
| 367 | { | ||
| 368 | kfree(dev); | ||
| 369 | } | ||
| 370 | |||
| 371 | static struct device * | ||
| 372 | __cpu_device_create(struct device *parent, void *drvdata, | ||
| 373 | const struct attribute_group **groups, | ||
| 374 | const char *fmt, va_list args) | ||
| 375 | { | ||
| 376 | struct device *dev = NULL; | ||
| 377 | int retval = -ENODEV; | ||
| 378 | |||
| 379 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
| 380 | if (!dev) { | ||
| 381 | retval = -ENOMEM; | ||
| 382 | goto error; | ||
| 383 | } | ||
| 384 | |||
| 385 | device_initialize(dev); | ||
| 386 | dev->parent = parent; | ||
| 387 | dev->groups = groups; | ||
| 388 | dev->release = device_create_release; | ||
| 389 | dev_set_drvdata(dev, drvdata); | ||
| 390 | |||
| 391 | retval = kobject_set_name_vargs(&dev->kobj, fmt, args); | ||
| 392 | if (retval) | ||
| 393 | goto error; | ||
| 394 | |||
| 395 | retval = device_add(dev); | ||
| 396 | if (retval) | ||
| 397 | goto error; | ||
| 398 | |||
| 399 | return dev; | ||
| 400 | |||
| 401 | error: | ||
| 402 | put_device(dev); | ||
| 403 | return ERR_PTR(retval); | ||
| 404 | } | ||
| 405 | |||
| 406 | struct device *cpu_device_create(struct device *parent, void *drvdata, | ||
| 407 | const struct attribute_group **groups, | ||
| 408 | const char *fmt, ...) | ||
| 409 | { | ||
| 410 | va_list vargs; | ||
| 411 | struct device *dev; | ||
| 412 | |||
| 413 | va_start(vargs, fmt); | ||
| 414 | dev = __cpu_device_create(parent, drvdata, groups, fmt, vargs); | ||
| 415 | va_end(vargs); | ||
| 416 | return dev; | ||
| 417 | } | ||
| 418 | EXPORT_SYMBOL_GPL(cpu_device_create); | ||
| 419 | |||
| 369 | #ifdef CONFIG_GENERIC_CPU_AUTOPROBE | 420 | #ifdef CONFIG_GENERIC_CPU_AUTOPROBE |
| 370 | static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); | 421 | static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); |
| 371 | #endif | 422 | #endif |
diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 96614b04544c..1bd120a0b084 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c | |||
| @@ -31,6 +31,11 @@ | |||
| 31 | #include <linux/fs.h> | 31 | #include <linux/fs.h> |
| 32 | #include <linux/workqueue.h> | 32 | #include <linux/workqueue.h> |
| 33 | 33 | ||
| 34 | static struct class devcd_class; | ||
| 35 | |||
| 36 | /* global disable flag, for security purposes */ | ||
| 37 | static bool devcd_disabled; | ||
| 38 | |||
| 34 | /* if data isn't read by userspace after 5 minutes then delete it */ | 39 | /* if data isn't read by userspace after 5 minutes then delete it */ |
| 35 | #define DEVCD_TIMEOUT (HZ * 60 * 5) | 40 | #define DEVCD_TIMEOUT (HZ * 60 * 5) |
| 36 | 41 | ||
| @@ -121,11 +126,51 @@ static const struct attribute_group *devcd_dev_groups[] = { | |||
| 121 | &devcd_dev_group, NULL, | 126 | &devcd_dev_group, NULL, |
| 122 | }; | 127 | }; |
| 123 | 128 | ||
| 129 | static int devcd_free(struct device *dev, void *data) | ||
| 130 | { | ||
| 131 | struct devcd_entry *devcd = dev_to_devcd(dev); | ||
| 132 | |||
| 133 | flush_delayed_work(&devcd->del_wk); | ||
| 134 | return 0; | ||
| 135 | } | ||
| 136 | |||
| 137 | static ssize_t disabled_show(struct class *class, struct class_attribute *attr, | ||
| 138 | char *buf) | ||
| 139 | { | ||
| 140 | return sprintf(buf, "%d\n", devcd_disabled); | ||
| 141 | } | ||
| 142 | |||
| 143 | static ssize_t disabled_store(struct class *class, struct class_attribute *attr, | ||
| 144 | const char *buf, size_t count) | ||
| 145 | { | ||
| 146 | long tmp = simple_strtol(buf, NULL, 10); | ||
| 147 | |||
| 148 | /* | ||
| 149 | * This essentially makes the attribute write-once, since you can't | ||
| 150 | * go back to not having it disabled. This is intentional, it serves | ||
| 151 | * as a system lockdown feature. | ||
| 152 | */ | ||
| 153 | if (tmp != 1) | ||
| 154 | return -EINVAL; | ||
| 155 | |||
| 156 | devcd_disabled = true; | ||
| 157 | |||
| 158 | class_for_each_device(&devcd_class, NULL, NULL, devcd_free); | ||
| 159 | |||
| 160 | return count; | ||
| 161 | } | ||
| 162 | |||
| 163 | static struct class_attribute devcd_class_attrs[] = { | ||
| 164 | __ATTR_RW(disabled), | ||
| 165 | __ATTR_NULL | ||
| 166 | }; | ||
| 167 | |||
| 124 | static struct class devcd_class = { | 168 | static struct class devcd_class = { |
| 125 | .name = "devcoredump", | 169 | .name = "devcoredump", |
| 126 | .owner = THIS_MODULE, | 170 | .owner = THIS_MODULE, |
| 127 | .dev_release = devcd_dev_release, | 171 | .dev_release = devcd_dev_release, |
| 128 | .dev_groups = devcd_dev_groups, | 172 | .dev_groups = devcd_dev_groups, |
| 173 | .class_attrs = devcd_class_attrs, | ||
| 129 | }; | 174 | }; |
| 130 | 175 | ||
| 131 | static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, | 176 | static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, |
| @@ -192,6 +237,9 @@ void dev_coredumpm(struct device *dev, struct module *owner, | |||
| 192 | struct devcd_entry *devcd; | 237 | struct devcd_entry *devcd; |
| 193 | struct device *existing; | 238 | struct device *existing; |
| 194 | 239 | ||
| 240 | if (devcd_disabled) | ||
| 241 | goto free; | ||
| 242 | |||
| 195 | existing = class_find_device(&devcd_class, NULL, dev, | 243 | existing = class_find_device(&devcd_class, NULL, dev, |
| 196 | devcd_match_failing); | 244 | devcd_match_failing); |
| 197 | if (existing) { | 245 | if (existing) { |
| @@ -249,14 +297,6 @@ static int __init devcoredump_init(void) | |||
| 249 | } | 297 | } |
| 250 | __initcall(devcoredump_init); | 298 | __initcall(devcoredump_init); |
| 251 | 299 | ||
| 252 | static int devcd_free(struct device *dev, void *data) | ||
| 253 | { | ||
| 254 | struct devcd_entry *devcd = dev_to_devcd(dev); | ||
| 255 | |||
| 256 | flush_delayed_work(&devcd->del_wk); | ||
| 257 | return 0; | ||
| 258 | } | ||
| 259 | |||
| 260 | static void __exit devcoredump_exit(void) | 300 | static void __exit devcoredump_exit(void) |
| 261 | { | 301 | { |
| 262 | class_for_each_device(&devcd_class, NULL, NULL, devcd_free); | 302 | class_for_each_device(&devcd_class, NULL, NULL, devcd_free); |
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 3d785ebb48d3..58470c395301 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c | |||
| @@ -591,8 +591,7 @@ static int fw_map_pages_buf(struct firmware_buf *buf) | |||
| 591 | if (!buf->is_paged_buf) | 591 | if (!buf->is_paged_buf) |
| 592 | return 0; | 592 | return 0; |
| 593 | 593 | ||
| 594 | if (buf->data) | 594 | vunmap(buf->data); |
| 595 | vunmap(buf->data); | ||
| 596 | buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); | 595 | buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); |
| 597 | if (!buf->data) | 596 | if (!buf->data) |
| 598 | return -ENOMEM; | 597 | return -ENOMEM; |
| @@ -925,7 +924,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, | |||
| 925 | kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); | 924 | kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD); |
| 926 | } | 925 | } |
| 927 | 926 | ||
| 928 | wait_for_completion(&buf->completion); | 927 | retval = wait_for_completion_interruptible(&buf->completion); |
| 929 | 928 | ||
| 930 | cancel_delayed_work_sync(&fw_priv->timeout_work); | 929 | cancel_delayed_work_sync(&fw_priv->timeout_work); |
| 931 | if (is_fw_load_aborted(buf)) | 930 | if (is_fw_load_aborted(buf)) |
| @@ -1004,7 +1003,7 @@ static int sync_cached_firmware_buf(struct firmware_buf *buf) | |||
| 1004 | break; | 1003 | break; |
| 1005 | } | 1004 | } |
| 1006 | mutex_unlock(&fw_lock); | 1005 | mutex_unlock(&fw_lock); |
| 1007 | wait_for_completion(&buf->completion); | 1006 | ret = wait_for_completion_interruptible(&buf->completion); |
| 1008 | mutex_lock(&fw_lock); | 1007 | mutex_lock(&fw_lock); |
| 1009 | } | 1008 | } |
| 1010 | mutex_unlock(&fw_lock); | 1009 | mutex_unlock(&fw_lock); |
diff --git a/drivers/base/node.c b/drivers/base/node.c index 472168cd0c97..a3b82e9c7f20 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c | |||
| @@ -25,32 +25,26 @@ static struct bus_type node_subsys = { | |||
| 25 | }; | 25 | }; |
| 26 | 26 | ||
| 27 | 27 | ||
| 28 | static ssize_t node_read_cpumap(struct device *dev, int type, char *buf) | 28 | static ssize_t node_read_cpumap(struct device *dev, bool list, char *buf) |
| 29 | { | 29 | { |
| 30 | struct node *node_dev = to_node(dev); | 30 | struct node *node_dev = to_node(dev); |
| 31 | const struct cpumask *mask = cpumask_of_node(node_dev->dev.id); | 31 | const struct cpumask *mask = cpumask_of_node(node_dev->dev.id); |
| 32 | int len; | ||
| 33 | 32 | ||
| 34 | /* 2008/04/07: buf currently PAGE_SIZE, need 9 chars per 32 bits. */ | 33 | /* 2008/04/07: buf currently PAGE_SIZE, need 9 chars per 32 bits. */ |
| 35 | BUILD_BUG_ON((NR_CPUS/32 * 9) > (PAGE_SIZE-1)); | 34 | BUILD_BUG_ON((NR_CPUS/32 * 9) > (PAGE_SIZE-1)); |
| 36 | 35 | ||
| 37 | len = type? | 36 | return cpumap_print_to_pagebuf(list, buf, mask); |
| 38 | cpulist_scnprintf(buf, PAGE_SIZE-2, mask) : | ||
| 39 | cpumask_scnprintf(buf, PAGE_SIZE-2, mask); | ||
| 40 | buf[len++] = '\n'; | ||
| 41 | buf[len] = '\0'; | ||
| 42 | return len; | ||
| 43 | } | 37 | } |
| 44 | 38 | ||
| 45 | static inline ssize_t node_read_cpumask(struct device *dev, | 39 | static inline ssize_t node_read_cpumask(struct device *dev, |
| 46 | struct device_attribute *attr, char *buf) | 40 | struct device_attribute *attr, char *buf) |
| 47 | { | 41 | { |
| 48 | return node_read_cpumap(dev, 0, buf); | 42 | return node_read_cpumap(dev, false, buf); |
| 49 | } | 43 | } |
| 50 | static inline ssize_t node_read_cpulist(struct device *dev, | 44 | static inline ssize_t node_read_cpulist(struct device *dev, |
| 51 | struct device_attribute *attr, char *buf) | 45 | struct device_attribute *attr, char *buf) |
| 52 | { | 46 | { |
| 53 | return node_read_cpumap(dev, 1, buf); | 47 | return node_read_cpumap(dev, true, buf); |
| 54 | } | 48 | } |
| 55 | 49 | ||
| 56 | static DEVICE_ATTR(cpumap, S_IRUGO, node_read_cpumask, NULL); | 50 | static DEVICE_ATTR(cpumap, S_IRUGO, node_read_cpumask, NULL); |
diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 233ececd15a3..9421fed40905 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c | |||
| @@ -580,9 +580,10 @@ void platform_driver_unregister(struct platform_driver *drv) | |||
| 580 | EXPORT_SYMBOL_GPL(platform_driver_unregister); | 580 | EXPORT_SYMBOL_GPL(platform_driver_unregister); |
| 581 | 581 | ||
| 582 | /** | 582 | /** |
| 583 | * platform_driver_probe - register driver for non-hotpluggable device | 583 | * __platform_driver_probe - register driver for non-hotpluggable device |
| 584 | * @drv: platform driver structure | 584 | * @drv: platform driver structure |
| 585 | * @probe: the driver probe routine, probably from an __init section | 585 | * @probe: the driver probe routine, probably from an __init section |
| 586 | * @module: module which will be the owner of the driver | ||
| 586 | * | 587 | * |
| 587 | * Use this instead of platform_driver_register() when you know the device | 588 | * Use this instead of platform_driver_register() when you know the device |
| 588 | * is not hotpluggable and has already been registered, and you want to | 589 | * is not hotpluggable and has already been registered, and you want to |
| @@ -598,8 +599,8 @@ EXPORT_SYMBOL_GPL(platform_driver_unregister); | |||
| 598 | * Returns zero if the driver registered and bound to a device, else returns | 599 | * Returns zero if the driver registered and bound to a device, else returns |
| 599 | * a negative error code and with the driver not registered. | 600 | * a negative error code and with the driver not registered. |
| 600 | */ | 601 | */ |
| 601 | int __init_or_module platform_driver_probe(struct platform_driver *drv, | 602 | int __init_or_module __platform_driver_probe(struct platform_driver *drv, |
| 602 | int (*probe)(struct platform_device *)) | 603 | int (*probe)(struct platform_device *), struct module *module) |
| 603 | { | 604 | { |
| 604 | int retval, code; | 605 | int retval, code; |
| 605 | 606 | ||
| @@ -614,7 +615,7 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv, | |||
| 614 | 615 | ||
| 615 | /* temporary section violation during probe() */ | 616 | /* temporary section violation during probe() */ |
| 616 | drv->probe = probe; | 617 | drv->probe = probe; |
| 617 | retval = code = platform_driver_register(drv); | 618 | retval = code = __platform_driver_register(drv, module); |
| 618 | 619 | ||
| 619 | /* | 620 | /* |
| 620 | * Fixup that section violation, being paranoid about code scanning | 621 | * Fixup that section violation, being paranoid about code scanning |
| @@ -633,27 +634,28 @@ int __init_or_module platform_driver_probe(struct platform_driver *drv, | |||
| 633 | platform_driver_unregister(drv); | 634 | platform_driver_unregister(drv); |
| 634 | return retval; | 635 | return retval; |
| 635 | } | 636 | } |
| 636 | EXPORT_SYMBOL_GPL(platform_driver_probe); | 637 | EXPORT_SYMBOL_GPL(__platform_driver_probe); |
| 637 | 638 | ||
| 638 | /** | 639 | /** |
| 639 | * platform_create_bundle - register driver and create corresponding device | 640 | * __platform_create_bundle - register driver and create corresponding device |
| 640 | * @driver: platform driver structure | 641 | * @driver: platform driver structure |
| 641 | * @probe: the driver probe routine, probably from an __init section | 642 | * @probe: the driver probe routine, probably from an __init section |
| 642 | * @res: set of resources that needs to be allocated for the device | 643 | * @res: set of resources that needs to be allocated for the device |
| 643 | * @n_res: number of resources | 644 | * @n_res: number of resources |
| 644 | * @data: platform specific data for this platform device | 645 | * @data: platform specific data for this platform device |
| 645 | * @size: size of platform specific data | 646 | * @size: size of platform specific data |
| 647 | * @module: module which will be the owner of the driver | ||
| 646 | * | 648 | * |
| 647 | * Use this in legacy-style modules that probe hardware directly and | 649 | * Use this in legacy-style modules that probe hardware directly and |
| 648 | * register a single platform device and corresponding platform driver. | 650 | * register a single platform device and corresponding platform driver. |
| 649 | * | 651 | * |
| 650 | * Returns &struct platform_device pointer on success, or ERR_PTR() on error. | 652 | * Returns &struct platform_device pointer on success, or ERR_PTR() on error. |
| 651 | */ | 653 | */ |
| 652 | struct platform_device * __init_or_module platform_create_bundle( | 654 | struct platform_device * __init_or_module __platform_create_bundle( |
| 653 | struct platform_driver *driver, | 655 | struct platform_driver *driver, |
| 654 | int (*probe)(struct platform_device *), | 656 | int (*probe)(struct platform_device *), |
| 655 | struct resource *res, unsigned int n_res, | 657 | struct resource *res, unsigned int n_res, |
| 656 | const void *data, size_t size) | 658 | const void *data, size_t size, struct module *module) |
| 657 | { | 659 | { |
| 658 | struct platform_device *pdev; | 660 | struct platform_device *pdev; |
| 659 | int error; | 661 | int error; |
| @@ -676,7 +678,7 @@ struct platform_device * __init_or_module platform_create_bundle( | |||
| 676 | if (error) | 678 | if (error) |
| 677 | goto err_pdev_put; | 679 | goto err_pdev_put; |
| 678 | 680 | ||
| 679 | error = platform_driver_probe(driver, probe); | 681 | error = __platform_driver_probe(driver, probe, module); |
| 680 | if (error) | 682 | if (error) |
| 681 | goto err_pdev_del; | 683 | goto err_pdev_del; |
| 682 | 684 | ||
| @@ -689,7 +691,7 @@ err_pdev_put: | |||
| 689 | err_out: | 691 | err_out: |
| 690 | return ERR_PTR(error); | 692 | return ERR_PTR(error); |
| 691 | } | 693 | } |
| 692 | EXPORT_SYMBOL_GPL(platform_create_bundle); | 694 | EXPORT_SYMBOL_GPL(__platform_create_bundle); |
| 693 | 695 | ||
| 694 | /* modalias support enables more hands-off userspace setup: | 696 | /* modalias support enables more hands-off userspace setup: |
| 695 | * (a) environment variable lets new-style hotplug events work once system is | 697 | * (a) environment variable lets new-style hotplug events work once system is |
diff --git a/drivers/base/topology.c b/drivers/base/topology.c index be7c1fb7c0c9..6491f45200a7 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c | |||
| @@ -29,75 +29,52 @@ | |||
| 29 | #include <linux/hardirq.h> | 29 | #include <linux/hardirq.h> |
| 30 | #include <linux/topology.h> | 30 | #include <linux/topology.h> |
| 31 | 31 | ||
| 32 | #define define_one_ro_named(_name, _func) \ | ||
| 33 | static DEVICE_ATTR(_name, 0444, _func, NULL) | ||
| 34 | |||
| 35 | #define define_one_ro(_name) \ | ||
| 36 | static DEVICE_ATTR(_name, 0444, show_##_name, NULL) | ||
| 37 | |||
| 38 | #define define_id_show_func(name) \ | 32 | #define define_id_show_func(name) \ |
| 39 | static ssize_t show_##name(struct device *dev, \ | 33 | static ssize_t name##_show(struct device *dev, \ |
| 40 | struct device_attribute *attr, char *buf) \ | 34 | struct device_attribute *attr, char *buf) \ |
| 41 | { \ | 35 | { \ |
| 42 | return sprintf(buf, "%d\n", topology_##name(dev->id)); \ | 36 | return sprintf(buf, "%d\n", topology_##name(dev->id)); \ |
| 43 | } | 37 | } |
| 44 | 38 | ||
| 45 | #if defined(topology_thread_cpumask) || defined(topology_core_cpumask) || \ | 39 | #define define_siblings_show_map(name, mask) \ |
| 46 | defined(topology_book_cpumask) | 40 | static ssize_t name##_show(struct device *dev, \ |
| 47 | static ssize_t show_cpumap(int type, const struct cpumask *mask, char *buf) | ||
| 48 | { | ||
| 49 | ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf; | ||
| 50 | int n = 0; | ||
| 51 | |||
| 52 | if (len > 1) { | ||
| 53 | n = type? | ||
| 54 | cpulist_scnprintf(buf, len-2, mask) : | ||
| 55 | cpumask_scnprintf(buf, len-2, mask); | ||
| 56 | buf[n++] = '\n'; | ||
| 57 | buf[n] = '\0'; | ||
| 58 | } | ||
| 59 | return n; | ||
| 60 | } | ||
| 61 | #endif | ||
| 62 | |||
| 63 | #define define_siblings_show_map(name) \ | ||
| 64 | static ssize_t show_##name(struct device *dev, \ | ||
| 65 | struct device_attribute *attr, char *buf) \ | 41 | struct device_attribute *attr, char *buf) \ |
| 66 | { \ | 42 | { \ |
| 67 | return show_cpumap(0, topology_##name(dev->id), buf); \ | 43 | return cpumap_print_to_pagebuf(false, buf, topology_##mask(dev->id));\ |
| 68 | } | 44 | } |
| 69 | 45 | ||
| 70 | #define define_siblings_show_list(name) \ | 46 | #define define_siblings_show_list(name, mask) \ |
| 71 | static ssize_t show_##name##_list(struct device *dev, \ | 47 | static ssize_t name##_list_show(struct device *dev, \ |
| 72 | struct device_attribute *attr, \ | 48 | struct device_attribute *attr, \ |
| 73 | char *buf) \ | 49 | char *buf) \ |
| 74 | { \ | 50 | { \ |
| 75 | return show_cpumap(1, topology_##name(dev->id), buf); \ | 51 | return cpumap_print_to_pagebuf(true, buf, topology_##mask(dev->id));\ |
| 76 | } | 52 | } |
| 77 | 53 | ||
| 78 | #define define_siblings_show_func(name) \ | 54 | #define define_siblings_show_func(name, mask) \ |
| 79 | define_siblings_show_map(name); define_siblings_show_list(name) | 55 | define_siblings_show_map(name, mask); \ |
| 56 | define_siblings_show_list(name, mask) | ||
| 80 | 57 | ||
| 81 | define_id_show_func(physical_package_id); | 58 | define_id_show_func(physical_package_id); |
| 82 | define_one_ro(physical_package_id); | 59 | static DEVICE_ATTR_RO(physical_package_id); |
| 83 | 60 | ||
| 84 | define_id_show_func(core_id); | 61 | define_id_show_func(core_id); |
| 85 | define_one_ro(core_id); | 62 | static DEVICE_ATTR_RO(core_id); |
| 86 | 63 | ||
| 87 | define_siblings_show_func(thread_cpumask); | 64 | define_siblings_show_func(thread_siblings, thread_cpumask); |
| 88 | define_one_ro_named(thread_siblings, show_thread_cpumask); | 65 | static DEVICE_ATTR_RO(thread_siblings); |
| 89 | define_one_ro_named(thread_siblings_list, show_thread_cpumask_list); | 66 | static DEVICE_ATTR_RO(thread_siblings_list); |
| 90 | 67 | ||
| 91 | define_siblings_show_func(core_cpumask); | 68 | define_siblings_show_func(core_siblings, core_cpumask); |
| 92 | define_one_ro_named(core_siblings, show_core_cpumask); | 69 | static DEVICE_ATTR_RO(core_siblings); |
| 93 | define_one_ro_named(core_siblings_list, show_core_cpumask_list); | 70 | static DEVICE_ATTR_RO(core_siblings_list); |
| 94 | 71 | ||
| 95 | #ifdef CONFIG_SCHED_BOOK | 72 | #ifdef CONFIG_SCHED_BOOK |
| 96 | define_id_show_func(book_id); | 73 | define_id_show_func(book_id); |
| 97 | define_one_ro(book_id); | 74 | static DEVICE_ATTR_RO(book_id); |
| 98 | define_siblings_show_func(book_cpumask); | 75 | define_siblings_show_func(book_siblings, book_cpumask); |
| 99 | define_one_ro_named(book_siblings, show_book_cpumask); | 76 | static DEVICE_ATTR_RO(book_siblings); |
| 100 | define_one_ro_named(book_siblings_list, show_book_cpumask_list); | 77 | static DEVICE_ATTR_RO(book_siblings_list); |
| 101 | #endif | 78 | #endif |
| 102 | 79 | ||
| 103 | static struct attribute *default_attrs[] = { | 80 | static struct attribute *default_attrs[] = { |
