forked from mirrors/linux
		
	devcoredump: add scatterlist support
Add scatterlist support (dev_coredumpsg) to allow drivers to avoid vmalloc() like dev_coredumpm(), while also avoiding the module reference that the latter function requires. This internally uses dev_coredumpm() with function inside the devcoredump module, requiring removing the const (which touches the driver using it.) Signed-off-by: Aviya Erenfeld <aviya.erenfeld@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									c4a74f63df
								
							
						
					
					
						commit
						522566376a
					
				
					 3 changed files with 154 additions and 19 deletions
				
			
		|  | @ -4,6 +4,7 @@ | |||
|  * GPL LICENSE SUMMARY | ||||
|  * | ||||
|  * Copyright(c) 2014 Intel Mobile Communications GmbH | ||||
|  * Copyright(c) 2015 Intel Deutschland GmbH | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of version 2 of the GNU General Public License as | ||||
|  | @ -41,12 +42,12 @@ static bool devcd_disabled; | |||
| 
 | ||||
| struct devcd_entry { | ||||
| 	struct device devcd_dev; | ||||
| 	const void *data; | ||||
| 	void *data; | ||||
| 	size_t datalen; | ||||
| 	struct module *owner; | ||||
| 	ssize_t (*read)(char *buffer, loff_t offset, size_t count, | ||||
| 			const void *data, size_t datalen); | ||||
| 	void (*free)(const void *data); | ||||
| 			void *data, size_t datalen); | ||||
| 	void (*free)(void *data); | ||||
| 	struct delayed_work del_wk; | ||||
| 	struct device *failing_dev; | ||||
| }; | ||||
|  | @ -174,7 +175,7 @@ static struct class devcd_class = { | |||
| }; | ||||
| 
 | ||||
| static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, | ||||
| 			   const void *data, size_t datalen) | ||||
| 			   void *data, size_t datalen) | ||||
| { | ||||
| 	if (offset > datalen) | ||||
| 		return -EINVAL; | ||||
|  | @ -188,6 +189,11 @@ static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, | |||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static void devcd_freev(void *data) | ||||
| { | ||||
| 	vfree(data); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * dev_coredumpv - create device coredump with vmalloc data | ||||
|  * @dev: the struct device for the crashed device | ||||
|  | @ -198,10 +204,10 @@ static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, | |||
|  * This function takes ownership of the vmalloc'ed data and will free | ||||
|  * it when it is no longer used. See dev_coredumpm() for more information. | ||||
|  */ | ||||
| void dev_coredumpv(struct device *dev, const void *data, size_t datalen, | ||||
| void dev_coredumpv(struct device *dev, void *data, size_t datalen, | ||||
| 		   gfp_t gfp) | ||||
| { | ||||
| 	dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, vfree); | ||||
| 	dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, devcd_freev); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(dev_coredumpv); | ||||
| 
 | ||||
|  | @ -212,6 +218,44 @@ static int devcd_match_failing(struct device *dev, const void *failing) | |||
| 	return devcd->failing_dev == failing; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * devcd_free_sgtable - free all the memory of the given scatterlist table | ||||
|  * (i.e. both pages and scatterlist instances) | ||||
|  * NOTE: if two tables allocated with devcd_alloc_sgtable and then chained | ||||
|  * using the sg_chain function then that function should be called only once | ||||
|  * on the chained table | ||||
|  * @table: pointer to sg_table to free | ||||
|  */ | ||||
| static void devcd_free_sgtable(void *data) | ||||
| { | ||||
| 	_devcd_free_sgtable(data); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * devcd_read_from_table - copy data from sg_table to a given buffer | ||||
|  * and return the number of bytes read | ||||
|  * @buffer: the buffer to copy the data to it | ||||
|  * @buf_len: the length of the buffer | ||||
|  * @data: the scatterlist table to copy from | ||||
|  * @offset: start copy from @offset@ bytes from the head of the data | ||||
|  *	in the given scatterlist | ||||
|  * @data_len: the length of the data in the sg_table | ||||
|  */ | ||||
| static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset, | ||||
| 				       size_t buf_len, void *data, | ||||
| 				       size_t data_len) | ||||
| { | ||||
| 	struct scatterlist *table = data; | ||||
| 
 | ||||
| 	if (offset > data_len) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (offset + buf_len > data_len) | ||||
| 		buf_len = data_len - offset; | ||||
| 	return sg_pcopy_to_buffer(table, sg_nents(table), buffer, buf_len, | ||||
| 				  offset); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * dev_coredumpm - create device coredump with read/free methods | ||||
|  * @dev: the struct device for the crashed device | ||||
|  | @ -228,10 +272,10 @@ static int devcd_match_failing(struct device *dev, const void *failing) | |||
|  * function will be called to free the data. | ||||
|  */ | ||||
| void dev_coredumpm(struct device *dev, struct module *owner, | ||||
| 		   const void *data, size_t datalen, gfp_t gfp, | ||||
| 		   void *data, size_t datalen, gfp_t gfp, | ||||
| 		   ssize_t (*read)(char *buffer, loff_t offset, size_t count, | ||||
| 				   const void *data, size_t datalen), | ||||
| 		   void (*free)(const void *data)) | ||||
| 				   void *data, size_t datalen), | ||||
| 		   void (*free)(void *data)) | ||||
| { | ||||
| 	static atomic_t devcd_count = ATOMIC_INIT(0); | ||||
| 	struct devcd_entry *devcd; | ||||
|  | @ -291,6 +335,27 @@ void dev_coredumpm(struct device *dev, struct module *owner, | |||
| } | ||||
| EXPORT_SYMBOL_GPL(dev_coredumpm); | ||||
| 
 | ||||
| /**
 | ||||
|  * dev_coredumpmsg - create device coredump that uses scatterlist as data | ||||
|  * parameter | ||||
|  * @dev: the struct device for the crashed device | ||||
|  * @table: the dump data | ||||
|  * @datalen: length of the data | ||||
|  * @gfp: allocation flags | ||||
|  * | ||||
|  * Creates a new device coredump for the given device. If a previous one hasn't | ||||
|  * been read yet, the new coredump is discarded. The data lifetime is determined | ||||
|  * by the device coredump framework and when it is no longer needed | ||||
|  * it will free the data. | ||||
|  */ | ||||
| void dev_coredumpsg(struct device *dev, struct scatterlist *table, | ||||
| 		    size_t datalen, gfp_t gfp) | ||||
| { | ||||
| 	dev_coredumpm(dev, NULL, table, datalen, gfp, devcd_read_from_sgtable, | ||||
| 		      devcd_free_sgtable); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(dev_coredumpsg); | ||||
| 
 | ||||
| static int __init devcoredump_init(void) | ||||
| { | ||||
| 	return class_register(&devcd_class); | ||||
|  |  | |||
|  | @ -71,7 +71,7 @@ | |||
| #include "iwl-csr.h" | ||||
| 
 | ||||
| static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, | ||||
| 				     const void *data, size_t datalen) | ||||
| 				     void *data, size_t datalen) | ||||
| { | ||||
| 	const struct iwl_mvm_dump_ptrs *dump_ptrs = data; | ||||
| 	ssize_t bytes_read; | ||||
|  | @ -104,7 +104,7 @@ static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, | |||
| 	return bytes_read + bytes_read_trans; | ||||
| } | ||||
| 
 | ||||
| static void iwl_mvm_free_coredump(const void *data) | ||||
| static void iwl_mvm_free_coredump(void *data) | ||||
| { | ||||
| 	const struct iwl_mvm_dump_ptrs *fw_error_dump = data; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,3 +1,22 @@ | |||
| /*
 | ||||
|  * This file is provided under the GPLv2 license. | ||||
|  * | ||||
|  * GPL LICENSE SUMMARY | ||||
|  * | ||||
|  * Copyright(c) 2015 Intel Deutschland GmbH | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of version 2 of the GNU General Public License as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, but | ||||
|  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * The full GNU General Public License is included in this distribution | ||||
|  * in the file called COPYING. | ||||
|  */ | ||||
| #ifndef __DEVCOREDUMP_H | ||||
| #define __DEVCOREDUMP_H | ||||
| 
 | ||||
|  | @ -5,17 +24,62 @@ | |||
| #include <linux/module.h> | ||||
| #include <linux/vmalloc.h> | ||||
| 
 | ||||
| #include <linux/scatterlist.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * _devcd_free_sgtable - free all the memory of the given scatterlist table | ||||
|  * (i.e. both pages and scatterlist instances) | ||||
|  * NOTE: if two tables allocated and chained using the sg_chain function then | ||||
|  * this function should be called only once on the first table | ||||
|  * @table: pointer to sg_table to free | ||||
|  */ | ||||
| static inline void _devcd_free_sgtable(struct scatterlist *table) | ||||
| { | ||||
| 	int i; | ||||
| 	struct page *page; | ||||
| 	struct scatterlist *iter; | ||||
| 	struct scatterlist *delete_iter; | ||||
| 
 | ||||
| 	/* free pages */ | ||||
| 	iter = table; | ||||
| 	for_each_sg(table, iter, sg_nents(table), i) { | ||||
| 		page = sg_page(iter); | ||||
| 		if (page) | ||||
| 			__free_page(page); | ||||
| 	} | ||||
| 
 | ||||
| 	/* then free all chained tables */ | ||||
| 	iter = table; | ||||
| 	delete_iter = table;	/* always points on a head of a table */ | ||||
| 	while (!sg_is_last(iter)) { | ||||
| 		iter++; | ||||
| 		if (sg_is_chain(iter)) { | ||||
| 			iter = sg_chain_ptr(iter); | ||||
| 			kfree(delete_iter); | ||||
| 			delete_iter = iter; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* free the last table */ | ||||
| 	kfree(delete_iter); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_DEV_COREDUMP | ||||
| void dev_coredumpv(struct device *dev, const void *data, size_t datalen, | ||||
| void dev_coredumpv(struct device *dev, void *data, size_t datalen, | ||||
| 		   gfp_t gfp); | ||||
| 
 | ||||
| void dev_coredumpm(struct device *dev, struct module *owner, | ||||
| 		   const void *data, size_t datalen, gfp_t gfp, | ||||
| 		   void *data, size_t datalen, gfp_t gfp, | ||||
| 		   ssize_t (*read)(char *buffer, loff_t offset, size_t count, | ||||
| 				   const void *data, size_t datalen), | ||||
| 		   void (*free)(const void *data)); | ||||
| 				   void *data, size_t datalen), | ||||
| 		   void (*free)(void *data)); | ||||
| 
 | ||||
| void dev_coredumpsg(struct device *dev, struct scatterlist *table, | ||||
| 		    size_t datalen, gfp_t gfp); | ||||
| #else | ||||
| static inline void dev_coredumpv(struct device *dev, const void *data, | ||||
| static inline void dev_coredumpv(struct device *dev, void *data, | ||||
| 				 size_t datalen, gfp_t gfp) | ||||
| { | ||||
| 	vfree(data); | ||||
|  | @ -23,13 +87,19 @@ static inline void dev_coredumpv(struct device *dev, const void *data, | |||
| 
 | ||||
| static inline void | ||||
| dev_coredumpm(struct device *dev, struct module *owner, | ||||
| 	      const void *data, size_t datalen, gfp_t gfp, | ||||
| 	      void *data, size_t datalen, gfp_t gfp, | ||||
| 	      ssize_t (*read)(char *buffer, loff_t offset, size_t count, | ||||
| 			      const void *data, size_t datalen), | ||||
| 	      void (*free)(const void *data)) | ||||
| 			      void *data, size_t datalen), | ||||
| 	      void (*free)(void *data)) | ||||
| { | ||||
| 	free(data); | ||||
| } | ||||
| 
 | ||||
| static inline void dev_coredumpsg(struct device *dev, struct scatterlist *table, | ||||
| 				  size_t datalen, gfp_t gfp) | ||||
| { | ||||
| 	_devcd_free_sgtable(table); | ||||
| } | ||||
| #endif /* CONFIG_DEV_COREDUMP */ | ||||
| 
 | ||||
| #endif /* __DEVCOREDUMP_H */ | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Aviya Erenfeld
						Aviya Erenfeld