mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 08:44:41 +00:00 
			
		
		
		
	Driver core patches for 4.2-rc1
Here is the driver core / firmware changes for 4.2-rc1. A number of small changes all over the place in the driver core, and in the firmware subsystem. Nothing really major, full details in the shortlog. Some of it is a bit of churn, given that the platform driver probing changes was found to not work well, so they were reverted. All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlWNoCQACgkQMUfUDdst+ym4JACdFrrXoMt2pb8nl5gMidGyM9/D jg8AnRgdW8ArDA/xOarULd/X43eA3J3C =Al2B -----END PGP SIGNATURE----- Merge tag 'driver-core-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core Pull driver core updates from Greg KH: "Here is the driver core / firmware changes for 4.2-rc1. A number of small changes all over the place in the driver core, and in the firmware subsystem. Nothing really major, full details in the shortlog. Some of it is a bit of churn, given that the platform driver probing changes was found to not work well, so they were reverted. All of these have been in linux-next for a while with no reported issues" * tag 'driver-core-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (31 commits) Revert "base/platform: Only insert MEM and IO resources" Revert "base/platform: Continue on insert_resource() error" Revert "of/platform: Use platform_device interface" Revert "base/platform: Remove code duplication" firmware: add missing kfree for work on async call fs: sysfs: don't pass count == 0 to bin file readers base:dd - Fix for typo in comment to function driver_deferred_probe_trigger(). base/platform: Remove code duplication of/platform: Use platform_device interface base/platform: Continue on insert_resource() error base/platform: Only insert MEM and IO resources firmware: use const for remaining firmware names firmware: fix possible use after free on name on asynchronous request firmware: check for file truncation on direct firmware loading firmware: fix __getname() missing failure check drivers: of/base: move of_init to driver_init drivers/base: cacheinfo: fix annoying typo when DT nodes are absent sysfs: disambiguate between "error code" and "failure" in comments driver-core: fix build for !CONFIG_MODULES driver-core: make __device_attach() static ...
This commit is contained in:
		
						commit
						8d7804a2f0
					
				
					 23 changed files with 372 additions and 84 deletions
				
			
		|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 <gregkh@linuxfoundation.org> | ||||
| 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 | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/async.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/errno.h> | ||||
|  | @ -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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
| 	} | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| #include <linux/acpi.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/cpufeature.h> | ||||
| #include <linux/tick.h> | ||||
| 
 | ||||
| #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 | ||||
|  |  | |||
|  | @ -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,16 +417,152 @@ 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); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| 	device_lock(dev); | ||||
| 	if (dev->driver) { | ||||
| 		if (klist_node_attached(&dev->p->knode_driver)) { | ||||
| 			ret = 1; | ||||
| 			goto out_unlock; | ||||
| 		} | ||||
| 		ret = device_bind_driver(dev); | ||||
| 		if (ret == 0) | ||||
| 			ret = 1; | ||||
| 		else { | ||||
| 			dev->driver = NULL; | ||||
| 			ret = 0; | ||||
| 		} | ||||
| 	} else { | ||||
| 		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. | ||||
|  | @ -443,31 +579,15 @@ static int __device_attach(struct device_driver *drv, void *data) | |||
|  */ | ||||
| int device_attach(struct device *dev) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	device_lock(dev); | ||||
| 	if (dev->driver) { | ||||
| 		if (klist_node_attached(&dev->p->knode_driver)) { | ||||
| 			ret = 1; | ||||
| 			goto out_unlock; | ||||
| 		} | ||||
| 		ret = device_bind_driver(dev); | ||||
| 		if (ret == 0) | ||||
| 			ret = 1; | ||||
| 		else { | ||||
| 			dev->driver = NULL; | ||||
| 			ret = 0; | ||||
| 		} | ||||
| 	} else { | ||||
| 		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); | ||||
| 		pm_request_idle(dev); | ||||
| 	} | ||||
| out_unlock: | ||||
| 	device_unlock(dev); | ||||
| 	return ret; | ||||
| 	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); | ||||
|  |  | |||
|  | @ -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 @@ fail: | |||
| 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 @@ found: | |||
| 
 | ||||
| static void free_fw_cache_entry(struct fw_cache_entry *fce) | ||||
| { | ||||
| 	kfree_const(fce->name); | ||||
| 	kfree(fce); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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. | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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); | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
							
								
								
									
										25
									
								
								init/main.c
									
										
									
									
									
								
							
							
						
						
									
										25
									
								
								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); | ||||
|  |  | |||
|  | @ -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 @@ out: | |||
| 	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; | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Linus Torvalds
						Linus Torvalds