|  | /* SPDX-License-Identifier: GPL-2.0-only */ | 
|  | /* | 
|  | * Copyright(c) 2015 Intel Deutschland GmbH | 
|  | */ | 
|  | #ifndef __DEVCOREDUMP_H | 
|  | #define __DEVCOREDUMP_H | 
|  |  | 
|  | #include <linux/device.h> | 
|  | #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, void *data, size_t datalen, | 
|  | gfp_t gfp); | 
|  |  | 
|  | void dev_coredumpm(struct device *dev, struct module *owner, | 
|  | void *data, size_t datalen, gfp_t gfp, | 
|  | ssize_t (*read)(char *buffer, loff_t offset, size_t count, | 
|  | 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, void *data, | 
|  | size_t datalen, gfp_t gfp) | 
|  | { | 
|  | vfree(data); | 
|  | } | 
|  |  | 
|  | static inline void | 
|  | dev_coredumpm(struct device *dev, struct module *owner, | 
|  | void *data, size_t datalen, gfp_t gfp, | 
|  | ssize_t (*read)(char *buffer, loff_t offset, size_t count, | 
|  | 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 */ |