diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index da95513571ea3e3e53263f6c91588fb58d50f3fb..b683e8ee69ecc59a353c700330b7162f50652123 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -243,7 +243,7 @@ Description: Parameters for the CPU cache attributes coherency_line_size: the minimum amount of data in bytes that gets transferred from memory to cache - level: the cache hierarcy in the multi-level cache configuration + level: the cache hierarchy in the multi-level cache configuration number_of_sets: total number of sets in the cache, a set is a collection of cache lines with the same cache index diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c84d078a6376159f4d28ea71b2550a20abdb8c63..1e18efe3a4ed82853ce8078761aeb86dfd4aecfb 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -953,6 +953,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted. auto selects the default scheme, which automatically enables eagerfpu restore for xsaveopt. + module.async_probe [KNL] + Enable asynchronous probe on this module. + early_ioremap_debug [KNL] Enable debug messages in early_ioremap support. This is useful for tracking down temporary early mappings diff --git a/MAINTAINERS b/MAINTAINERS index 161747bdecf341bcadd3082b415089111a660d8d..e4b32dcc2494b9690992cc9d264f78fe4980601a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3450,16 +3450,17 @@ F: drivers/block/drbd/ F: lib/lru_cache.c F: Documentation/blockdev/drbd/ -DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS +DRIVER CORE, KOBJECTS, DEBUGFS, KERNFS AND SYSFS M: Greg Kroah-Hartman T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git S: Supported F: Documentation/kobject.txt F: drivers/base/ -F: fs/sysfs/ F: fs/debugfs/ -F: include/linux/kobj* +F: fs/kernfs/ +F: fs/sysfs/ F: include/linux/debugfs.h +F: include/linux/kobj* F: lib/kobj* DRM DRIVERS diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 38bd5d998c81aadda711b03093a9b854e1018cc8..1f614d778a8b5ffc2b1cf32465fce3a579839f3a 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -336,7 +336,7 @@ int alloc_bootmem_huge_page(struct hstate *hstate) unsigned long gpage_npages[MMU_PAGE_COUNT]; static int __init do_gpage_early_setup(char *param, char *val, - const char *unused) + const char *unused, void *arg) { static phys_addr_t size; unsigned long npages; @@ -385,7 +385,7 @@ void __init reserve_hugetlb_gpages(void) strlcpy(cmdline, boot_command_line, COMMAND_LINE_SIZE); parse_args("hugetlb gpages", cmdline, NULL, 0, 0, 0, - &do_gpage_early_setup); + NULL, &do_gpage_early_setup); /* * Walk gpage list in reverse, allocating larger page sizes first. diff --git a/drivers/base/base.h b/drivers/base/base.h index 251c5d30f963060d3fec65b61205ae0225f141a5..fd3347d9f15351c4e859816c43b69bb9f59d5c98 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -116,6 +116,7 @@ static inline int driver_match_device(struct device_driver *drv, { return drv->bus->match ? drv->bus->match(dev, drv) : 1; } +extern bool driver_allows_async_probing(struct device_driver *drv); extern int driver_add_groups(struct device_driver *drv, const struct attribute_group **groups); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 79bc203f51ef08511eff94661cc77fac163475d5..500592486e8832cfd61673302359cc352b51efa0 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -10,6 +10,7 @@ * */ +#include #include #include #include @@ -549,15 +550,12 @@ void bus_probe_device(struct device *dev) { struct bus_type *bus = dev->bus; struct subsys_interface *sif; - int ret; if (!bus) return; - if (bus->p->drivers_autoprobe) { - ret = device_attach(dev); - WARN_ON(ret < 0); - } + if (bus->p->drivers_autoprobe) + device_initial_probe(dev); mutex_lock(&bus->p->mutex); list_for_each_entry(sif, &bus->p->interfaces, node) @@ -659,6 +657,17 @@ static ssize_t uevent_store(struct device_driver *drv, const char *buf, } static DRIVER_ATTR_WO(uevent); +static void driver_attach_async(void *_drv, async_cookie_t cookie) +{ + struct device_driver *drv = _drv; + int ret; + + ret = driver_attach(drv); + + pr_debug("bus: '%s': driver %s async attach completed: %d\n", + drv->bus->name, drv->name, ret); +} + /** * bus_add_driver - Add a driver to the bus. * @drv: driver. @@ -691,9 +700,15 @@ int bus_add_driver(struct device_driver *drv) klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); if (drv->bus->p->drivers_autoprobe) { - error = driver_attach(drv); - if (error) - goto out_unregister; + if (driver_allows_async_probing(drv)) { + pr_debug("bus: '%s': probing driver %s asynchronously\n", + drv->bus->name, drv->name); + async_schedule(driver_attach_async, drv); + } else { + error = driver_attach(drv); + if (error) + goto out_unregister; + } } module_add_driver(drv->owner, drv); diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index df0c66cb7ad3719016436dd7eb16ab1d3234568d..764280a917761e9bf3360111bb92709d676c69e4 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -191,12 +191,12 @@ static int detect_cache_attributes(unsigned int cpu) if (ret) goto free_ci; /* - * For systems using DT for cache hierarcy, of_node and shared_cpu_map + * For systems using DT for cache hierarchy, of_node and shared_cpu_map * will be set up here only if they are not populated already */ ret = cache_shared_cpu_map_setup(cpu); if (ret) { - pr_warn("Unable to detect cache hierarcy from DT for CPU %d\n", + pr_warn("Unable to detect cache hierarchy from DT for CPU %d\n", cpu); goto free_ci; } diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index f160ea44a86d68a26b689b5ff7c5f0bcaf5ec74e..78720e70617634c6301339510564ac6ed4380455 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "base.h" @@ -265,6 +266,30 @@ static ssize_t print_cpus_offline(struct device *dev, } static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL); +static ssize_t print_cpus_isolated(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int n = 0, len = PAGE_SIZE-2; + + n = scnprintf(buf, len, "%*pbl\n", cpumask_pr_args(cpu_isolated_map)); + + return n; +} +static DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL); + +#ifdef CONFIG_NO_HZ_FULL +static ssize_t print_cpus_nohz_full(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int n = 0, len = PAGE_SIZE-2; + + n = scnprintf(buf, len, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask)); + + return n; +} +static DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL); +#endif + static void cpu_device_release(struct device *dev) { /* @@ -431,6 +456,10 @@ static struct attribute *cpu_root_attrs[] = { &cpu_attrs[2].attr.attr, &dev_attr_kernel_max.attr, &dev_attr_offline.attr, + &dev_attr_isolated.attr, +#ifdef CONFIG_NO_HZ_FULL + &dev_attr_nohz_full.attr, +#endif #ifdef CONFIG_GENERIC_CPU_AUTOPROBE &dev_attr_modalias.attr, #endif diff --git a/drivers/base/dd.c b/drivers/base/dd.c index e843fdbe492514d83fd1f66cfc8678b10099e877..a638bbb1a27a573f7d959c20d459c73a368e8794 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -141,7 +141,7 @@ static bool driver_deferred_probe_enable = false; * more than one device is probing at the same time, it is possible for one * probe to complete successfully while another is about to defer. If the second * depends on the first, then it will get put on the pending list after the - * trigger event has already occured and will be stuck there. + * trigger event has already occurred and will be stuck there. * * The atomic 'deferred_trigger_count' is used to determine if a successful * trigger has occurred in the midst of probing a driver. If the trigger count @@ -417,31 +417,107 @@ int driver_probe_device(struct device_driver *drv, struct device *dev) return ret; } -static int __device_attach(struct device_driver *drv, void *data) +bool driver_allows_async_probing(struct device_driver *drv) { - struct device *dev = data; + switch (drv->probe_type) { + case PROBE_PREFER_ASYNCHRONOUS: + return true; + + case PROBE_FORCE_SYNCHRONOUS: + return false; + + default: + if (module_requested_async_probing(drv->owner)) + return true; + + return false; + } +} + +struct device_attach_data { + struct device *dev; + + /* + * Indicates whether we are are considering asynchronous probing or + * not. Only initial binding after device or driver registration + * (including deferral processing) may be done asynchronously, the + * rest is always synchronous, as we expect it is being done by + * request from userspace. + */ + bool check_async; + + /* + * Indicates if we are binding synchronous or asynchronous drivers. + * When asynchronous probing is enabled we'll execute 2 passes + * over drivers: first pass doing synchronous probing and second + * doing asynchronous probing (if synchronous did not succeed - + * most likely because there was no driver requiring synchronous + * probing - and we found asynchronous driver during first pass). + * The 2 passes are done because we can't shoot asynchronous + * probe for given device and driver from bus_for_each_drv() since + * driver pointer is not guaranteed to stay valid once + * bus_for_each_drv() iterates to the next driver on the bus. + */ + bool want_async; + + /* + * We'll set have_async to 'true' if, while scanning for matching + * driver, we'll encounter one that requests asynchronous probing. + */ + bool have_async; +}; + +static int __device_attach_driver(struct device_driver *drv, void *_data) +{ + struct device_attach_data *data = _data; + struct device *dev = data->dev; + bool async_allowed; + + /* + * Check if device has already been claimed. This may + * happen with driver loading, device discovery/registration, + * and deferred probe processing happens all at once with + * multiple threads. + */ + if (dev->driver) + return -EBUSY; if (!driver_match_device(drv, dev)) return 0; + async_allowed = driver_allows_async_probing(drv); + + if (async_allowed) + data->have_async = true; + + if (data->check_async && async_allowed != data->want_async) + return 0; + return driver_probe_device(drv, dev); } -/** - * device_attach - try to attach device to a driver. - * @dev: device. - * - * Walk the list of drivers that the bus has and call - * driver_probe_device() for each pair. If a compatible - * pair is found, break out and return. - * - * Returns 1 if the device was bound to a driver; - * 0 if no matching driver was found; - * -ENODEV if the device is not registered. - * - * When called for a USB interface, @dev->parent lock must be held. - */ -int device_attach(struct device *dev) +static void __device_attach_async_helper(void *_dev, async_cookie_t cookie) +{ + struct device *dev = _dev; + struct device_attach_data data = { + .dev = dev, + .check_async = true, + .want_async = true, + }; + + device_lock(dev); + + bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); + dev_dbg(dev, "async probe completed\n"); + + pm_request_idle(dev); + + device_unlock(dev); + + put_device(dev); +} + +static int __device_attach(struct device *dev, bool allow_async) { int ret = 0; @@ -459,15 +535,59 @@ int device_attach(struct device *dev) ret = 0; } } else { - ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); - pm_request_idle(dev); + struct device_attach_data data = { + .dev = dev, + .check_async = allow_async, + .want_async = false, + }; + + ret = bus_for_each_drv(dev->bus, NULL, &data, + __device_attach_driver); + if (!ret && allow_async && data.have_async) { + /* + * If we could not find appropriate driver + * synchronously and we are allowed to do + * async probes and there are drivers that + * want to probe asynchronously, we'll + * try them. + */ + dev_dbg(dev, "scheduling asynchronous probe\n"); + get_device(dev); + async_schedule(__device_attach_async_helper, dev); + } else { + pm_request_idle(dev); + } } out_unlock: device_unlock(dev); return ret; } + +/** + * device_attach - try to attach device to a driver. + * @dev: device. + * + * Walk the list of drivers that the bus has and call + * driver_probe_device() for each pair. If a compatible + * pair is found, break out and return. + * + * Returns 1 if the device was bound to a driver; + * 0 if no matching driver was found; + * -ENODEV if the device is not registered. + * + * When called for a USB interface, @dev->parent lock must be held. + */ +int device_attach(struct device *dev) +{ + return __device_attach(dev, false); +} EXPORT_SYMBOL_GPL(device_attach); +void device_initial_probe(struct device *dev) +{ + __device_attach(dev, true); +} + static int __driver_attach(struct device *dev, void *data) { struct device_driver *drv = data; @@ -522,6 +642,9 @@ static void __device_release_driver(struct device *dev) drv = dev->driver; if (drv) { + if (driver_allows_async_probing(drv)) + async_synchronize_full(); + pm_runtime_get_sync(dev); driver_sysfs_remove(dev); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 171841ad10089feef1aea6e6035327a68e745604..9c4288362a8eaab0825d489c6362ceb573cbad30 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -150,17 +150,17 @@ struct firmware_buf { int page_array_size; struct list_head pending_list; #endif - char fw_id[]; + const char *fw_id; }; struct fw_cache_entry { struct list_head list; - char name[]; + const char *name; }; struct fw_name_devm { unsigned long magic; - char name[]; + const char *name; }; #define to_fwbuf(d) container_of(d, struct firmware_buf, ref) @@ -181,13 +181,17 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name, { struct firmware_buf *buf; - buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1, GFP_ATOMIC); - + buf = kzalloc(sizeof(*buf), GFP_ATOMIC); if (!buf) - return buf; + return NULL; + + buf->fw_id = kstrdup_const(fw_name, GFP_ATOMIC); + if (!buf->fw_id) { + kfree(buf); + return NULL; + } kref_init(&buf->ref); - strcpy(buf->fw_id, fw_name); buf->fwc = fwc; init_completion(&buf->completion); #ifdef CONFIG_FW_LOADER_USER_HELPER @@ -257,6 +261,7 @@ static void __fw_free_buf(struct kref *ref) } else #endif vfree(buf->data); + kfree_const(buf->fw_id); kfree(buf); } @@ -320,9 +325,13 @@ static int fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf) static int fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf) { - int i; + int i, len; int rc = -ENOENT; - char *path = __getname(); + char *path; + + path = __getname(); + if (!path) + return -ENOMEM; for (i = 0; i < ARRAY_SIZE(fw_path); i++) { struct file *file; @@ -331,7 +340,12 @@ static int fw_get_filesystem_firmware(struct device *device, if (!fw_path[i][0]) continue; - snprintf(path, PATH_MAX, "%s/%s", fw_path[i], buf->fw_id); + len = snprintf(path, PATH_MAX, "%s/%s", + fw_path[i], buf->fw_id); + if (len >= PATH_MAX) { + rc = -ENAMETOOLONG; + break; + } file = filp_open(path, O_RDONLY, 0); if (IS_ERR(file)) @@ -392,6 +406,7 @@ static void fw_name_devm_release(struct device *dev, void *res) if (fwn->magic == (unsigned long)&fw_cache) pr_debug("%s: fw_name-%s devm-%p released\n", __func__, fwn->name, res); + kfree_const(fwn->name); } static int fw_devm_match(struct device *dev, void *res, @@ -422,13 +437,17 @@ static int fw_add_devm_name(struct device *dev, const char *name) if (fwn) return 1; - fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm) + - strlen(name) + 1, GFP_KERNEL); + fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm), + GFP_KERNEL); if (!fwn) return -ENOMEM; + fwn->name = kstrdup_const(name, GFP_KERNEL); + if (!fwn->name) { + kfree(fwn); + return -ENOMEM; + } fwn->magic = (unsigned long)&fw_cache; - strcpy(fwn->name, name); devres_add(dev, fwn); return 0; @@ -1247,6 +1266,7 @@ static void request_firmware_work_func(struct work_struct *work) put_device(fw_work->device); /* taken in request_firmware_nowait() */ module_put(fw_work->module); + kfree_const(fw_work->name); kfree(fw_work); } @@ -1286,7 +1306,11 @@ request_firmware_nowait( return -ENOMEM; fw_work->module = module; - fw_work->name = name; + fw_work->name = kstrdup_const(name, gfp); + if (!fw_work->name) { + kfree(fw_work); + return -ENOMEM; + } fw_work->device = device; fw_work->context = context; fw_work->cont = cont; @@ -1294,6 +1318,7 @@ request_firmware_nowait( (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER); if (!try_module_get(module)) { + kfree_const(fw_work->name); kfree(fw_work); return -EFAULT; } @@ -1384,11 +1409,16 @@ static struct fw_cache_entry *alloc_fw_cache_entry(const char *name) { struct fw_cache_entry *fce; - fce = kzalloc(sizeof(*fce) + strlen(name) + 1, GFP_ATOMIC); + fce = kzalloc(sizeof(*fce), GFP_ATOMIC); if (!fce) goto exit; - strcpy(fce->name, name); + fce->name = kstrdup_const(name, GFP_ATOMIC); + if (!fce->name) { + kfree(fce); + fce = NULL; + goto exit; + } exit: return fce; } @@ -1428,6 +1458,7 @@ static int fw_cache_piggyback_on_request(const char *name) static void free_fw_cache_entry(struct fw_cache_entry *fce) { + kfree_const(fce->name); kfree(fce); } diff --git a/drivers/base/platform.c b/drivers/base/platform.c index ebf034b97278592ab57cbd24ef0f53d3cfc56e0a..063f0ab152590dfeea4488d2686b8ff13b99a7bf 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -613,6 +613,19 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv, { int retval, code; + if (drv->driver.probe_type == PROBE_PREFER_ASYNCHRONOUS) { + pr_err("%s: drivers registered with %s can not be probed asynchronously\n", + drv->driver.name, __func__); + return -EINVAL; + } + + /* + * We have to run our probes synchronously because we check if + * we find any devices to bind to and exit with error if there + * are any. + */ + drv->driver.probe_type = PROBE_FORCE_SYNCHRONOUS; + /* * Prevent driver from requesting probe deferral to avoid further * futile probe attempts. diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 92772fffc52ff292fc38e8acd09bf0002d332c8b..73aea40a9c89592035e08ecf98292dc9aab9df62 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2964,6 +2964,7 @@ static struct pci_driver amd64_pci_driver = { .probe = probe_one_instance, .remove = remove_one_instance, .id_table = amd64_pci_table, + .driver.probe_type = PROBE_FORCE_SYNCHRONOUS, }; static void setup_pci_device(void) diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 2bacb99885665f3a0fae9579a569128195d86fd4..7247252ee9b1beffa3c624cb54db676a707fffe2 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -785,7 +785,6 @@ static unsigned int kernfs_fop_poll(struct file *filp, poll_table *wait) struct kernfs_node *kn = filp->f_path.dentry->d_fsdata; struct kernfs_open_node *on = kn->attr.open; - /* need parent for the kobj, grab both */ if (!kernfs_get_active(kn)) goto trigger; diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 7c2867b44141e5df54734e7e27e3df344a976dea..6c95628ea377dc0d4c07cbb7979f2a280dd3e6a8 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -90,7 +90,7 @@ static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf, return 0; if (size) { - if (pos > size) + if (pos >= size) return 0; if (pos + count > size) count = size - pos; diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index b400c04371f0d08c980b54183938b36afa47c1ec..39a01993676864e56b55f8125155646f808f4f40 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -135,7 +135,7 @@ static int internal_create_group(struct kobject *kobj, int update, * This function creates a group for the first time. It will explicitly * warn and error if any of the attribute files being created already exist. * - * Returns 0 on success or error. + * Returns 0 on success or error code on failure. */ int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) @@ -155,7 +155,7 @@ EXPORT_SYMBOL_GPL(sysfs_create_group); * It will explicitly warn and error if any of the attribute files being * created already exist. * - * Returns 0 on success or error code from sysfs_create_group on error. + * Returns 0 on success or error code from sysfs_create_group on failure. */ int sysfs_create_groups(struct kobject *kobj, const struct attribute_group **groups) @@ -193,7 +193,7 @@ EXPORT_SYMBOL_GPL(sysfs_create_groups); * The primary use for this function is to call it after making a change * that affects group visibility. * - * Returns 0 on success or error. + * Returns 0 on success or error code on failure. */ int sysfs_update_group(struct kobject *kobj, const struct attribute_group *grp) diff --git a/include/linux/cacheinfo.h b/include/linux/cacheinfo.h index 3daf5ed392c9a5e40fc99a7e587ee318403dbc06..2189935075b48e3d5b06012f9279f6eea4973fa7 100644 --- a/include/linux/cacheinfo.h +++ b/include/linux/cacheinfo.h @@ -19,7 +19,7 @@ enum cache_type { /** * struct cacheinfo - represent a cache leaf node * @type: type of the cache - data, inst or unified - * @level: represents the hierarcy in the multi-level cache + * @level: represents the hierarchy in the multi-level cache * @coherency_line_size: size of each cache line usually representing * the minimum amount of data that gets transferred from memory * @number_of_sets: total number of sets, a set is a collection of cache diff --git a/include/linux/device.h b/include/linux/device.h index 6558af90c8fe3b9263441dc401b3689940d3453e..00ac57c26615103b0983040981cac8012c9e2f99 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -195,6 +195,34 @@ extern int bus_unregister_notifier(struct bus_type *bus, extern struct kset *bus_get_kset(struct bus_type *bus); extern struct klist *bus_get_device_klist(struct bus_type *bus); +/** + * enum probe_type - device driver probe type to try + * Device drivers may opt in for special handling of their + * respective probe routines. This tells the core what to + * expect and prefer. + * + * @PROBE_DEFAULT_STRATEGY: Used by drivers that work equally well + * whether probed synchronously or asynchronously. + * @PROBE_PREFER_ASYNCHRONOUS: Drivers for "slow" devices which + * probing order is not essential for booting the system may + * opt into executing their probes asynchronously. + * @PROBE_FORCE_SYNCHRONOUS: Use this to annotate drivers that need + * their probe routines to run synchronously with driver and + * device registration (with the exception of -EPROBE_DEFER + * handling - re-probing always ends up being done asynchronously). + * + * Note that the end goal is to switch the kernel to use asynchronous + * probing by default, so annotating drivers with + * %PROBE_PREFER_ASYNCHRONOUS is a temporary measure that allows us + * to speed up boot process while we are validating the rest of the + * drivers. + */ +enum probe_type { + PROBE_DEFAULT_STRATEGY, + PROBE_PREFER_ASYNCHRONOUS, + PROBE_FORCE_SYNCHRONOUS, +}; + /** * struct device_driver - The basic device driver structure * @name: Name of the device driver. @@ -202,6 +230,7 @@ extern struct klist *bus_get_device_klist(struct bus_type *bus); * @owner: The module owner. * @mod_name: Used for built-in modules. * @suppress_bind_attrs: Disables bind/unbind via sysfs. + * @probe_type: Type of the probe (synchronous or asynchronous) to use. * @of_match_table: The open firmware table. * @acpi_match_table: The ACPI match table. * @probe: Called to query the existence of a specific device, @@ -235,6 +264,7 @@ struct device_driver { const char *mod_name; /* used for built-in modules */ bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ + enum probe_type probe_type; const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_match_table; @@ -975,6 +1005,7 @@ extern int __must_check device_bind_driver(struct device *dev); extern void device_release_driver(struct device *dev); extern int __must_check device_attach(struct device *dev); extern int __must_check driver_attach(struct device_driver *drv); +extern void device_initial_probe(struct device *dev); extern int __must_check device_reprobe(struct device *dev); /* diff --git a/include/linux/module.h b/include/linux/module.h index 255fca74de7dbebe02d44a0a5946217ae20f3732..7ffe0851d24438876faa9d756178d1654c7e825e 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -257,6 +257,8 @@ struct module { bool sig_ok; #endif + bool async_probe_requested; + /* symbols that will be GPL-only in the near future. */ const struct kernel_symbol *gpl_future_syms; const unsigned long *gpl_future_crcs; @@ -508,6 +510,11 @@ int unregister_module_notifier(struct notifier_block *nb); extern void print_modules(void); +static inline bool module_requested_async_probing(struct module *module) +{ + return module && module->async_probe_requested; +} + #else /* !CONFIG_MODULES... */ /* Given an address, look for it in the exception tables. */ @@ -618,6 +625,12 @@ static inline int unregister_module_notifier(struct notifier_block *nb) static inline void print_modules(void) { } + +static inline bool module_requested_async_probing(struct module *module) +{ + return false; +} + #endif /* CONFIG_MODULES */ #ifdef CONFIG_SYSFS diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index 1c9effa25e2632497384e973a28954f7e33aedbc..6480dcaca275b62541964ebfe390f0c0da8d371f 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -310,6 +310,15 @@ static inline void __kernel_param_unlock(void) #define core_param(name, var, type, perm) \ param_check_##type(name, &(var)); \ __module_param_call("", name, ¶m_ops_##type, &var, perm, -1, 0) + +/** + * core_param_unsafe - same as core_param but taints kernel + */ +#define core_param_unsafe(name, var, type, perm) \ + param_check_##type(name, &(var)); \ + __module_param_call("", name, ¶m_ops_##type, &var, perm, \ + -1, KERNEL_PARAM_FL_UNSAFE) + #endif /* !MODULE */ /** @@ -357,8 +366,9 @@ extern char *parse_args(const char *name, unsigned num, s16 level_min, s16 level_max, + void *arg, int (*unknown)(char *param, char *val, - const char *doing)); + const char *doing, void *arg)); /* Called by module remove. */ #ifdef CONFIG_SYSFS diff --git a/init/main.c b/init/main.c index 2a89545e0a5d690db09acaa7042c83256ea84fb1..c599aea23bb1cbaec3eeb55ec2a181fcd735fd2c 100644 --- a/init/main.c +++ b/init/main.c @@ -235,7 +235,8 @@ static int __init loglevel(char *str) early_param("loglevel", loglevel); /* Change NUL term back to "=", to make "param" the whole string. */ -static int __init repair_env_string(char *param, char *val, const char *unused) +static int __init repair_env_string(char *param, char *val, + const char *unused, void *arg) { if (val) { /* param=val or param="val"? */ @@ -252,14 +253,15 @@ static int __init repair_env_string(char *param, char *val, const char *unused) } /* Anything after -- gets handed straight to init. */ -static int __init set_init_arg(char *param, char *val, const char *unused) +static int __init set_init_arg(char *param, char *val, + const char *unused, void *arg) { unsigned int i; if (panic_later) return 0; - repair_env_string(param, val, unused); + repair_env_string(param, val, unused, NULL); for (i = 0; argv_init[i]; i++) { if (i == MAX_INIT_ARGS) { @@ -276,9 +278,10 @@ static int __init set_init_arg(char *param, char *val, const char *unused) * Unknown boot options get handed to init, unless they look like * unused parameters (modprobe will find them in /proc/cmdline). */ -static int __init unknown_bootoption(char *param, char *val, const char *unused) +static int __init unknown_bootoption(char *param, char *val, + const char *unused, void *arg) { - repair_env_string(param, val, unused); + repair_env_string(param, val, unused, NULL); /* Handle obsolete-style parameters */ if (obsolete_checksetup(param)) @@ -410,7 +413,8 @@ static noinline void __init_refok rest_init(void) } /* Check for early params. */ -static int __init do_early_param(char *param, char *val, const char *unused) +static int __init do_early_param(char *param, char *val, + const char *unused, void *arg) { const struct obs_kernel_param *p; @@ -429,7 +433,8 @@ static int __init do_early_param(char *param, char *val, const char *unused) void __init parse_early_options(char *cmdline) { - parse_args("early options", cmdline, NULL, 0, 0, 0, do_early_param); + parse_args("early options", cmdline, NULL, 0, 0, 0, NULL, + do_early_param); } /* Arch code calls this early on, or if not, just before other parsing. */ @@ -535,10 +540,10 @@ asmlinkage __visible void __init start_kernel(void) after_dashes = parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, - -1, -1, &unknown_bootoption); + -1, -1, NULL, &unknown_bootoption); if (!IS_ERR_OR_NULL(after_dashes)) parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, - set_init_arg); + NULL, set_init_arg); jump_label_init(); @@ -848,7 +853,7 @@ static void __init do_initcall_level(int level) initcall_command_line, __start___param, __stop___param - __start___param, level, level, - &repair_env_string); + NULL, &repair_env_string); for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) do_one_initcall(*fn); diff --git a/kernel/module.c b/kernel/module.c index b38f96a183b5b07e3c31e791e81062edc4b23408..f80a97f7da1f14b96c87efaf82f9f8c88612ab54 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3107,7 +3107,7 @@ static noinline int do_init_module(struct module *mod) * * http://thread.gmane.org/gmane.linux.kernel/1420814 */ - if (current->flags & PF_USED_ASYNC) + if (!mod->async_probe_requested && (current->flags & PF_USED_ASYNC)) async_synchronize_full(); mutex_lock(&module_mutex); @@ -3237,10 +3237,19 @@ static int complete_formation(struct module *mod, struct load_info *info) return err; } -static int unknown_module_param_cb(char *param, char *val, const char *modname) +static int unknown_module_param_cb(char *param, char *val, const char *modname, + void *arg) { + struct module *mod = arg; + int ret; + + if (strcmp(param, "async_probe") == 0) { + mod->async_probe_requested = true; + return 0; + } + /* Check for magic 'dyndbg' arg */ - int ret = ddebug_dyndbg_module_param_cb(param, val, modname); + ret = ddebug_dyndbg_module_param_cb(param, val, modname); if (ret != 0) pr_warn("%s: unknown parameter '%s' ignored\n", modname, param); return 0; @@ -3342,7 +3351,8 @@ static int load_module(struct load_info *info, const char __user *uargs, /* Module is ready to execute: parsing args may do that. */ after_dashes = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, - -32768, 32767, unknown_module_param_cb); + -32768, 32767, NULL, + unknown_module_param_cb); if (IS_ERR(after_dashes)) { err = PTR_ERR(after_dashes); goto bug_cleanup; diff --git a/kernel/params.c b/kernel/params.c index a22d6a759b1a9391d5713364097bcfca35f197e7..30288c1e15dd1afe441f7d4dd0cd8f8b288eed28 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -100,8 +100,9 @@ static int parse_one(char *param, unsigned num_params, s16 min_level, s16 max_level, + void *arg, int (*handle_unknown)(char *param, char *val, - const char *doing)) + const char *doing, void *arg)) { unsigned int i; int err; @@ -128,7 +129,7 @@ static int parse_one(char *param, if (handle_unknown) { pr_debug("doing %s: %s='%s'\n", doing, param, val); - return handle_unknown(param, val, doing); + return handle_unknown(param, val, doing, arg); } pr_debug("Unknown argument '%s'\n", param); @@ -194,7 +195,9 @@ char *parse_args(const char *doing, unsigned num, s16 min_level, s16 max_level, - int (*unknown)(char *param, char *val, const char *doing)) + void *arg, + int (*unknown)(char *param, char *val, + const char *doing, void *arg)) { char *param, *val; @@ -214,7 +217,7 @@ char *parse_args(const char *doing, return args; irq_was_disabled = irqs_disabled(); ret = parse_one(param, val, doing, params, num, - min_level, max_level, unknown); + min_level, max_level, arg, unknown); if (irq_was_disabled && !irqs_disabled()) pr_warn("%s: option '%s' enabled irq's!\n", doing, param); diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index d8f3d3150603cc0d2abd06e415f0c9a35521e2fa..e491e02eff549fbe8c2e245c3f475b908818938f 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -887,7 +887,7 @@ static int ddebug_dyndbg_param_cb(char *param, char *val, /* handle both dyndbg and $module.dyndbg params at boot */ static int ddebug_dyndbg_boot_param_cb(char *param, char *val, - const char *unused) + const char *unused, void *arg) { vpr_info("%s=\"%s\"\n", param, val); return ddebug_dyndbg_param_cb(param, val, NULL, 0); @@ -1028,7 +1028,7 @@ static int __init dynamic_debug_init(void) */ cmdline = kstrdup(saved_command_line, GFP_KERNEL); parse_args("dyndbg params", cmdline, NULL, - 0, 0, 0, &ddebug_dyndbg_boot_param_cb); + 0, 0, 0, NULL, &ddebug_dyndbg_boot_param_cb); kfree(cmdline); return 0;