Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os 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>

authored by

Aviya Erenfeld and committed by
Greg Kroah-Hartman
52256637 c4a74f63

+154 -19
+74 -9
drivers/base/devcoredump.c
··· 4 4 * GPL LICENSE SUMMARY 5 5 * 6 6 * Copyright(c) 2014 Intel Mobile Communications GmbH 7 + * Copyright(c) 2015 Intel Deutschland GmbH 7 8 * 8 9 * This program is free software; you can redistribute it and/or modify 9 10 * it under the terms of version 2 of the GNU General Public License as ··· 42 41 43 42 struct devcd_entry { 44 43 struct device devcd_dev; 45 - const void *data; 44 + void *data; 46 45 size_t datalen; 47 46 struct module *owner; 48 47 ssize_t (*read)(char *buffer, loff_t offset, size_t count, 49 - const void *data, size_t datalen); 50 - void (*free)(const void *data); 48 + void *data, size_t datalen); 49 + void (*free)(void *data); 51 50 struct delayed_work del_wk; 52 51 struct device *failing_dev; 53 52 }; ··· 175 174 }; 176 175 177 176 static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count, 178 - const void *data, size_t datalen) 177 + void *data, size_t datalen) 179 178 { 180 179 if (offset > datalen) 181 180 return -EINVAL; ··· 189 188 return count; 190 189 } 191 190 191 + static void devcd_freev(void *data) 192 + { 193 + vfree(data); 194 + } 195 + 192 196 /** 193 197 * dev_coredumpv - create device coredump with vmalloc data 194 198 * @dev: the struct device for the crashed device ··· 204 198 * This function takes ownership of the vmalloc'ed data and will free 205 199 * it when it is no longer used. See dev_coredumpm() for more information. 206 200 */ 207 - void dev_coredumpv(struct device *dev, const void *data, size_t datalen, 201 + void dev_coredumpv(struct device *dev, void *data, size_t datalen, 208 202 gfp_t gfp) 209 203 { 210 - dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, vfree); 204 + dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, devcd_freev); 211 205 } 212 206 EXPORT_SYMBOL_GPL(dev_coredumpv); 213 207 ··· 216 210 struct devcd_entry *devcd = dev_to_devcd(dev); 217 211 218 212 return devcd->failing_dev == failing; 213 + } 214 + 215 + /** 216 + * devcd_free_sgtable - free all the memory of the given scatterlist table 217 + * (i.e. both pages and scatterlist instances) 218 + * NOTE: if two tables allocated with devcd_alloc_sgtable and then chained 219 + * using the sg_chain function then that function should be called only once 220 + * on the chained table 221 + * @table: pointer to sg_table to free 222 + */ 223 + static void devcd_free_sgtable(void *data) 224 + { 225 + _devcd_free_sgtable(data); 226 + } 227 + 228 + /** 229 + * devcd_read_from_table - copy data from sg_table to a given buffer 230 + * and return the number of bytes read 231 + * @buffer: the buffer to copy the data to it 232 + * @buf_len: the length of the buffer 233 + * @data: the scatterlist table to copy from 234 + * @offset: start copy from @offset@ bytes from the head of the data 235 + * in the given scatterlist 236 + * @data_len: the length of the data in the sg_table 237 + */ 238 + static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset, 239 + size_t buf_len, void *data, 240 + size_t data_len) 241 + { 242 + struct scatterlist *table = data; 243 + 244 + if (offset > data_len) 245 + return -EINVAL; 246 + 247 + if (offset + buf_len > data_len) 248 + buf_len = data_len - offset; 249 + return sg_pcopy_to_buffer(table, sg_nents(table), buffer, buf_len, 250 + offset); 219 251 } 220 252 221 253 /** ··· 272 228 * function will be called to free the data. 273 229 */ 274 230 void dev_coredumpm(struct device *dev, struct module *owner, 275 - const void *data, size_t datalen, gfp_t gfp, 231 + void *data, size_t datalen, gfp_t gfp, 276 232 ssize_t (*read)(char *buffer, loff_t offset, size_t count, 277 - const void *data, size_t datalen), 278 - void (*free)(const void *data)) 233 + void *data, size_t datalen), 234 + void (*free)(void *data)) 279 235 { 280 236 static atomic_t devcd_count = ATOMIC_INIT(0); 281 237 struct devcd_entry *devcd; ··· 334 290 free(data); 335 291 } 336 292 EXPORT_SYMBOL_GPL(dev_coredumpm); 293 + 294 + /** 295 + * dev_coredumpmsg - create device coredump that uses scatterlist as data 296 + * parameter 297 + * @dev: the struct device for the crashed device 298 + * @table: the dump data 299 + * @datalen: length of the data 300 + * @gfp: allocation flags 301 + * 302 + * Creates a new device coredump for the given device. If a previous one hasn't 303 + * been read yet, the new coredump is discarded. The data lifetime is determined 304 + * by the device coredump framework and when it is no longer needed 305 + * it will free the data. 306 + */ 307 + void dev_coredumpsg(struct device *dev, struct scatterlist *table, 308 + size_t datalen, gfp_t gfp) 309 + { 310 + dev_coredumpm(dev, NULL, table, datalen, gfp, devcd_read_from_sgtable, 311 + devcd_free_sgtable); 312 + } 313 + EXPORT_SYMBOL_GPL(dev_coredumpsg); 337 314 338 315 static int __init devcoredump_init(void) 339 316 {
+2 -2
drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c
··· 71 71 #include "iwl-csr.h" 72 72 73 73 static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, 74 - const void *data, size_t datalen) 74 + void *data, size_t datalen) 75 75 { 76 76 const struct iwl_mvm_dump_ptrs *dump_ptrs = data; 77 77 ssize_t bytes_read; ··· 104 104 return bytes_read + bytes_read_trans; 105 105 } 106 106 107 - static void iwl_mvm_free_coredump(const void *data) 107 + static void iwl_mvm_free_coredump(void *data) 108 108 { 109 109 const struct iwl_mvm_dump_ptrs *fw_error_dump = data; 110 110
+78 -8
include/linux/devcoredump.h
··· 1 + /* 2 + * This file is provided under the GPLv2 license. 3 + * 4 + * GPL LICENSE SUMMARY 5 + * 6 + * Copyright(c) 2015 Intel Deutschland GmbH 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of version 2 of the GNU General Public License as 10 + * published by the Free Software Foundation. 11 + * 12 + * This program is distributed in the hope that it will be useful, but 13 + * WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 + * General Public License for more details. 16 + * 17 + * The full GNU General Public License is included in this distribution 18 + * in the file called COPYING. 19 + */ 1 20 #ifndef __DEVCOREDUMP_H 2 21 #define __DEVCOREDUMP_H 3 22 ··· 24 5 #include <linux/module.h> 25 6 #include <linux/vmalloc.h> 26 7 8 + #include <linux/scatterlist.h> 9 + #include <linux/slab.h> 10 + 11 + /* 12 + * _devcd_free_sgtable - free all the memory of the given scatterlist table 13 + * (i.e. both pages and scatterlist instances) 14 + * NOTE: if two tables allocated and chained using the sg_chain function then 15 + * this function should be called only once on the first table 16 + * @table: pointer to sg_table to free 17 + */ 18 + static inline void _devcd_free_sgtable(struct scatterlist *table) 19 + { 20 + int i; 21 + struct page *page; 22 + struct scatterlist *iter; 23 + struct scatterlist *delete_iter; 24 + 25 + /* free pages */ 26 + iter = table; 27 + for_each_sg(table, iter, sg_nents(table), i) { 28 + page = sg_page(iter); 29 + if (page) 30 + __free_page(page); 31 + } 32 + 33 + /* then free all chained tables */ 34 + iter = table; 35 + delete_iter = table; /* always points on a head of a table */ 36 + while (!sg_is_last(iter)) { 37 + iter++; 38 + if (sg_is_chain(iter)) { 39 + iter = sg_chain_ptr(iter); 40 + kfree(delete_iter); 41 + delete_iter = iter; 42 + } 43 + } 44 + 45 + /* free the last table */ 46 + kfree(delete_iter); 47 + } 48 + 49 + 27 50 #ifdef CONFIG_DEV_COREDUMP 28 - void dev_coredumpv(struct device *dev, const void *data, size_t datalen, 51 + void dev_coredumpv(struct device *dev, void *data, size_t datalen, 29 52 gfp_t gfp); 30 53 31 54 void dev_coredumpm(struct device *dev, struct module *owner, 32 - const void *data, size_t datalen, gfp_t gfp, 55 + void *data, size_t datalen, gfp_t gfp, 33 56 ssize_t (*read)(char *buffer, loff_t offset, size_t count, 34 - const void *data, size_t datalen), 35 - void (*free)(const void *data)); 57 + void *data, size_t datalen), 58 + void (*free)(void *data)); 59 + 60 + void dev_coredumpsg(struct device *dev, struct scatterlist *table, 61 + size_t datalen, gfp_t gfp); 36 62 #else 37 - static inline void dev_coredumpv(struct device *dev, const void *data, 63 + static inline void dev_coredumpv(struct device *dev, void *data, 38 64 size_t datalen, gfp_t gfp) 39 65 { 40 66 vfree(data); ··· 87 23 88 24 static inline void 89 25 dev_coredumpm(struct device *dev, struct module *owner, 90 - const void *data, size_t datalen, gfp_t gfp, 26 + void *data, size_t datalen, gfp_t gfp, 91 27 ssize_t (*read)(char *buffer, loff_t offset, size_t count, 92 - const void *data, size_t datalen), 93 - void (*free)(const void *data)) 28 + void *data, size_t datalen), 29 + void (*free)(void *data)) 94 30 { 95 31 free(data); 32 + } 33 + 34 + static inline void dev_coredumpsg(struct device *dev, struct scatterlist *table, 35 + size_t datalen, gfp_t gfp) 36 + { 37 + _devcd_free_sgtable(table); 96 38 } 97 39 #endif /* CONFIG_DEV_COREDUMP */ 98 40