Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/printer: Allow NULL data in devcoredump printer

We want to determine the size of the devcoredump before writing it out.
To that end, we will run the devcoredump printer with NULL data to get
the size, alloc data based on the generated offset, then run the
devcorecump again with a valid data pointer to print. This necessitates
not writing data to the data pointer on the initial pass, when it is
NULL.

v5:
- Better commit message (Jonathan)
- Add kerenl doc with examples (Jani)

Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Acked-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Signed-off-by: Matthew Brost <matthew.brost@intel.com>
Reviewed-by: Jonathan Cavitt <jonathan.cavitt@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240801154118.2547543-3-matthew.brost@intel.com

+61 -6
+8 -5
drivers/gpu/drm/drm_print.c
··· 100 100 copy = iterator->remain; 101 101 102 102 /* Copy out the bit of the string that we need */ 103 - memcpy(iterator->data, 104 - str + (iterator->start - iterator->offset), copy); 103 + if (iterator->data) 104 + memcpy(iterator->data, 105 + str + (iterator->start - iterator->offset), copy); 105 106 106 107 iterator->offset = iterator->start + copy; 107 108 iterator->remain -= copy; ··· 111 110 112 111 len = min_t(ssize_t, strlen(str), iterator->remain); 113 112 114 - memcpy(iterator->data + pos, str, len); 113 + if (iterator->data) 114 + memcpy(iterator->data + pos, str, len); 115 115 116 116 iterator->offset += len; 117 117 iterator->remain -= len; ··· 142 140 if ((iterator->offset >= iterator->start) && (len < iterator->remain)) { 143 141 ssize_t pos = iterator->offset - iterator->start; 144 142 145 - snprintf(((char *) iterator->data) + pos, 146 - iterator->remain, "%pV", vaf); 143 + if (iterator->data) 144 + snprintf(((char *) iterator->data) + pos, 145 + iterator->remain, "%pV", vaf); 147 146 148 147 iterator->offset += len; 149 148 iterator->remain -= len;
+53 -1
include/drm/drm_print.h
··· 221 221 222 222 /** 223 223 * struct drm_print_iterator - local struct used with drm_printer_coredump 224 - * @data: Pointer to the devcoredump output buffer 224 + * @data: Pointer to the devcoredump output buffer, can be NULL if using 225 + * drm_printer_coredump to determine size of devcoredump 225 226 * @start: The offset within the buffer to start writing 226 227 * @remain: The number of bytes to write for this iteration 227 228 */ ··· 266 265 * dev_coredumpm(dev, THIS_MODULE, data, 0, GFP_KERNEL, 267 266 * coredump_read, ...) 268 267 * } 268 + * 269 + * The above example has a time complexity of O(N^2), where N is the size of the 270 + * devcoredump. This is acceptable for small devcoredumps but scales poorly for 271 + * larger ones. 272 + * 273 + * Another use case for drm_coredump_printer is to capture the devcoredump into 274 + * a saved buffer before the dev_coredump() callback. This involves two passes: 275 + * one to determine the size of the devcoredump and another to print it to a 276 + * buffer. Then, in dev_coredump(), copy from the saved buffer into the 277 + * devcoredump read buffer. 278 + * 279 + * For example:: 280 + * 281 + * char *devcoredump_saved_buffer; 282 + * 283 + * ssize_t __coredump_print(char *buffer, ssize_t count, ...) 284 + * { 285 + * struct drm_print_iterator iter; 286 + * struct drm_printer p; 287 + * 288 + * iter.data = buffer; 289 + * iter.start = 0; 290 + * iter.remain = count; 291 + * 292 + * p = drm_coredump_printer(&iter); 293 + * 294 + * drm_printf(p, "foo=%d\n", foo); 295 + * ... 296 + * return count - iter.remain; 297 + * } 298 + * 299 + * void coredump_print(...) 300 + * { 301 + * ssize_t count; 302 + * 303 + * count = __coredump_print(NULL, INT_MAX, ...); 304 + * devcoredump_saved_buffer = kvmalloc(count, GFP_KERNEL); 305 + * __coredump_print(devcoredump_saved_buffer, count, ...); 306 + * } 307 + * 308 + * void coredump_read(char *buffer, loff_t offset, size_t count, 309 + * void *data, size_t datalen) 310 + * { 311 + * ... 312 + * memcpy(buffer, devcoredump_saved_buffer + offset, count); 313 + * ... 314 + * } 315 + * 316 + * The above example has a time complexity of O(N*2), where N is the size of the 317 + * devcoredump. This scales better than the previous example for larger 318 + * devcoredumps. 269 319 * 270 320 * RETURNS: 271 321 * The &drm_printer object