Pull non-MM updates from Andrew Morton: - "ida: Remove the ida_simple_xxx() API" from Christophe Jaillet completes the removal of this legacy IDR API - "panic: introduce panic status function family" from Jinchao Wang provides a number of cleanups to the panic code and its various helpers, which were rather ad-hoc and scattered all over the place - "tools/delaytop: implement real-time keyboard interaction support" from Fan Yu adds a few nice user-facing usability changes to the delaytop monitoring tool - "efi: Fix EFI boot with kexec handover (KHO)" from Evangelos Petrongonas fixes a panic which was happening with the combination of EFI and KHO - "Squashfs: performance improvement and a sanity check" from Phillip Lougher teaches squashfs's lseek() about SEEK_DATA/SEEK_HOLE. A mere 150x speedup was measured for a well-chosen microbenchmark - plus another 50-odd singleton patches all over the place * tag 'mm-nonmm-stable-2025-10-02-15-29' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (75 commits) Squashfs: reject negative file sizes in squashfs_read_inode() kallsyms: use kmalloc_array() instead of kmalloc() MAINTAINERS: update Sibi Sankar's email address Squashfs: add SEEK_DATA/SEEK_HOLE support Squashfs: add additional inode sanity checking lib/genalloc: fix device leak in of_gen_pool_get() panic: remove CONFIG_PANIC_ON_OOPS_VALUE ocfs2: fix double free in user_cluster_connect() checkpatch: suppress strscpy warnings for userspace tools cramfs: fix incorrect physical page address calculation kernel: prevent prctl(PR_SET_PDEATHSIG) from racing with parent process exit Squashfs: fix uninit-value in squashfs_get_parent kho: only fill kimage if KHO is finalized ocfs2: avoid extra calls to strlen() after ocfs2_sprintf_system_inode_name() kernel/sys.c: fix the racy usage of task_lock(tsk->group_leader) in sys_prlimit64() paths sched/task.h: fix the wrong comment on task_lock() nesting with tasklist_lock coccinelle: platform_no_drv_owner: handle also built-in drivers coccinelle: of_table: handle SPI device ID tables lib/decompress: use designated initializers for struct compress_format efi: support booting with kexec handover (KHO) ...
Unified · Split
| @@ -721,7 +721,8 @@ Shuah Khan <shuah@kernel.org> <shuahkhan@gmail.com> · 720 unmodified lines | ||
| 721 | 721 | Shuah Khan <shuah@kernel.org> <shuah.khan@hp.com> |
| 722 | 722 | Shuah Khan <shuah@kernel.org> <shuahkh@osg.samsung.com> |
| 723 | 723 | Shuah Khan <shuah@kernel.org> <shuah.kh@samsung.com> |
| 724 | −Sibi Sankar <quic_sibis@quicinc.com> <sibis@codeaurora.org> | |
| 724 | +Sibi Sankar <sibi.sankar@oss.qualcomm.com> <sibis@codeaurora.org> | |
| 725 | +Sibi Sankar <sibi.sankar@oss.qualcomm.com> <quic_sibis@quicinc.com> | |
| 725 | 726 | Sid Manning <quic_sidneym@quicinc.com> <sidneym@codeaurora.org> |
| 726 | 727 | Simon Arlott <simon@octiron.net> <simon@fire.lp0.eu> |
| 727 | 728 | Simona Vetter <simona.vetter@ffwll.ch> <daniel.vetter@ffwll.ch> |
| @@ -134,47 +134,72 @@ The above command can be used with -v to get more debug information. · 133 unmodified lines | ||
| 134 | 134 | |
| 135 | 135 | After the system starts, use `delaytop` to get the system-wide delay information, |
| 136 | 136 | which includes system-wide PSI information and Top-N high-latency tasks. |
| 137 | +Note: PSI support requires `CONFIG_PSI=y` and `psi=1` for full functionality. | |
| 137 | 138 | |
| 138 | −`delaytop` supports sorting by CPU latency in descending order by default, | |
| 139 | −displays the top 20 high-latency tasks by default, and refreshes the latency | |
| 140 | −data every 2 seconds by default. | |
| 139 | +`delaytop` is an interactive tool for monitoring system pressure and task delays. | |
| 140 | +It supports multiple sorting options, display modes, and real-time keyboard controls. | |
| 141 | 141 | |
| 142 | −Get PSI information and Top-N tasks delay, since system boot:: | |
| 142 | +Basic usage with default settings (sorts by CPU delay, shows top 20 tasks, refreshes every 2 seconds):: | |
| 143 | 143 | |
| 144 | 144 | bash# ./delaytop |
| 145 | − System Pressure Information: (avg10/avg60/avg300/total) | |
| 146 | − CPU some: 0.0%/ 0.0%/ 0.0%/ 345(ms) | |
| 145 | + System Pressure Information: (avg10/avg60vg300/total) | |
| 146 | + CPU some: 0.0%/ 0.0%/ 0.0%/ 106137(ms) | |
| 147 | 147 | CPU full: 0.0%/ 0.0%/ 0.0%/ 0(ms) |
| 148 | 148 | Memory full: 0.0%/ 0.0%/ 0.0%/ 0(ms) |
| 149 | 149 | Memory some: 0.0%/ 0.0%/ 0.0%/ 0(ms) |
| 150 | − IO full: 0.0%/ 0.0%/ 0.0%/ 65(ms) | |
| 151 | − IO some: 0.0%/ 0.0%/ 0.0%/ 79(ms) | |
| 150 | + IO full: 0.0%/ 0.0%/ 0.0%/ 2240(ms) | |
| 151 | + IO some: 0.0%/ 0.0%/ 0.0%/ 2783(ms) | |
| 152 | 152 | IRQ full: 0.0%/ 0.0%/ 0.0%/ 0(ms) |
| 153 | − Top 20 processes (sorted by CPU delay): | |
| 154 | − PID TGID COMMAND CPU(ms) IO(ms) SWAP(ms) RCL(ms) THR(ms) CMP(ms) WP(ms) IRQ(ms) | |
| 155 | − ---------------------------------------------------------------------------------------------- | |
| 156 | − 161 161 zombie_memcg_re 1.40 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 157 | − 130 130 blkcg_punt_bio 1.37 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 158 | − 444 444 scsi_tmf_0 0.73 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 159 | − 1280 1280 rsyslogd 0.53 0.04 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 160 | − 12 12 ksoftirqd/0 0.47 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 161 | − 1277 1277 nbd-server 0.44 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 162 | − 308 308 kworker/2:2-sys 0.41 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 163 | − 55 55 netns 0.36 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 164 | − 1187 1187 acpid 0.31 0.03 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 165 | − 6184 6184 kworker/1:2-sys 0.24 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 166 | − 186 186 kaluad 0.24 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 167 | − 18 18 ksoftirqd/1 0.24 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 168 | − 185 185 kmpath_rdacd 0.23 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 169 | − 190 190 kstrp 0.23 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 170 | − 2759 2759 agetty 0.20 0.03 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 171 | − 1190 1190 kworker/0:3-sys 0.19 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 172 | − 1272 1272 sshd 0.15 0.04 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 173 | − 1156 1156 license 0.15 0.11 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 174 | − 134 134 md 0.13 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 175 | − 6142 6142 kworker/3:2-xfs 0.13 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | |
| 176 | − | |
| 177 | −Dynamic interactive interface of delaytop:: | |
| 153 | + [o]sort [M]memverbose [q]quit | |
| 154 | + Top 20 processes (sorted by cpu delay): | |
| 155 | + PID TGID COMMAND CPU(ms) IO(ms) IRQ(ms) MEM(ms) | |
| 156 | + ------------------------------------------------------------------------ | |
| 157 | + 110 110 kworker/15:0H-s 27.91 0.00 0.00 0.00 | |
| 158 | + 57 57 cpuhp/7 3.18 0.00 0.00 0.00 | |
| 159 | + 99 99 cpuhp/14 2.97 0.00 0.00 0.00 | |
| 160 | + 51 51 cpuhp/6 0.90 0.00 0.00 0.00 | |
| 161 | + 44 44 kworker/4:0H-sy 0.80 0.00 0.00 0.00 | |
| 162 | + 60 60 ksoftirqd/7 0.74 0.00 0.00 0.00 | |
| 163 | + 76 76 idle_inject/10 0.31 0.00 0.00 0.00 | |
| 164 | + 100 100 idle_inject/14 0.30 0.00 0.00 0.00 | |
| 165 | + 1309 1309 systemsettings 0.29 0.00 0.00 0.00 | |
| 166 | + 45 45 cpuhp/5 0.22 0.00 0.00 0.00 | |
| 167 | + 63 63 cpuhp/8 0.20 0.00 0.00 0.00 | |
| 168 | + 87 87 cpuhp/12 0.18 0.00 0.00 0.00 | |
| 169 | + 93 93 cpuhp/13 0.17 0.00 0.00 0.00 | |
| 170 | + 1265 1265 acpid 0.17 0.00 0.00 0.00 | |
| 171 | + 1552 1552 sshd 0.17 0.00 0.00 0.00 | |
| 172 | + 2584 2584 sddm-helper 0.16 0.00 0.00 0.00 | |
| 173 | + 1284 1284 rtkit-daemon 0.15 0.00 0.00 0.00 | |
| 174 | + 1326 1326 nde-netfilter 0.14 0.00 0.00 0.00 | |
| 175 | + 27 27 cpuhp/2 0.13 0.00 0.00 0.00 | |
| 176 | + 631 631 kworker/11:2-rc 0.11 0.00 0.00 0.00 | |
| 177 | + | |
| 178 | +Interactive keyboard controls during runtime:: | |
| 179 | + | |
| 180 | + o - Select sort field (CPU, IO, IRQ, Memory, etc.) | |
| 181 | + M - Toggle display mode (Default/Memory Verbose) | |
| 182 | + q - Quit | |
| 183 | + | |
| 184 | +Available sort fields(use -s/--sort or interactive command):: | |
| 185 | + | |
| 186 | + cpu(c) - CPU delay | |
| 187 | + blkio(i) - I/O delay | |
| 188 | + irq(q) - IRQ delay | |
| 189 | + mem(m) - Total memory delay | |
| 190 | + swapin(s) - Swapin delay (memory verbose mode only) | |
| 191 | + freepages(r) - Freepages reclaim delay (memory verbose mode only) | |
| 192 | + thrashing(t) - Thrashing delay (memory verbose mode only) | |
| 193 | + compact(p) - Compaction delay (memory verbose mode only) | |
| 194 | + wpcopy(w) - Write page copy delay (memory verbose mode only) | |
| 195 | + | |
| 196 | +Advanced usage examples:: | |
| 197 | + | |
| 198 | + # ./delaytop -s blkio | |
| 199 | + Sorted by IO delay | |
| 200 | + | |
| 201 | + # ./delaytop -s mem -M | |
| 202 | + Sorted by memory delay in memory verbose mode | |
| 178 | 203 | |
| 179 | 204 | # ./delaytop -p pid |
| 180 | 205 | Print delayacct stats |
| @@ -4603,7 +4603,7 @@ · 4602 unmodified lines | ||
| 4603 | 4603 | bit 2: print timer info |
| 4604 | 4604 | bit 3: print locks info if CONFIG_LOCKDEP is on |
| 4605 | 4605 | bit 4: print ftrace buffer |
| 4606 | − bit 5: replay all messages on consoles at the end of panic | |
| 4606 | + bit 5: replay all kernel messages on consoles at the end of panic | |
| 4607 | 4607 | bit 6: print all CPUs backtrace (if available in the arch) |
| 4608 | 4608 | bit 7: print only tasks in uninterruptible (blocked) state |
| 4609 | 4609 | *Be aware* that this option may print a _lot_ of lines, |
| @@ -890,7 +890,7 @@ bit 1 print system memory info · 889 unmodified lines | ||
| 890 | 890 | bit 2 print timer info |
| 891 | 891 | bit 3 print locks info if ``CONFIG_LOCKDEP`` is on |
| 892 | 892 | bit 4 print ftrace buffer |
| 893 | −bit 5 replay all messages on consoles at the end of panic | |
| 893 | +bit 5 replay all kernel messages on consoles at the end of panic | |
| 894 | 894 | bit 6 print all CPUs backtrace (if available in the arch) |
| 895 | 895 | bit 7 print only tasks in uninterruptible (blocked) state |
| 896 | 896 | ===== ============================================ |
| @@ -361,7 +361,12 @@ local tasks spawned by the process and the global task that handles USB bus #1: · 360 unmodified lines | ||
| 361 | 361 | */ |
| 362 | 362 | sleep(2); |
| 363 | 363 | |
| 364 | − n = __atomic_load_n(&cover[0], __ATOMIC_RELAXED); | |
| 364 | + /* | |
| 365 | + * The load to the coverage count should be an acquire to pair with | |
| 366 | + * pair with the corresponding write memory barrier (smp_wmb()) on | |
| 367 | + * the kernel-side in kcov_move_area(). | |
| 368 | + */ | |
| 369 | + n = __atomic_load_n(&cover[0], __ATOMIC_ACQUIRE); | |
| 365 | 370 | for (i = 0; i < n; i++) |
| 366 | 371 | printf("0x%lx\n", cover[i + 1]); |
| 367 | 372 | if (ioctl(fd, KCOV_DISABLE, 0)) |
| @@ -20987,7 +20987,7 @@ F: Documentation/devicetree/bindings/power/avs/qcom,cpr.yaml · 20986 unmodified lines | ||
| 20987 | 20987 | F: drivers/pmdomain/qcom/cpr.c |
| 20988 | 20988 | |
| 20989 | 20989 | QUALCOMM CPUCP MAILBOX DRIVER |
| 20990 | −M: Sibi Sankar <quic_sibis@quicinc.com> | |
| 20990 | +M: Sibi Sankar <sibi.sankar@oss.qualcomm.com> | |
| 20991 | 20991 | L: linux-arm-msm@vger.kernel.org |
| 20992 | 20992 | S: Supported |
| 20993 | 20993 | F: Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml |
| @@ -165,14 +165,23 @@ static struct crash_mem *fill_up_crash_elf_data(void) · 164 unmodified lines | ||
| 165 | 165 | /* |
| 166 | 166 | * Exclusion of crash region, crashk_low_res and/or crashk_cma_ranges |
| 167 | 167 | * may cause range splits. So add extra slots here. |
| 168 | + * | |
| 169 | + * Exclusion of low 1M may not cause another range split, because the | |
| 170 | + * range of exclude is [0, 1M] and the condition for splitting a new | |
| 171 | + * region is that the start, end parameters are both in a certain | |
| 172 | + * existing region in cmem and cannot be equal to existing region's | |
| 173 | + * start or end. Obviously, the start of [0, 1M] cannot meet this | |
| 174 | + * condition. | |
| 175 | + * | |
| 176 | + * But in order to lest the low 1M could be changed in the future, | |
| 177 | + * (e.g. [start, 1M]), add a extra slot. | |
| 168 | 178 | */ |
| 169 | − nr_ranges += 2 + crashk_cma_cnt; | |
| 179 | + nr_ranges += 3 + crashk_cma_cnt; | |
| 170 | 180 | cmem = vzalloc(struct_size(cmem, ranges, nr_ranges)); |
| 171 | 181 | if (!cmem) |
| 172 | 182 | return NULL; |
| 173 | 183 | |
| 174 | 184 | cmem->max_nr_ranges = nr_ranges; |
| 175 | − cmem->nr_ranges = 0; | |
| 176 | 185 | |
| 177 | 186 | return cmem; |
| 178 | 187 | } |
| @@ -323,16 +332,20 @@ int crash_setup_memmap_entries(struct kimage *image, struct boot_params *params) · 144 unmodified lines | ||
| 323 | 332 | struct crash_mem *cmem; |
| 324 | 333 | |
| 325 | 334 | /* |
| 326 | − * Using random kexec_buf for passing dm crypt keys may cause a range | |
| 327 | − * split. So use two slots here. | |
| 335 | + * In the current x86 architecture code, the elfheader is always | |
| 336 | + * allocated at crashk_res.start. But it depends on the allocation | |
| 337 | + * position of elfheader in crashk_res. To avoid potential out of | |
| 338 | + * bounds in future, add an extra slot. | |
| 339 | + * | |
| 340 | + * And using random kexec_buf for passing dm crypt keys may cause a | |
| 341 | + * range split too, add another extra slot here. | |
| 328 | 342 | */ |
| 329 | − nr_ranges = 2; | |
| 343 | + nr_ranges = 3; | |
| 330 | 344 | cmem = vzalloc(struct_size(cmem, ranges, nr_ranges)); |
| 331 | 345 | if (!cmem) |
| 332 | 346 | return -ENOMEM; |
| 333 | 347 | |
| 334 | 348 | cmem->max_nr_ranges = nr_ranges; |
| 335 | − cmem->nr_ranges = 0; | |
| 336 | 349 | |
| 337 | 350 | memset(&cmd, 0, sizeof(struct crash_memmap_data)); |
| 338 | 351 | cmd.params = params; |
| @@ -16,6 +16,8 @@ · 15 unmodified lines | ||
| 16 | 16 | #include <linux/kexec.h> |
| 17 | 17 | #include <linux/kernel.h> |
| 18 | 18 | #include <linux/mm.h> |
| 19 | +#include <linux/libfdt.h> | |
| 20 | +#include <linux/of_fdt.h> | |
| 19 | 21 | #include <linux/efi.h> |
| 20 | 22 | #include <linux/random.h> |
| 21 | 23 | |
| @@ -212,6 +214,28 @@ setup_efi_state(struct boot_params *params, unsigned long params_load_addr, · 190 unmodified lines | ||
| 212 | 214 | } |
| 213 | 215 | #endif /* CONFIG_EFI */ |
| 214 | 216 | |
| 217 | +#ifdef CONFIG_OF_FLATTREE | |
| 218 | +static void setup_dtb(struct boot_params *params, | |
| 219 | + unsigned long params_load_addr, | |
| 220 | + unsigned int dtb_setup_data_offset) | |
| 221 | +{ | |
| 222 | + struct setup_data *sd = (void *)params + dtb_setup_data_offset; | |
| 223 | + unsigned long setup_data_phys, dtb_len; | |
| 224 | + | |
| 225 | + dtb_len = fdt_totalsize(initial_boot_params); | |
| 226 | + sd->type = SETUP_DTB; | |
| 227 | + sd->len = dtb_len; | |
| 228 | + | |
| 229 | + /* Carry over current boot DTB with setup_data */ | |
| 230 | + memcpy(sd->data, initial_boot_params, dtb_len); | |
| 231 | + | |
| 232 | + /* Add setup data */ | |
| 233 | + setup_data_phys = params_load_addr + dtb_setup_data_offset; | |
| 234 | + sd->next = params->hdr.setup_data; | |
| 235 | + params->hdr.setup_data = setup_data_phys; | |
| 236 | +} | |
| 237 | +#endif /* CONFIG_OF_FLATTREE */ | |
| 238 | + | |
| 215 | 239 | static void |
| 216 | 240 | setup_ima_state(const struct kimage *image, struct boot_params *params, |
| 217 | 241 | unsigned long params_load_addr, |
| @@ -336,6 +360,17 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params, · 118 unmodified lines | ||
| 336 | 360 | sizeof(struct efi_setup_data); |
| 337 | 361 | #endif |
| 338 | 362 | |
| 363 | +#ifdef CONFIG_OF_FLATTREE | |
| 364 | + if (image->force_dtb && initial_boot_params) { | |
| 365 | + setup_dtb(params, params_load_addr, setup_data_offset); | |
| 366 | + setup_data_offset += sizeof(struct setup_data) + | |
| 367 | + fdt_totalsize(initial_boot_params); | |
| 368 | + } else { | |
| 369 | + pr_debug("Not carrying over DTB, force_dtb = %d\n", | |
| 370 | + image->force_dtb); | |
| 371 | + } | |
| 372 | +#endif | |
| 373 | + | |
| 339 | 374 | if (IS_ENABLED(CONFIG_IMA_KEXEC)) { |
| 340 | 375 | /* Setup IMA log buffer state */ |
| 341 | 376 | setup_ima_state(image, params, params_load_addr, |
| @@ -529,6 +564,12 @@ static void *bzImage64_load(struct kimage *image, char *kernel, · 187 unmodified lines | ||
| 529 | 564 | sizeof(struct setup_data) + |
| 530 | 565 | RNG_SEED_LENGTH; |
| 531 | 566 | |
| 567 | +#ifdef CONFIG_OF_FLATTREE | |
| 568 | + if (image->force_dtb && initial_boot_params) | |
| 569 | + kbuf.bufsz += sizeof(struct setup_data) + | |
| 570 | + fdt_totalsize(initial_boot_params); | |
| 571 | +#endif | |
| 572 | + | |
| 532 | 573 | if (IS_ENABLED(CONFIG_IMA_KEXEC)) |
| 533 | 574 | kbuf.bufsz += sizeof(struct setup_data) + |
| 534 | 575 | sizeof(struct ima_setup_data); |
| @@ -537,7 +578,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel, · 2 unmodified lines | ||
| 537 | 578 | kbuf.bufsz += sizeof(struct setup_data) + |
| 538 | 579 | sizeof(struct kho_data); |
| 539 | 580 | |
| 540 | − params = kzalloc(kbuf.bufsz, GFP_KERNEL); | |
| 581 | + params = kvzalloc(kbuf.bufsz, GFP_KERNEL); | |
| 541 | 582 | if (!params) |
| 542 | 583 | return ERR_PTR(-ENOMEM); |
| 543 | 584 | efi_map_offset = params_cmdline_sz; |
| @@ -647,7 +688,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel, · 103 unmodified lines | ||
| 647 | 688 | return ldata; |
| 648 | 689 | |
| 649 | 690 | out_free_params: |
| 650 | − kfree(params); | |
| 691 | + kvfree(params); | |
| 651 | 692 | return ERR_PTR(ret); |
| 652 | 693 | } |
| 653 | 694 | |
| @@ -659,7 +700,7 @@ static int bzImage64_cleanup(void *loader_data) · 5 unmodified lines | ||
| 659 | 700 | if (!ldata) |
| 660 | 701 | return 0; |
| 661 | 702 | |
| 662 | − kfree(ldata->bootparams_buf); | |
| 703 | + kvfree(ldata->bootparams_buf); | |
| 663 | 704 | ldata->bootparams_buf = NULL; |
| 664 | 705 | |
| 665 | 706 | return 0; |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | #include <linux/efi.h> |
| 13 | 13 | #include <linux/fwnode.h> |
| 14 | 14 | #include <linux/init.h> |
| 15 | +#include <linux/kexec_handover.h> | |
| 15 | 16 | #include <linux/memblock.h> |
| 16 | 17 | #include <linux/mm_types.h> |
| 17 | 18 | #include <linux/of.h> |
| @@ -164,12 +165,32 @@ static __init void reserve_regions(void) · 146 unmodified lines | ||
| 164 | 165 | pr_info("Processing EFI memory map:\n"); |
| 165 | 166 | |
| 166 | 167 | /* |
| 167 | − * Discard memblocks discovered so far: if there are any at this | |
| 168 | − * point, they originate from memory nodes in the DT, and UEFI | |
| 169 | − * uses its own memory map instead. | |
| 168 | + * Discard memblocks discovered so far except for KHO scratch | |
| 169 | + * regions. Most memblocks at this point originate from memory nodes | |
| 170 | + * in the DT and UEFI uses its own memory map instead. However, if | |
| 171 | + * KHO is enabled, scratch regions, which are good known memory | |
| 172 | + * must be preserved. | |
| 170 | 173 | */ |
| 171 | 174 | memblock_dump_all(); |
| 172 | − memblock_remove(0, PHYS_ADDR_MAX); | |
| 175 | + | |
| 176 | + if (is_kho_boot()) { | |
| 177 | + struct memblock_region *r; | |
| 178 | + | |
| 179 | + /* Remove all non-KHO regions */ | |
| 180 | + for_each_mem_region(r) { | |
| 181 | + if (!memblock_is_kho_scratch(r)) { | |
| 182 | + memblock_remove(r->base, r->size); | |
| 183 | + r--; | |
| 184 | + } | |
| 185 | + } | |
| 186 | + } else { | |
| 187 | + /* | |
| 188 | + * KHO is disabled. Discard memblocks discovered so far: | |
| 189 | + * if there are any at this point, they originate from memory | |
| 190 | + * nodes in the DT, and UEFI uses its own memory map instead. | |
| 191 | + */ | |
| 192 | + memblock_remove(0, PHYS_ADDR_MAX); | |
| 193 | + } | |
| 173 | 194 | |
| 174 | 195 | for_each_efi_memory_desc(md) { |
| 175 | 196 | paddr = md->phys_addr; |
| @@ -279,14 +279,7 @@ static int fbcon_get_rotate(struct fb_info *info) · 278 unmodified lines | ||
| 279 | 279 | |
| 280 | 280 | static bool fbcon_skip_panic(struct fb_info *info) |
| 281 | 281 | { |
| 282 | −/* panic_cpu is not exported, and can't be used if built as module. Use | |
| 283 | − * oops_in_progress instead, but non-fatal oops won't be printed. | |
| 284 | − */ | |
| 285 | −#if defined(MODULE) | |
| 286 | − return (info->skip_panic && unlikely(oops_in_progress)); | |
| 287 | −#else | |
| 288 | − return (info->skip_panic && unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID)); | |
| 289 | −#endif | |
| 282 | + return (info->skip_panic && unlikely(panic_in_progress())); | |
| 290 | 283 | } |
| 291 | 284 | |
| 292 | 285 | static inline bool fbcon_is_active(struct vc_data *vc, struct fb_info *info) |
| @@ -421,7 +421,7 @@ static int cramfs_physmem_mmap(struct file *file, struct vm_area_struct *vma) · 420 unmodified lines | ||
| 421 | 421 | vm_fault_t vmf; |
| 422 | 422 | unsigned long off = i * PAGE_SIZE; |
| 423 | 423 | vmf = vmf_insert_mixed(vma, vma->vm_start + off, |
| 424 | − address + off); | |
| 424 | + PHYS_PFN(address + off)); | |
| 425 | 425 | if (vmf & VM_FAULT_ERROR) |
| 426 | 426 | ret = vm_fault_to_errno(vmf, 0); |
| 427 | 427 | } |
| @@ -1209,7 +1209,7 @@ EXPORT_SYMBOL_GPL(fat_alloc_new_dir); · 1208 unmodified lines | ||
| 1209 | 1209 | |
| 1210 | 1210 | static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, |
| 1211 | 1211 | int *nr_cluster, struct msdos_dir_entry **de, |
| 1212 | − struct buffer_head **bh, loff_t *i_pos) | |
| 1212 | + struct buffer_head **bh) | |
| 1213 | 1213 | { |
| 1214 | 1214 | struct super_block *sb = dir->i_sb; |
| 1215 | 1215 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
| @@ -1269,7 +1269,6 @@ static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, · 53 unmodified lines | ||
| 1269 | 1269 | get_bh(bhs[n]); |
| 1270 | 1270 | *bh = bhs[n]; |
| 1271 | 1271 | *de = (struct msdos_dir_entry *)((*bh)->b_data + offset); |
| 1272 | − *i_pos = fat_make_i_pos(sb, *bh, *de); | |
| 1273 | 1272 | |
| 1274 | 1273 | /* Second stage: clear the rest of cluster, and write outs */ |
| 1275 | 1274 | err = fat_zeroed_cluster(dir, start_blknr, ++n, bhs, MAX_BUF_PER_PAGE); |
| @@ -1298,7 +1297,7 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots, · 22 unmodified lines | ||
| 1298 | 1297 | struct buffer_head *bh, *prev, *bhs[3]; /* 32*slots (672bytes) */ |
| 1299 | 1298 | struct msdos_dir_entry *de; |
| 1300 | 1299 | int err, free_slots, i, nr_bhs; |
| 1301 | − loff_t pos, i_pos; | |
| 1300 | + loff_t pos; | |
| 1302 | 1301 | |
| 1303 | 1302 | sinfo->nr_slots = nr_slots; |
| 1304 | 1303 | |
| @@ -1386,7 +1385,7 @@ found: · 81 unmodified lines | ||
| 1386 | 1385 | * add the cluster to dir. |
| 1387 | 1386 | */ |
| 1388 | 1387 | cluster = fat_add_new_entries(dir, slots, nr_slots, &nr_cluster, |
| 1389 | − &de, &bh, &i_pos); | |
| 1388 | + &de, &bh); | |
| 1390 | 1389 | if (cluster < 0) { |
| 1391 | 1390 | err = cluster; |
| 1392 | 1391 | goto error_remove; |
| @@ -6928,8 +6928,7 @@ static int ocfs2_grab_folios(struct inode *inode, loff_t start, loff_t end, · 6927 unmodified lines | ||
| 6928 | 6928 | |
| 6929 | 6929 | out: |
| 6930 | 6930 | if (ret != 0) { |
| 6931 | − if (folios) | |
| 6932 | − ocfs2_unlock_and_free_folios(folios, numfolios); | |
| 6931 | + ocfs2_unlock_and_free_folios(folios, numfolios); | |
| 6933 | 6932 | numfolios = 0; |
| 6934 | 6933 | } |
| 6935 | 6934 | |
| @@ -1477,7 +1477,6 @@ way_up_top: · 1476 unmodified lines | ||
| 1477 | 1477 | goto send_response; |
| 1478 | 1478 | } else if (res->owner != DLM_LOCK_RES_OWNER_UNKNOWN) { |
| 1479 | 1479 | spin_unlock(&res->spinlock); |
| 1480 | − // mlog(0, "node %u is the master\n", res->owner); | |
| 1481 | 1480 | response = DLM_MASTER_RESP_NO; |
| 1482 | 1481 | if (mle) |
| 1483 | 1482 | kmem_cache_free(dlm_mle_cache, mle); |
| @@ -1493,7 +1492,6 @@ way_up_top: · 9 unmodified lines | ||
| 1493 | 1492 | BUG(); |
| 1494 | 1493 | } |
| 1495 | 1494 | |
| 1496 | − // mlog(0, "lockres is in progress...\n"); | |
| 1497 | 1495 | spin_lock(&dlm->master_lock); |
| 1498 | 1496 | found = dlm_find_mle(dlm, &tmpmle, name, namelen); |
| 1499 | 1497 | if (!found) { |
| @@ -1503,8 +1501,6 @@ way_up_top: · 3 unmodified lines | ||
| 1503 | 1501 | set_maybe = 1; |
| 1504 | 1502 | spin_lock(&tmpmle->spinlock); |
| 1505 | 1503 | if (tmpmle->type == DLM_MLE_BLOCK) { |
| 1506 | − // mlog(0, "this node is waiting for " | |
| 1507 | − // "lockres to be mastered\n"); | |
| 1508 | 1504 | response = DLM_MASTER_RESP_NO; |
| 1509 | 1505 | } else if (tmpmle->type == DLM_MLE_MIGRATION) { |
| 1510 | 1506 | mlog(0, "node %u is master, but trying to migrate to " |
| @@ -1531,8 +1527,6 @@ way_up_top: · 20 unmodified lines | ||
| 1531 | 1527 | } else |
| 1532 | 1528 | response = DLM_MASTER_RESP_NO; |
| 1533 | 1529 | } else { |
| 1534 | − // mlog(0, "this node is attempting to " | |
| 1535 | − // "master lockres\n"); | |
| 1536 | 1530 | response = DLM_MASTER_RESP_MAYBE; |
| 1537 | 1531 | } |
| 1538 | 1532 | if (set_maybe) |
| @@ -1559,7 +1553,6 @@ way_up_top: · 20 unmodified lines | ||
| 1559 | 1553 | found = dlm_find_mle(dlm, &tmpmle, name, namelen); |
| 1560 | 1554 | if (!found) { |
| 1561 | 1555 | /* this lockid has never been seen on this node yet */ |
| 1562 | − // mlog(0, "no mle found\n"); | |
| 1563 | 1556 | if (!mle) { |
| 1564 | 1557 | spin_unlock(&dlm->master_lock); |
| 1565 | 1558 | spin_unlock(&dlm->spinlock); |
| @@ -1573,8 +1566,6 @@ way_up_top: · 7 unmodified lines | ||
| 1573 | 1566 | goto way_up_top; |
| 1574 | 1567 | } |
| 1575 | 1568 | |
| 1576 | − // mlog(0, "this is second time thru, already allocated, " | |
| 1577 | − // "add the block.\n"); | |
| 1578 | 1569 | dlm_init_mle(mle, DLM_MLE_BLOCK, dlm, NULL, name, namelen); |
| 1579 | 1570 | set_bit(request->node_idx, mle->maybe_map); |
| 1580 | 1571 | __dlm_insert_mle(dlm, mle); |
| @@ -1897,8 +1888,6 @@ ok: · 316 unmodified lines | ||
| 1897 | 1888 | spin_unlock(&res->spinlock); |
| 1898 | 1889 | } |
| 1899 | 1890 | |
| 1900 | − // mlog(0, "woo! got an assert_master from node %u!\n", | |
| 1901 | − // assert->node_idx); | |
| 1902 | 1891 | if (mle) { |
| 1903 | 1892 | int extra_ref = 0; |
| 1904 | 1893 | int nn = -1; |
| @@ -464,7 +464,6 @@ static int dlm_do_recovery(struct dlm_ctxt *dlm) · 463 unmodified lines | ||
| 464 | 464 | } |
| 465 | 465 | |
| 466 | 466 | if (dlm->reco.dead_node == O2NM_INVALID_NODE_NUM) { |
| 467 | − // mlog(0, "nothing to recover! sleeping now!\n"); | |
| 468 | 467 | spin_unlock(&dlm->spinlock); |
| 469 | 468 | /* return to main thread loop and sleep. */ |
| 470 | 469 | return 0; |
| @@ -1495,6 +1495,14 @@ int ocfs2_validate_inode_block(struct super_block *sb, · 1494 unmodified lines | ||
| 1495 | 1495 | goto bail; |
| 1496 | 1496 | } |
| 1497 | 1497 | |
| 1498 | + if (le16_to_cpu(di->i_suballoc_slot) != (u16)OCFS2_INVALID_SLOT && | |
| 1499 | + (u32)le16_to_cpu(di->i_suballoc_slot) > OCFS2_SB(sb)->max_slots - 1) { | |
| 1500 | + rc = ocfs2_error(sb, "Invalid dinode %llu: suballoc slot %u\n", | |
| 1501 | + (unsigned long long)bh->b_blocknr, | |
| 1502 | + le16_to_cpu(di->i_suballoc_slot)); | |
| 1503 | + goto bail; | |
| 1504 | + } | |
| 1505 | + | |
| 1498 | 1506 | rc = 0; |
| 1499 | 1507 | |
| 1500 | 1508 | bail: |
| @@ -358,13 +358,11 @@ static int ocfs2_info_handle_freeinode(struct inode *inode, · 357 unmodified lines | ||
| 358 | 358 | goto bail; |
| 359 | 359 | } |
| 360 | 360 | } else { |
| 361 | − ocfs2_sprintf_system_inode_name(namebuf, | |
| 362 | − sizeof(namebuf), | |
| 363 | − type, i); | |
| 361 | + int len = ocfs2_sprintf_system_inode_name(namebuf, | |
| 362 | + sizeof(namebuf), | |
| 363 | + type, i); | |
| 364 | 364 | status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, |
| 365 | − namebuf, | |
| 366 | − strlen(namebuf), | |
| 367 | − &blkno); | |
| 365 | + namebuf, len, &blkno); | |
| 368 | 366 | if (status < 0) { |
| 369 | 367 | status = -ENOENT; |
| 370 | 368 | goto bail; |
| @@ -651,12 +649,10 @@ static int ocfs2_info_handle_freefrag(struct inode *inode, · 280 unmodified lines | ||
| 651 | 649 | goto bail; |
| 652 | 650 | } |
| 653 | 651 | } else { |
| 654 | − ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, | |
| 655 | − OCFS2_INVALID_SLOT); | |
| 652 | + int len = ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), | |
| 653 | + type, OCFS2_INVALID_SLOT); | |
| 656 | 654 | status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, |
| 657 | − namebuf, | |
| 658 | − strlen(namebuf), | |
| 659 | − &blkno); | |
| 655 | + namebuf, len, &blkno); | |
| 660 | 656 | if (status < 0) { |
| 661 | 657 | status = -ENOENT; |
| 662 | 658 | goto bail; |
| @@ -364,7 +364,7 @@ static int ocfs2_find_victim_alloc_group(struct inode *inode, · 363 unmodified lines | ||
| 364 | 364 | int *vict_bit, |
| 365 | 365 | struct buffer_head **ret_bh) |
| 366 | 366 | { |
| 367 | − int ret, i, bits_per_unit = 0; | |
| 367 | + int ret, i, len, bits_per_unit = 0; | |
| 368 | 368 | u64 blkno; |
| 369 | 369 | char namebuf[40]; |
| 370 | 370 | |
| @@ -375,9 +375,9 @@ static int ocfs2_find_victim_alloc_group(struct inode *inode, · 4 unmodified lines | ||
| 375 | 375 | struct ocfs2_dinode *ac_dinode; |
| 376 | 376 | struct ocfs2_group_desc *bg; |
| 377 | 377 | |
| 378 | − ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, slot); | |
| 379 | − ret = ocfs2_lookup_ino_from_name(osb->sys_root_inode, namebuf, | |
| 380 | − strlen(namebuf), &blkno); | |
| 378 | + len = ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, slot); | |
| 379 | + ret = ocfs2_lookup_ino_from_name(osb->sys_root_inode, namebuf, len, &blkno); | |
| 380 | + | |
| 381 | 381 | if (ret) { |
| 382 | 382 | ret = -ENOENT; |
| 383 | 383 | goto out; |
| @@ -614,7 +614,7 @@ struct ocfs2_super_block { · 613 unmodified lines | ||
| 614 | 614 | __le16 s_reserved0; |
| 615 | 615 | __le32 s_dx_seed[3]; /* seed[0-2] for dx dir hash. |
| 616 | 616 | * s_uuid_hash serves as seed[3]. */ |
| 617 | −/*C0*/ __le64 s_reserved2[15]; /* Fill out superblock */ | |
| 617 | +/*C8*/ __le64 s_reserved2[15]; /* Fill out superblock */ | |
| 618 | 618 | /*140*/ |
| 619 | 619 | |
| 620 | 620 | /* |
| @@ -1011,6 +1011,7 @@ static int user_cluster_connect(struct ocfs2_cluster_connection *conn) · 1010 unmodified lines | ||
| 1011 | 1011 | printk(KERN_ERR "ocfs2: Could not determine" |
| 1012 | 1012 | " locking version\n"); |
| 1013 | 1013 | user_cluster_disconnect(conn); |
| 1014 | + lc = NULL; | |
| 1014 | 1015 | goto out; |
| 1015 | 1016 | } |
| 1016 | 1017 | wait_event(lc->oc_wait, (atomic_read(&lc->oc_this_node) > 0)); |
| @@ -127,14 +127,14 @@ static struct inode * _ocfs2_get_system_file_inode(struct ocfs2_super *osb, · 126 unmodified lines | ||
| 127 | 127 | char namebuf[40]; |
| 128 | 128 | struct inode *inode = NULL; |
| 129 | 129 | u64 blkno; |
| 130 | − int status = 0; | |
| 130 | + int len, status = 0; | |
| 131 | 131 | |
| 132 | − ocfs2_sprintf_system_inode_name(namebuf, | |
| 133 | − sizeof(namebuf), | |
| 134 | − type, slot); | |
| 132 | + len = ocfs2_sprintf_system_inode_name(namebuf, | |
| 133 | + sizeof(namebuf), | |
| 134 | + type, slot); | |
| 135 | 135 | |
| 136 | − status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, namebuf, | |
| 137 | − strlen(namebuf), &blkno); | |
| 136 | + status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, | |
| 137 | + namebuf, len, &blkno); | |
| 138 | 138 | if (status < 0) { |
| 139 | 139 | goto bail; |
| 140 | 140 | } |
| @@ -3947,7 +3947,7 @@ static int proc_task_readdir(struct file *file, struct dir_context *ctx) · 3946 unmodified lines | ||
| 3947 | 3947 | tid = task_pid_nr_ns(task, ns); |
| 3948 | 3948 | if (!tid) |
| 3949 | 3949 | continue; /* The task has just exited. */ |
| 3950 | − len = snprintf(name, sizeof(name), "%u", tid); | |
| 3950 | + len = snprintf(name, sizeof(name), "%d", tid); | |
| 3951 | 3951 | if (!proc_fill_cache(file, ctx, name, len, |
| 3952 | 3952 | proc_task_instantiate, task, NULL)) { |
| 3953 | 3953 | /* returning this tgid failed, save it as the first |
| @@ -307,7 +307,8 @@ static int fill_meta_index(struct inode *inode, int index, · 306 unmodified lines | ||
| 307 | 307 | all_done: |
| 308 | 308 | *index_block = cur_index_block; |
| 309 | 309 | *index_offset = cur_offset; |
| 310 | − *data_block = cur_data_block; | |
| 310 | + if (data_block) | |
| 311 | + *data_block = cur_data_block; | |
| 311 | 312 | |
| 312 | 313 | /* |
| 313 | 314 | * Scale cache index (cache slot entry) to index |
| @@ -324,17 +325,15 @@ failed: · 10 unmodified lines | ||
| 324 | 325 | * Get the on-disk location and compressed size of the datablock |
| 325 | 326 | * specified by index. Fill_meta_index() does most of the work. |
| 326 | 327 | */ |
| 327 | −static int read_blocklist(struct inode *inode, int index, u64 *block) | |
| 328 | +static int read_blocklist_ptrs(struct inode *inode, int index, u64 *start, | |
| 329 | + int *offset, u64 *block) | |
| 328 | 330 | { |
| 329 | − u64 start; | |
| 330 | 331 | long long blks; |
| 331 | − int offset; | |
| 332 | 332 | __le32 size; |
| 333 | − int res = fill_meta_index(inode, index, &start, &offset, block); | |
| 333 | + int res = fill_meta_index(inode, index, start, offset, block); | |
| 334 | 334 | |
| 335 | − TRACE("read_blocklist: res %d, index %d, start 0x%llx, offset" | |
| 336 | − " 0x%x, block 0x%llx\n", res, index, start, offset, | |
| 337 | − *block); | |
| 335 | + TRACE("read_blocklist: res %d, index %d, start 0x%llx, offset 0x%x, block 0x%llx\n", | |
| 336 | + res, index, *start, *offset, block ? *block : 0); | |
| 338 | 337 | |
| 339 | 338 | if (res < 0) |
| 340 | 339 | return res; |
| @@ -346,22 +345,31 @@ static int read_blocklist(struct inode *inode, int index, u64 *block) · 5 unmodified lines | ||
| 346 | 345 | * extra block indexes needed. |
| 347 | 346 | */ |
| 348 | 347 | if (res < index) { |
| 349 | − blks = read_indexes(inode->i_sb, index - res, &start, &offset); | |
| 348 | + blks = read_indexes(inode->i_sb, index - res, start, offset); | |
| 350 | 349 | if (blks < 0) |
| 351 | 350 | return (int) blks; |
| 352 | − *block += blks; | |
| 351 | + if (block) | |
| 352 | + *block += blks; | |
| 353 | 353 | } |
| 354 | 354 | |
| 355 | 355 | /* |
| 356 | 356 | * Read length of block specified by index. |
| 357 | 357 | */ |
| 358 | − res = squashfs_read_metadata(inode->i_sb, &size, &start, &offset, | |
| 358 | + res = squashfs_read_metadata(inode->i_sb, &size, start, offset, | |
| 359 | 359 | sizeof(size)); |
| 360 | 360 | if (res < 0) |
| 361 | 361 | return res; |
| 362 | 362 | return squashfs_block_size(size); |
| 363 | 363 | } |
| 364 | 364 | |
| 365 | +static inline int read_blocklist(struct inode *inode, int index, u64 *block) | |
| 366 | +{ | |
| 367 | + u64 start; | |
| 368 | + int offset; | |
| 369 | + | |
| 370 | + return read_blocklist_ptrs(inode, index, &start, &offset, block); | |
| 371 | +} | |
| 372 | + | |
| 365 | 373 | static bool squashfs_fill_page(struct folio *folio, |
| 366 | 374 | struct squashfs_cache_entry *buffer, size_t offset, |
| 367 | 375 | size_t avail) |
| @@ -658,7 +666,114 @@ skip_pages: · 290 unmodified lines | ||
| 658 | 666 | kfree(pages); |
| 659 | 667 | } |
| 660 | 668 | |
| 669 | +static loff_t seek_hole_data(struct file *file, loff_t offset, int whence) | |
| 670 | +{ | |
| 671 | + struct inode *inode = file->f_mapping->host; | |
| 672 | + struct super_block *sb = inode->i_sb; | |
| 673 | + struct squashfs_sb_info *msblk = sb->s_fs_info; | |
| 674 | + u64 start, index = offset >> msblk->block_log; | |
| 675 | + u64 file_end = (i_size_read(inode) + msblk->block_size - 1) >> msblk->block_log; | |
| 676 | + int s_offset, length; | |
| 677 | + __le32 *blist = NULL; | |
| 678 | + | |
| 679 | + /* reject offset if negative or beyond file end */ | |
| 680 | + if ((unsigned long long)offset >= i_size_read(inode)) | |
| 681 | + return -ENXIO; | |
| 682 | + | |
| 683 | + /* is offset within tailend and is tailend packed into a fragment? */ | |
| 684 | + if (index + 1 == file_end && | |
| 685 | + squashfs_i(inode)->fragment_block != SQUASHFS_INVALID_BLK) { | |
| 686 | + if (whence == SEEK_DATA) | |
| 687 | + return offset; | |
| 688 | + | |
| 689 | + /* there is an implicit hole at the end of any file */ | |
| 690 | + return i_size_read(inode); | |
| 691 | + } | |
| 692 | + | |
| 693 | + length = read_blocklist_ptrs(inode, index, &start, &s_offset, NULL); | |
| 694 | + if (length < 0) | |
| 695 | + return length; | |
| 696 | + | |
| 697 | + /* nothing more to do if offset matches desired whence value */ | |
| 698 | + if ((length == 0 && whence == SEEK_HOLE) || | |
| 699 | + (length && whence == SEEK_DATA)) | |
| 700 | + return offset; | |
| 701 | + | |
| 702 | + /* skip scanning forwards if we're at file end */ | |
| 703 | + if (++ index == file_end) | |
| 704 | + goto not_found; | |
| 705 | + | |
| 706 | + blist = kmalloc(SQUASHFS_SCAN_INDEXES << 2, GFP_KERNEL); | |
| 707 | + if (blist == NULL) { | |
| 708 | + ERROR("%s: Failed to allocate block_list\n", __func__); | |
| 709 | + return -ENOMEM; | |
| 710 | + } | |
| 711 | + | |
| 712 | + while (index < file_end) { | |
| 713 | + int i, indexes = min(file_end - index, SQUASHFS_SCAN_INDEXES); | |
| 714 | + | |
| 715 | + offset = squashfs_read_metadata(sb, blist, &start, &s_offset, indexes << 2); | |
| 716 | + if (offset < 0) | |
| 717 | + goto finished; | |
| 718 | + | |
| 719 | + for (i = 0; i < indexes; i++) { | |
| 720 | + length = squashfs_block_size(blist[i]); | |
| 721 | + if (length < 0) { | |
| 722 | + offset = length; | |
| 723 | + goto finished; | |
| 724 | + } | |
| 725 | + | |
| 726 | + /* does this block match desired whence value? */ | |
| 727 | + if ((length == 0 && whence == SEEK_HOLE) || | |
| 728 | + (length && whence == SEEK_DATA)) { | |
| 729 | + offset = (index + i) << msblk->block_log; | |
| 730 | + goto finished; | |
| 731 | + } | |
| 732 | + } | |
| 733 | + | |
| 734 | + index += indexes; | |
| 735 | + } | |
| 736 | + | |
| 737 | +not_found: | |
| 738 | + /* whence value determines what happens */ | |
| 739 | + if (whence == SEEK_DATA) | |
| 740 | + offset = -ENXIO; | |
| 741 | + else | |
| 742 | + /* there is an implicit hole at the end of any file */ | |
| 743 | + offset = i_size_read(inode); | |
| 744 | + | |
| 745 | +finished: | |
| 746 | + kfree(blist); | |
| 747 | + return offset; | |
| 748 | +} | |
| 749 | + | |
| 750 | +static loff_t squashfs_llseek(struct file *file, loff_t offset, int whence) | |
| 751 | +{ | |
| 752 | + struct inode *inode = file->f_mapping->host; | |
| 753 | + | |
| 754 | + switch (whence) { | |
| 755 | + default: | |
| 756 | + return generic_file_llseek(file, offset, whence); | |
| 757 | + case SEEK_DATA: | |
| 758 | + case SEEK_HOLE: | |
| 759 | + offset = seek_hole_data(file, offset, whence); | |
| 760 | + break; | |
| 761 | + } | |
| 762 | + | |
| 763 | + if (offset < 0) | |
| 764 | + return offset; | |
| 765 | + | |
| 766 | + return vfs_setpos(file, offset, inode->i_sb->s_maxbytes); | |
| 767 | +} | |
| 768 | + | |
| 661 | 769 | const struct address_space_operations squashfs_aops = { |
| 662 | 770 | .read_folio = squashfs_read_folio, |
| 663 | 771 | .readahead = squashfs_readahead |
| 664 | 772 | }; |
| 773 | + | |
| 774 | +const struct file_operations squashfs_file_operations = { | |
| 775 | + .llseek = squashfs_llseek, | |
| 776 | + .read_iter = generic_file_read_iter, | |
| 777 | + .mmap_prepare = generic_file_readonly_mmap_prepare, | |
| 778 | + .splice_read = filemap_splice_read | |
| 779 | +}; | |
| @@ -68,6 +68,10 @@ static int squashfs_new_inode(struct super_block *sb, struct inode *inode, · 67 unmodified lines | ||
| 68 | 68 | inode->i_mode = le16_to_cpu(sqsh_ino->mode); |
| 69 | 69 | inode->i_size = 0; |
| 70 | 70 | |
| 71 | + /* File type must not be set at this moment, for it will later be set by the caller. */ | |
| 72 | + if (inode->i_mode & S_IFMT) | |
| 73 | + err = -EIO; | |
| 74 | + | |
| 71 | 75 | return err; |
| 72 | 76 | } |
| 73 | 77 | |
| @@ -140,8 +144,17 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 66 unmodified lines | ||
| 140 | 144 | if (err < 0) |
| 141 | 145 | goto failed_read; |
| 142 | 146 | |
| 147 | + inode->i_size = le32_to_cpu(sqsh_ino->file_size); | |
| 143 | 148 | frag = le32_to_cpu(sqsh_ino->fragment); |
| 144 | 149 | if (frag != SQUASHFS_INVALID_FRAG) { |
| 150 | + /* | |
| 151 | + * the file cannot have a fragment (tailend) and have a | |
| 152 | + * file size a multiple of the block size | |
| 153 | + */ | |
| 154 | + if ((inode->i_size & (msblk->block_size - 1)) == 0) { | |
| 155 | + err = -EINVAL; | |
| 156 | + goto failed_read; | |
| 157 | + } | |
| 145 | 158 | frag_offset = le32_to_cpu(sqsh_ino->offset); |
| 146 | 159 | frag_size = squashfs_frag_lookup(sb, frag, &frag_blk); |
| 147 | 160 | if (frag_size < 0) { |
| @@ -155,8 +168,7 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 7 unmodified lines | ||
| 155 | 168 | } |
| 156 | 169 | |
| 157 | 170 | set_nlink(inode, 1); |
| 158 | − inode->i_size = le32_to_cpu(sqsh_ino->file_size); | |
| 159 | − inode->i_fop = &generic_ro_fops; | |
| 171 | + inode->i_fop = &squashfs_file_operations; | |
| 160 | 172 | inode->i_mode |= S_IFREG; |
| 161 | 173 | inode->i_blocks = ((inode->i_size - 1) >> 9) + 1; |
| 162 | 174 | squashfs_i(inode)->fragment_block = frag_blk; |
| @@ -165,6 +177,7 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 2 unmodified lines | ||
| 165 | 177 | squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block); |
| 166 | 178 | squashfs_i(inode)->block_list_start = block; |
| 167 | 179 | squashfs_i(inode)->offset = offset; |
| 180 | + squashfs_i(inode)->parent = 0; | |
| 168 | 181 | inode->i_data.a_ops = &squashfs_aops; |
| 169 | 182 | |
| 170 | 183 | TRACE("File inode %x:%x, start_block %llx, block_list_start " |
| @@ -183,8 +196,21 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 12 unmodified lines | ||
| 183 | 196 | if (err < 0) |
| 184 | 197 | goto failed_read; |
| 185 | 198 | |
| 199 | + inode->i_size = le64_to_cpu(sqsh_ino->file_size); | |
| 200 | + if (inode->i_size < 0) { | |
| 201 | + err = -EINVAL; | |
| 202 | + goto failed_read; | |
| 203 | + } | |
| 186 | 204 | frag = le32_to_cpu(sqsh_ino->fragment); |
| 187 | 205 | if (frag != SQUASHFS_INVALID_FRAG) { |
| 206 | + /* | |
| 207 | + * the file cannot have a fragment (tailend) and have a | |
| 208 | + * file size a multiple of the block size | |
| 209 | + */ | |
| 210 | + if ((inode->i_size & (msblk->block_size - 1)) == 0) { | |
| 211 | + err = -EINVAL; | |
| 212 | + goto failed_read; | |
| 213 | + } | |
| 188 | 214 | frag_offset = le32_to_cpu(sqsh_ino->offset); |
| 189 | 215 | frag_size = squashfs_frag_lookup(sb, frag, &frag_blk); |
| 190 | 216 | if (frag_size < 0) { |
| @@ -199,9 +225,8 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 8 unmodified lines | ||
| 199 | 225 | |
| 200 | 226 | xattr_id = le32_to_cpu(sqsh_ino->xattr); |
| 201 | 227 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); |
| 202 | − inode->i_size = le64_to_cpu(sqsh_ino->file_size); | |
| 203 | 228 | inode->i_op = &squashfs_inode_ops; |
| 204 | − inode->i_fop = &generic_ro_fops; | |
| 229 | + inode->i_fop = &squashfs_file_operations; | |
| 205 | 230 | inode->i_mode |= S_IFREG; |
| 206 | 231 | inode->i_blocks = (inode->i_size - |
| 207 | 232 | le64_to_cpu(sqsh_ino->sparse) + 511) >> 9; |
| @@ -212,6 +237,7 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 4 unmodified lines | ||
| 212 | 237 | squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block); |
| 213 | 238 | squashfs_i(inode)->block_list_start = block; |
| 214 | 239 | squashfs_i(inode)->offset = offset; |
| 240 | + squashfs_i(inode)->parent = 0; | |
| 215 | 241 | inode->i_data.a_ops = &squashfs_aops; |
| 216 | 242 | |
| 217 | 243 | TRACE("File inode %x:%x, start_block %llx, block_list_start " |
| @@ -292,6 +318,7 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 74 unmodified lines | ||
| 292 | 318 | inode->i_mode |= S_IFLNK; |
| 293 | 319 | squashfs_i(inode)->start = block; |
| 294 | 320 | squashfs_i(inode)->offset = offset; |
| 321 | + squashfs_i(inode)->parent = 0; | |
| 295 | 322 | |
| 296 | 323 | if (type == SQUASHFS_LSYMLINK_TYPE) { |
| 297 | 324 | __le32 xattr; |
| @@ -329,6 +356,7 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 31 unmodified lines | ||
| 329 | 356 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); |
| 330 | 357 | rdev = le32_to_cpu(sqsh_ino->rdev); |
| 331 | 358 | init_special_inode(inode, inode->i_mode, new_decode_dev(rdev)); |
| 359 | + squashfs_i(inode)->parent = 0; | |
| 332 | 360 | |
| 333 | 361 | TRACE("Device inode %x:%x, rdev %x\n", |
| 334 | 362 | SQUASHFS_INODE_BLK(ino), offset, rdev); |
| @@ -353,6 +381,7 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 18 unmodified lines | ||
| 353 | 381 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); |
| 354 | 382 | rdev = le32_to_cpu(sqsh_ino->rdev); |
| 355 | 383 | init_special_inode(inode, inode->i_mode, new_decode_dev(rdev)); |
| 384 | + squashfs_i(inode)->parent = 0; | |
| 356 | 385 | |
| 357 | 386 | TRACE("Device inode %x:%x, rdev %x\n", |
| 358 | 387 | SQUASHFS_INODE_BLK(ino), offset, rdev); |
| @@ -373,6 +402,7 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 14 unmodified lines | ||
| 373 | 402 | inode->i_mode |= S_IFSOCK; |
| 374 | 403 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); |
| 375 | 404 | init_special_inode(inode, inode->i_mode, 0); |
| 405 | + squashfs_i(inode)->parent = 0; | |
| 376 | 406 | break; |
| 377 | 407 | } |
| 378 | 408 | case SQUASHFS_LFIFO_TYPE: |
| @@ -392,6 +422,7 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 13 unmodified lines | ||
| 392 | 422 | inode->i_op = &squashfs_inode_ops; |
| 393 | 423 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); |
| 394 | 424 | init_special_inode(inode, inode->i_mode, 0); |
| 425 | + squashfs_i(inode)->parent = 0; | |
| 395 | 426 | break; |
| 396 | 427 | } |
| 397 | 428 | default: |
| @@ -107,6 +107,7 @@ extern const struct address_space_operations squashfs_aops; · 106 unmodified lines | ||
| 107 | 107 | |
| 108 | 108 | /* inode.c */ |
| 109 | 109 | extern const struct inode_operations squashfs_inode_ops; |
| 110 | +extern const struct file_operations squashfs_file_operations; | |
| 110 | 111 | |
| 111 | 112 | /* namei.c */ |
| 112 | 113 | extern const struct inode_operations squashfs_dir_inode_ops; |
| @@ -208,6 +208,7 @@ static inline int squashfs_block_size(__le32 raw) · 207 unmodified lines | ||
| 208 | 208 | #define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) |
| 209 | 209 | #define SQUASHFS_META_ENTRIES 127 |
| 210 | 210 | #define SQUASHFS_META_SLOTS 8 |
| 211 | +#define SQUASHFS_SCAN_INDEXES 1024 | |
| 211 | 212 | |
| 212 | 213 | struct meta_entry { |
| 213 | 214 | u64 data_block; |
| @@ -16,6 +16,7 @@ struct squashfs_inode_info { · 15 unmodified lines | ||
| 16 | 16 | u64 xattr; |
| 17 | 17 | unsigned int xattr_size; |
| 18 | 18 | int xattr_count; |
| 19 | + int parent; | |
| 19 | 20 | union { |
| 20 | 21 | struct { |
| 21 | 22 | u64 fragment_block; |
| @@ -27,7 +28,6 @@ struct squashfs_inode_info { · 5 unmodified lines | ||
| 27 | 28 | u64 dir_idx_start; |
| 28 | 29 | int dir_idx_offset; |
| 29 | 30 | int dir_idx_cnt; |
| 30 | − int parent; | |
| 31 | 31 | }; |
| 32 | 32 | }; |
| 33 | 33 | struct inode vfs_inode; |
| @@ -334,14 +334,6 @@ static inline void ida_init(struct ida *ida) · 333 unmodified lines | ||
| 334 | 334 | xa_init_flags(&ida->xa, IDA_INIT_FLAGS); |
| 335 | 335 | } |
| 336 | 336 | |
| 337 | −/* | |
| 338 | − * ida_simple_get() and ida_simple_remove() are deprecated. Use | |
| 339 | − * ida_alloc() and ida_free() instead respectively. | |
| 340 | − */ | |
| 341 | −#define ida_simple_get(ida, start, end, gfp) \ | |
| 342 | − ida_alloc_range(ida, start, (end) - 1, gfp) | |
| 343 | −#define ida_simple_remove(ida, id) ida_free(ida, id) | |
| 344 | − | |
| 345 | 337 | static inline bool ida_is_empty(const struct ida *ida) |
| 346 | 338 | { |
| 347 | 339 | return xa_empty(&ida->xa); |
| @@ -164,11 +164,23 @@ extern int root_mountflags; · 163 unmodified lines | ||
| 164 | 164 | |
| 165 | 165 | extern bool early_boot_irqs_disabled; |
| 166 | 166 | |
| 167 | −/* | |
| 168 | − * Values used for system_state. Ordering of the states must not be changed | |
| 167 | +/** | |
| 168 | + * enum system_states - Values used for system_state. | |
| 169 | + * | |
| 170 | + * @SYSTEM_BOOTING: %0, no init needed | |
| 171 | + * @SYSTEM_SCHEDULING: system is ready for scheduling; OK to use RCU | |
| 172 | + * @SYSTEM_FREEING_INITMEM: system is freeing all of initmem; almost running | |
| 173 | + * @SYSTEM_RUNNING: system is up and running | |
| 174 | + * @SYSTEM_HALT: system entered clean system halt state | |
| 175 | + * @SYSTEM_POWER_OFF: system entered shutdown/clean power off state | |
| 176 | + * @SYSTEM_RESTART: system entered emergency power off or normal restart | |
| 177 | + * @SYSTEM_SUSPEND: system entered suspend or hibernate state | |
| 178 | + * | |
| 179 | + * Note: | |
| 180 | + * Ordering of the states must not be changed | |
| 169 | 181 | * as code checks for <, <=, >, >= STATE. |
| 170 | 182 | */ |
| 171 | −extern enum system_states { | |
| 183 | +enum system_states { | |
| 172 | 184 | SYSTEM_BOOTING, |
| 173 | 185 | SYSTEM_SCHEDULING, |
| 174 | 186 | SYSTEM_FREEING_INITMEM, |
| @@ -177,7 +189,8 @@ extern enum system_states { · 2 unmodified lines | ||
| 177 | 189 | SYSTEM_POWER_OFF, |
| 178 | 190 | SYSTEM_RESTART, |
| 179 | 191 | SYSTEM_SUSPEND, |
| 180 | −} system_state; | |
| 192 | +}; | |
| 193 | +extern enum system_states system_state; | |
| 181 | 194 | |
| 182 | 195 | /* |
| 183 | 196 | * General tracing related utility functions - trace_printk(), |
| @@ -395,6 +395,9 @@ struct kimage { · 394 unmodified lines | ||
| 395 | 395 | |
| 396 | 396 | /* Information for loading purgatory */ |
| 397 | 397 | struct purgatory_info purgatory_info; |
| 398 | + | |
| 399 | + /* Force carrying over the DTB from the current boot */ | |
| 400 | + bool force_dtb; | |
| 398 | 401 | #endif |
| 399 | 402 | |
| 400 | 403 | #ifdef CONFIG_CRASH_HOTPLUG |
| @@ -461,7 +464,7 @@ bool kexec_load_permitted(int kexec_image_type); · 60 unmodified lines | ||
| 461 | 464 | /* List of defined/legal kexec file flags */ |
| 462 | 465 | #define KEXEC_FILE_FLAGS (KEXEC_FILE_UNLOAD | KEXEC_FILE_ON_CRASH | \ |
| 463 | 466 | KEXEC_FILE_NO_INITRAMFS | KEXEC_FILE_DEBUG | \ |
| 464 | − KEXEC_FILE_NO_CMA) | |
| 467 | + KEXEC_FILE_NO_CMA | KEXEC_FILE_FORCE_DTB) | |
| 465 | 468 | |
| 466 | 469 | /* flag to track if kexec reboot is in progress */ |
| 467 | 470 | extern bool kexec_in_progress; |
| @@ -40,6 +40,7 @@ struct kho_serialization; · 39 unmodified lines | ||
| 40 | 40 | |
| 41 | 41 | #ifdef CONFIG_KEXEC_HANDOVER |
| 42 | 42 | bool kho_is_enabled(void); |
| 43 | +bool is_kho_boot(void); | |
| 43 | 44 | |
| 44 | 45 | int kho_preserve_folio(struct folio *folio); |
| 45 | 46 | int kho_preserve_phys(phys_addr_t phys, size_t size); |
| @@ -60,6 +61,11 @@ static inline bool kho_is_enabled(void) · 14 unmodified lines | ||
| 60 | 61 | return false; |
| 61 | 62 | } |
| 62 | 63 | |
| 64 | +static inline bool is_kho_boot(void) | |
| 65 | +{ | |
| 66 | + return false; | |
| 67 | +} | |
| 68 | + | |
| 63 | 69 | static inline int kho_preserve_folio(struct folio *folio) |
| 64 | 70 | { |
| 65 | 71 | return -EOPNOTSUPP; |
| @@ -20,8 +20,16 @@ · 19 unmodified lines | ||
| 20 | 20 | * using the generic single-entry routines. |
| 21 | 21 | */ |
| 22 | 22 | |
| 23 | +/** | |
| 24 | + * LIST_HEAD_INIT - initialize a &struct list_head's links to point to itself | |
| 25 | + * @name: name of the list_head | |
| 26 | + */ | |
| 23 | 27 | #define LIST_HEAD_INIT(name) { &(name), &(name) } |
| 24 | 28 | |
| 29 | +/** | |
| 30 | + * LIST_HEAD - definition of a &struct list_head with initialization values | |
| 31 | + * @name: name of the list_head | |
| 32 | + */ | |
| 25 | 33 | #define LIST_HEAD(name) \ |
| 26 | 34 | struct list_head name = LIST_HEAD_INIT(name) |
| 27 | 35 | |
| @@ -349,6 +349,19 @@ static inline void kernel_param_unlock(struct module *mod) · 348 unmodified lines | ||
| 349 | 349 | __module_param_call("", name, ¶m_ops_##type, &var, perm, \ |
| 350 | 350 | -1, KERNEL_PARAM_FL_UNSAFE) |
| 351 | 351 | |
| 352 | +/** | |
| 353 | + * __core_param_cb - similar like core_param, with a set/get ops instead of type. | |
| 354 | + * @name: the name of the cmdline and sysfs parameter (often the same as var) | |
| 355 | + * @var: the variable | |
| 356 | + * @ops: the set & get operations for this parameter. | |
| 357 | + * @perm: visibility in sysfs | |
| 358 | + * | |
| 359 | + * Ideally this should be called 'core_param_cb', but the name has been | |
| 360 | + * used for module core parameter, so add the '__' prefix | |
| 361 | + */ | |
| 362 | +#define __core_param_cb(name, ops, arg, perm) \ | |
| 363 | + __module_param_call("", name, ops, arg, perm, -1, 0) | |
| 364 | + | |
| 352 | 365 | #endif /* !MODULE */ |
| 353 | 366 | |
| 354 | 367 | /** |
| @@ -103,7 +103,7 @@ struct nvmem_cell_info { · 102 unmodified lines | ||
| 103 | 103 | * |
| 104 | 104 | * Note: A default "nvmem<id>" name will be assigned to the device if |
| 105 | 105 | * no name is specified in its configuration. In such case "<id>" is |
| 106 | − * generated with ida_simple_get() and provided id field is ignored. | |
| 106 | + * generated with ida_alloc() and provided id field is ignored. | |
| 107 | 107 | * |
| 108 | 108 | * Note: Specifying name and setting id to -1 implies a unique device |
| 109 | 109 | * whose name is provided as-is (kept unaltered). |
| @@ -43,6 +43,12 @@ void abort(void); · 42 unmodified lines | ||
| 43 | 43 | extern atomic_t panic_cpu; |
| 44 | 44 | #define PANIC_CPU_INVALID -1 |
| 45 | 45 | |
| 46 | +bool panic_try_start(void); | |
| 47 | +void panic_reset(void); | |
| 48 | +bool panic_in_progress(void); | |
| 49 | +bool panic_on_this_cpu(void); | |
| 50 | +bool panic_on_other_cpu(void); | |
| 51 | + | |
| 46 | 52 | /* |
| 47 | 53 | * Only to be used by arch init code. If the user over-wrote the default |
| 48 | 54 | * CONFIG_PANIC_TIMEOUT, honor it. |
| @@ -330,8 +330,6 @@ static inline bool pr_flush(int timeout_ms, bool reset_on_progress) · 329 unmodified lines | ||
| 330 | 330 | |
| 331 | 331 | #endif |
| 332 | 332 | |
| 333 | −bool this_cpu_in_panic(void); | |
| 334 | − | |
| 335 | 333 | #ifdef CONFIG_SMP |
| 336 | 334 | extern int __printk_cpu_sync_try_get(void); |
| 337 | 335 | extern void __printk_cpu_sync_wait(void); |
| @@ -210,9 +210,8 @@ static inline struct vm_struct *task_stack_vm_area(const struct task_struct *t) · 209 unmodified lines | ||
| 210 | 210 | * pins the final release of task.io_context. Also protects ->cpuset and |
| 211 | 211 | * ->cgroup.subsys[]. And ->vfork_done. And ->sysvshm.shm_clist. |
| 212 | 212 | * |
| 213 | − * Nests both inside and outside of read_lock(&tasklist_lock). | |
| 214 | − * It must not be nested with write_lock_irq(&tasklist_lock), | |
| 215 | − * neither inside nor outside. | |
| 213 | + * Nests inside of read_lock(&tasklist_lock). It must not be nested with | |
| 214 | + * write_lock_irq(&tasklist_lock), neither inside nor outside. | |
| 216 | 215 | */ |
| 217 | 216 | static inline void task_lock(struct task_struct *p) |
| 218 | 217 | { |
| @@ -22,12 +22,16 @@ · 21 unmodified lines | ||
| 22 | 22 | * KEXEC_FILE_ON_CRASH : Load/unload operation belongs to kdump image. |
| 23 | 23 | * KEXEC_FILE_NO_INITRAMFS : No initramfs is being loaded. Ignore the initrd |
| 24 | 24 | * fd field. |
| 25 | + * KEXEC_FILE_FORCE_DTB : Force carrying over the current boot's DTB to the new | |
| 26 | + * kernel on x86. This is already the default behavior on | |
| 27 | + * some other architectures, like ARM64 and PowerPC. | |
| 25 | 28 | */ |
| 26 | 29 | #define KEXEC_FILE_UNLOAD 0x00000001 |
| 27 | 30 | #define KEXEC_FILE_ON_CRASH 0x00000002 |
| 28 | 31 | #define KEXEC_FILE_NO_INITRAMFS 0x00000004 |
| 29 | 32 | #define KEXEC_FILE_DEBUG 0x00000008 |
| 30 | 33 | #define KEXEC_FILE_NO_CMA 0x00000010 |
| 34 | +#define KEXEC_FILE_FORCE_DTB 0x00000020 | |
| 31 | 35 | |
| 32 | 36 | /* These values match the ELF architecture values. |
| 33 | 37 | * Unless there is a good reason that should continue to be the case. |
| @@ -545,6 +545,12 @@ static int __init unknown_bootoption(char *param, char *val, · 544 unmodified lines | ||
| 545 | 545 | const char *unused, void *arg) |
| 546 | 546 | { |
| 547 | 547 | size_t len = strlen(param); |
| 548 | + /* | |
| 549 | + * Well-known bootloader identifiers: | |
| 550 | + * 1. LILO/Grub pass "BOOT_IMAGE=..."; | |
| 551 | + * 2. kexec/kdump (kexec-tools) pass "kexec". | |
| 552 | + */ | |
| 553 | + const char *bootloader[] = { "BOOT_IMAGE=", "kexec", NULL }; | |
| 548 | 554 | |
| 549 | 555 | /* Handle params aliased to sysctls */ |
| 550 | 556 | if (sysctl_is_alias(param)) |
| @@ -552,6 +558,12 @@ static int __init unknown_bootoption(char *param, char *val, · 1 unmodified line | ||
| 552 | 558 | |
| 553 | 559 | repair_env_string(param, val); |
| 554 | 560 | |
| 561 | + /* Handle bootloader identifier */ | |
| 562 | + for (int i = 0; bootloader[i]; i++) { | |
| 563 | + if (strstarts(param, bootloader[i])) | |
| 564 | + return 0; | |
| 565 | + } | |
| 566 | + | |
| 555 | 567 | /* Handle obsolete-style parameters */ |
| 556 | 568 | if (obsolete_checksetup(param)) |
| 557 | 569 | return 0; |
| @@ -148,6 +148,17 @@ config CRASH_DM_CRYPT_CONFIGS · 147 unmodified lines | ||
| 148 | 148 | CRASH_DM_CRYPT cannot directly select CONFIGFS_FS, because that |
| 149 | 149 | is required to be built-in. |
| 150 | 150 | |
| 151 | +config CRASH_DUMP_KUNIT_TEST | |
| 152 | + tristate "Unit Tests for kernel crash dumps" if !KUNIT_ALL_TESTS | |
| 153 | + depends on CRASH_DUMP && KUNIT | |
| 154 | + default KUNIT_ALL_TESTS | |
| 155 | + help | |
| 156 | + This option builds KUnit unit tests for kernel crash dumps. The unit | |
| 157 | + tests will be used to verify the correctness of covered functions and | |
| 158 | + also prevent any regression. | |
| 159 | + | |
| 160 | + If unsure, say N. | |
| 161 | + | |
| 151 | 162 | config CRASH_HOTPLUG |
| 152 | 163 | bool "Update the crash elfcorehdr on system configuration changes" |
| 153 | 164 | default y |
| @@ -78,6 +78,7 @@ obj-$(CONFIG_CRASH_RESERVE) += crash_reserve.o · 77 unmodified lines | ||
| 78 | 78 | obj-$(CONFIG_KEXEC_CORE) += kexec_core.o |
| 79 | 79 | obj-$(CONFIG_CRASH_DUMP) += crash_core.o |
| 80 | 80 | obj-$(CONFIG_CRASH_DM_CRYPT) += crash_dump_dm_crypt.o |
| 81 | +obj-$(CONFIG_CRASH_DUMP_KUNIT_TEST) += crash_core_test.o | |
| 81 | 82 | obj-$(CONFIG_KEXEC) += kexec.o |
| 82 | 83 | obj-$(CONFIG_KEXEC_FILE) += kexec_file.o |
| 83 | 84 | obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o |
| @@ -22,6 +22,7 @@ · 21 unmodified lines | ||
| 22 | 22 | #include <linux/btf.h> |
| 23 | 23 | #include <linux/objtool.h> |
| 24 | 24 | #include <linux/delay.h> |
| 25 | +#include <linux/panic.h> | |
| 25 | 26 | |
| 26 | 27 | #include <asm/page.h> |
| 27 | 28 | #include <asm/sections.h> |
| @@ -143,17 +144,7 @@ STACK_FRAME_NON_STANDARD(__crash_kexec); · 115 unmodified lines | ||
| 143 | 144 | |
| 144 | 145 | __bpf_kfunc void crash_kexec(struct pt_regs *regs) |
| 145 | 146 | { |
| 146 | − int old_cpu, this_cpu; | |
| 147 | − | |
| 148 | − /* | |
| 149 | − * Only one CPU is allowed to execute the crash_kexec() code as with | |
| 150 | − * panic(). Otherwise parallel calls of panic() and crash_kexec() | |
| 151 | − * may stop each other. To exclude them, we use panic_cpu here too. | |
| 152 | − */ | |
| 153 | − old_cpu = PANIC_CPU_INVALID; | |
| 154 | − this_cpu = raw_smp_processor_id(); | |
| 155 | − | |
| 156 | − if (atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu)) { | |
| 147 | + if (panic_try_start()) { | |
| 157 | 148 | /* This is the 1st CPU which comes here, so go ahead. */ |
| 158 | 149 | __crash_kexec(regs); |
| 159 | 150 | |
| @@ -161,7 +152,7 @@ __bpf_kfunc void crash_kexec(struct pt_regs *regs) · 1 unmodified line | ||
| 161 | 152 | * Reset panic_cpu to allow another panic()/crash_kexec() |
| 162 | 153 | * call. |
| 163 | 154 | */ |
| 164 | − atomic_set(&panic_cpu, PANIC_CPU_INVALID); | |
| 155 | + panic_reset(); | |
| 165 | 156 | } |
| 166 | 157 | } |
| 167 | 158 | |
| @@ -274,6 +265,20 @@ int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map, · 106 unmodified lines | ||
| 274 | 265 | return 0; |
| 275 | 266 | } |
| 276 | 267 | |
| 268 | +/** | |
| 269 | + * crash_exclude_mem_range - exclude a mem range for existing ranges | |
| 270 | + * @mem: mem->range contains an array of ranges sorted in ascending order | |
| 271 | + * @mstart: the start of to-be-excluded range | |
| 272 | + * @mend: the start of to-be-excluded range | |
| 273 | + * | |
| 274 | + * If you are unsure if a range split will happen, to avoid function call | |
| 275 | + * failure because of -ENOMEM, always make sure | |
| 276 | + * mem->max_nr_ranges == mem->nr_ranges + 1 | |
| 277 | + * before calling the function each time. | |
| 278 | + * | |
| 279 | + * returns 0 if a memory range is excluded successfully | |
| 280 | + * return -ENOMEM if mem->ranges doesn't have space to hold split ranges | |
| 281 | + */ | |
| 277 | 282 | int crash_exclude_mem_range(struct crash_mem *mem, |
| 278 | 283 | unsigned long long mstart, unsigned long long mend) |
| 279 | 284 | { |
| @@ -333,6 +338,7 @@ int crash_exclude_mem_range(struct crash_mem *mem, · 53 unmodified lines | ||
| 333 | 338 | |
| 334 | 339 | return 0; |
| 335 | 340 | } |
| 341 | +EXPORT_SYMBOL_GPL(crash_exclude_mem_range); | |
| 336 | 342 | |
| 337 | 343 | ssize_t crash_get_memory_size(void) |
| 338 | 344 | { |
| @@ -0,0 +1,343 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +#include <kunit/test.h> | |
| 3 | +#include <linux/crash_core.h> // For struct crash_mem and struct range if defined there | |
| 4 | + | |
| 5 | +// Helper to create and initialize crash_mem | |
| 6 | +static struct crash_mem *create_crash_mem(struct kunit *test, unsigned int max_ranges, | |
| 7 | + unsigned int nr_initial_ranges, | |
| 8 | + const struct range *initial_ranges) | |
| 9 | +{ | |
| 10 | + struct crash_mem *mem; | |
| 11 | + size_t alloc_size; | |
| 12 | + | |
| 13 | + // Check if max_ranges can even hold initial_ranges | |
| 14 | + if (max_ranges < nr_initial_ranges) { | |
| 15 | + kunit_err(test, "max_ranges (%u) < nr_initial_ranges (%u)\n", | |
| 16 | + max_ranges, nr_initial_ranges); | |
| 17 | + return NULL; | |
| 18 | + } | |
| 19 | + | |
| 20 | + alloc_size = sizeof(struct crash_mem) + (size_t)max_ranges * sizeof(struct range); | |
| 21 | + mem = kunit_kzalloc(test, alloc_size, GFP_KERNEL); | |
| 22 | + if (!mem) { | |
| 23 | + kunit_err(test, "Failed to allocate crash_mem\n"); | |
| 24 | + return NULL; | |
| 25 | + } | |
| 26 | + | |
| 27 | + mem->max_nr_ranges = max_ranges; | |
| 28 | + mem->nr_ranges = nr_initial_ranges; | |
| 29 | + if (initial_ranges && nr_initial_ranges > 0) { | |
| 30 | + memcpy(mem->ranges, initial_ranges, | |
| 31 | + nr_initial_ranges * sizeof(struct range)); | |
| 32 | + } | |
| 33 | + | |
| 34 | + return mem; | |
| 35 | +} | |
| 36 | + | |
| 37 | +// Helper to compare ranges for assertions | |
| 38 | +static void assert_ranges_equal(struct kunit *test, | |
| 39 | + const struct range *actual_ranges, | |
| 40 | + unsigned int actual_nr_ranges, | |
| 41 | + const struct range *expected_ranges, | |
| 42 | + unsigned int expected_nr_ranges, | |
| 43 | + const char *case_name) | |
| 44 | +{ | |
| 45 | + unsigned int i; | |
| 46 | + | |
| 47 | + KUNIT_ASSERT_EQ_MSG(test, expected_nr_ranges, actual_nr_ranges, | |
| 48 | + "%s: Number of ranges mismatch.", case_name); | |
| 49 | + | |
| 50 | + for (i = 0; i < expected_nr_ranges; i++) { | |
| 51 | + KUNIT_ASSERT_EQ_MSG(test, expected_ranges[i].start, actual_ranges[i].start, | |
| 52 | + "%s: Range %u start mismatch.", case_name, i); | |
| 53 | + KUNIT_ASSERT_EQ_MSG(test, expected_ranges[i].end, actual_ranges[i].end, | |
| 54 | + "%s: Range %u end mismatch.", case_name, i); | |
| 55 | + } | |
| 56 | +} | |
| 57 | + | |
| 58 | +// Structure for test parameters | |
| 59 | +struct exclude_test_param { | |
| 60 | + const char *description; | |
| 61 | + unsigned long long exclude_start; | |
| 62 | + unsigned long long exclude_end; | |
| 63 | + unsigned int initial_max_ranges; | |
| 64 | + const struct range *initial_ranges; | |
| 65 | + unsigned int initial_nr_ranges; | |
| 66 | + const struct range *expected_ranges; | |
| 67 | + unsigned int expected_nr_ranges; | |
| 68 | + int expected_ret; | |
| 69 | +}; | |
| 70 | + | |
| 71 | +static void run_exclude_test_case(struct kunit *test, const struct exclude_test_param *params) | |
| 72 | +{ | |
| 73 | + struct crash_mem *mem; | |
| 74 | + int ret; | |
| 75 | + | |
| 76 | + kunit_info(test, "%s", params->description); | |
| 77 | + | |
| 78 | + mem = create_crash_mem(test, params->initial_max_ranges, | |
| 79 | + params->initial_nr_ranges, params->initial_ranges); | |
| 80 | + if (!mem) | |
| 81 | + return; // Error already logged by create_crash_mem or kunit_kzalloc | |
| 82 | + | |
| 83 | + ret = crash_exclude_mem_range(mem, params->exclude_start, params->exclude_end); | |
| 84 | + | |
| 85 | + KUNIT_ASSERT_EQ_MSG(test, params->expected_ret, ret, | |
| 86 | + "%s: Return value mismatch.", params->description); | |
| 87 | + | |
| 88 | + if (params->expected_ret == 0) { | |
| 89 | + assert_ranges_equal(test, mem->ranges, mem->nr_ranges, | |
| 90 | + params->expected_ranges, params->expected_nr_ranges, | |
| 91 | + params->description); | |
| 92 | + } else { | |
| 93 | + // If an error is expected, nr_ranges might still be relevant to check | |
| 94 | + // depending on the exact point of failure. For ENOMEM on split, | |
| 95 | + // nr_ranges shouldn't have changed. | |
| 96 | + KUNIT_ASSERT_EQ_MSG(test, params->initial_nr_ranges, | |
| 97 | + mem->nr_ranges, | |
| 98 | + "%s: Number of ranges mismatch on error.", | |
| 99 | + params->description); | |
| 100 | + } | |
| 101 | +} | |
| 102 | + | |
| 103 | +/* | |
| 104 | + * Test Strategy 1: One to-be-excluded range A and one existing range B. | |
| 105 | + * | |
| 106 | + * Exhaust all possibilities of the position of A regarding B. | |
| 107 | + */ | |
| 108 | + | |
| 109 | +static const struct range single_range_b = { .start = 100, .end = 199 }; | |
| 110 | + | |
| 111 | +static const struct exclude_test_param exclude_single_range_test_data[] = { | |
| 112 | + { | |
| 113 | + .description = "1.1: A is left of B, no overlap", | |
| 114 | + .exclude_start = 10, .exclude_end = 50, | |
| 115 | + .initial_max_ranges = 1, | |
| 116 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 117 | + .expected_ranges = &single_range_b, .expected_nr_ranges = 1, | |
| 118 | + .expected_ret = 0, | |
| 119 | + }, | |
| 120 | + { | |
| 121 | + .description = "1.2: A's right boundary touches B's left boundary", | |
| 122 | + .exclude_start = 10, .exclude_end = 99, | |
| 123 | + .initial_max_ranges = 1, | |
| 124 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 125 | + .expected_ranges = &single_range_b, .expected_nr_ranges = 1, | |
| 126 | + .expected_ret = 0, | |
| 127 | + }, | |
| 128 | + { | |
| 129 | + .description = "1.3: A overlaps B's left part", | |
| 130 | + .exclude_start = 50, .exclude_end = 149, | |
| 131 | + .initial_max_ranges = 1, | |
| 132 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 133 | + .expected_ranges = (const struct range[]){{ .start = 150, .end = 199 }}, | |
| 134 | + .expected_nr_ranges = 1, | |
| 135 | + .expected_ret = 0, | |
| 136 | + }, | |
| 137 | + { | |
| 138 | + .description = "1.4: A is completely inside B", | |
| 139 | + .exclude_start = 120, .exclude_end = 179, | |
| 140 | + .initial_max_ranges = 2, // Needs space for split | |
| 141 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 142 | + .expected_ranges = (const struct range[]){ | |
| 143 | + { .start = 100, .end = 119 }, | |
| 144 | + { .start = 180, .end = 199 } | |
| 145 | + }, | |
| 146 | + .expected_nr_ranges = 2, | |
| 147 | + .expected_ret = 0, | |
| 148 | + }, | |
| 149 | + { | |
| 150 | + .description = "1.5: A overlaps B's right part", | |
| 151 | + .exclude_start = 150, .exclude_end = 249, | |
| 152 | + .initial_max_ranges = 1, | |
| 153 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 154 | + .expected_ranges = (const struct range[]){{ .start = 100, .end = 149 }}, | |
| 155 | + .expected_nr_ranges = 1, | |
| 156 | + .expected_ret = 0, | |
| 157 | + }, | |
| 158 | + { | |
| 159 | + .description = "1.6: A's left boundary touches B's right boundary", | |
| 160 | + .exclude_start = 200, .exclude_end = 250, | |
| 161 | + .initial_max_ranges = 1, | |
| 162 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 163 | + .expected_ranges = &single_range_b, .expected_nr_ranges = 1, | |
| 164 | + .expected_ret = 0, | |
| 165 | + }, | |
| 166 | + { | |
| 167 | + .description = "1.7: A is right of B, no overlap", | |
| 168 | + .exclude_start = 250, .exclude_end = 300, | |
| 169 | + .initial_max_ranges = 1, | |
| 170 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 171 | + .expected_ranges = &single_range_b, .expected_nr_ranges = 1, | |
| 172 | + .expected_ret = 0, | |
| 173 | + }, | |
| 174 | + { | |
| 175 | + .description = "1.8: A completely covers B and extends beyond", | |
| 176 | + .exclude_start = 50, .exclude_end = 250, | |
| 177 | + .initial_max_ranges = 1, | |
| 178 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 179 | + .expected_ranges = NULL, .expected_nr_ranges = 0, | |
| 180 | + .expected_ret = 0, | |
| 181 | + }, | |
| 182 | + { | |
| 183 | + .description = "1.9: A covers B and extends to the left", | |
| 184 | + .exclude_start = 50, .exclude_end = 199, // A ends exactly where B ends | |
| 185 | + .initial_max_ranges = 1, | |
| 186 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 187 | + .expected_ranges = NULL, .expected_nr_ranges = 0, | |
| 188 | + .expected_ret = 0, | |
| 189 | + }, | |
| 190 | + { | |
| 191 | + .description = "1.10: A covers B and extends to the right", | |
| 192 | + .exclude_start = 100, .exclude_end = 250, // A starts exactly where B starts | |
| 193 | + .initial_max_ranges = 1, | |
| 194 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 195 | + .expected_ranges = NULL, .expected_nr_ranges = 0, | |
| 196 | + .expected_ret = 0, | |
| 197 | + }, | |
| 198 | + { | |
| 199 | + .description = "1.11: A is identical to B", | |
| 200 | + .exclude_start = 100, .exclude_end = 199, | |
| 201 | + .initial_max_ranges = 1, | |
| 202 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 203 | + .expected_ranges = NULL, .expected_nr_ranges = 0, | |
| 204 | + .expected_ret = 0, | |
| 205 | + }, | |
| 206 | + { | |
| 207 | + .description = "1.12: A is a point, left of B, no overlap", | |
| 208 | + .exclude_start = 10, .exclude_end = 10, | |
| 209 | + .initial_max_ranges = 1, | |
| 210 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 211 | + .expected_ranges = &single_range_b, .expected_nr_ranges = 1, | |
| 212 | + .expected_ret = 0, | |
| 213 | + }, | |
| 214 | + { | |
| 215 | + .description = "1.13: A is a point, at start of B", | |
| 216 | + .exclude_start = 100, .exclude_end = 100, | |
| 217 | + .initial_max_ranges = 1, | |
| 218 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 219 | + .expected_ranges = (const struct range[]){{ .start = 101, .end = 199 }}, | |
| 220 | + .expected_nr_ranges = 1, | |
| 221 | + .expected_ret = 0, | |
| 222 | + }, | |
| 223 | + { | |
| 224 | + .description = "1.14: A is a point, in middle of B (causes split)", | |
| 225 | + .exclude_start = 150, .exclude_end = 150, | |
| 226 | + .initial_max_ranges = 2, // Needs space for split | |
| 227 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 228 | + .expected_ranges = (const struct range[]){ | |
| 229 | + { .start = 100, .end = 149 }, | |
| 230 | + { .start = 151, .end = 199 } | |
| 231 | + }, | |
| 232 | + .expected_nr_ranges = 2, | |
| 233 | + .expected_ret = 0, | |
| 234 | + }, | |
| 235 | + { | |
| 236 | + .description = "1.15: A is a point, at end of B", | |
| 237 | + .exclude_start = 199, .exclude_end = 199, | |
| 238 | + .initial_max_ranges = 1, | |
| 239 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 240 | + .expected_ranges = (const struct range[]){{ .start = 100, .end = 198 }}, | |
| 241 | + .expected_nr_ranges = 1, | |
| 242 | + .expected_ret = 0, | |
| 243 | + }, | |
| 244 | + { | |
| 245 | + .description = "1.16: A is a point, right of B, no overlap", | |
| 246 | + .exclude_start = 250, .exclude_end = 250, | |
| 247 | + .initial_max_ranges = 1, | |
| 248 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 249 | + .expected_ranges = &single_range_b, .expected_nr_ranges = 1, | |
| 250 | + .expected_ret = 0, | |
| 251 | + }, | |
| 252 | + // ENOMEM case for single range split | |
| 253 | + { | |
| 254 | + .description = "1.17: A completely inside B (split), no space (ENOMEM)", | |
| 255 | + .exclude_start = 120, .exclude_end = 179, | |
| 256 | + .initial_max_ranges = 1, // Not enough for split | |
| 257 | + .initial_ranges = &single_range_b, .initial_nr_ranges = 1, | |
| 258 | + .expected_ranges = NULL, // Not checked on error by assert_ranges_equal for content | |
| 259 | + .expected_nr_ranges = 1, // Should remain unchanged | |
| 260 | + .expected_ret = -ENOMEM, | |
| 261 | + }, | |
| 262 | +}; | |
| 263 | + | |
| 264 | + | |
| 265 | +static void exclude_single_range_test(struct kunit *test) | |
| 266 | +{ | |
| 267 | + size_t i; | |
| 268 | + | |
| 269 | + for (i = 0; i < ARRAY_SIZE(exclude_single_range_test_data); i++) { | |
| 270 | + kunit_log(KERN_INFO, test, "Running: %s", exclude_single_range_test_data[i].description); | |
| 271 | + run_exclude_test_case(test, &exclude_single_range_test_data[i]); | |
| 272 | + // KUnit will stop on first KUNIT_ASSERT failure within run_exclude_test_case | |
| 273 | + } | |
| 274 | +} | |
| 275 | + | |
| 276 | +/* | |
| 277 | + * Test Strategy 2: Regression test. | |
| 278 | + */ | |
| 279 | + | |
| 280 | +static const struct exclude_test_param exclude_range_regression_test_data[] = { | |
| 281 | + // Test data from commit a2e9a95d2190 | |
| 282 | + { | |
| 283 | + .description = "2.1: exclude low 1M", | |
| 284 | + .exclude_start = 0, .exclude_end = (1 << 20) - 1, | |
| 285 | + .initial_max_ranges = 3, | |
| 286 | + .initial_ranges = (const struct range[]){ | |
| 287 | + { .start = 0, .end = 0x3efff }, | |
| 288 | + { .start = 0x3f000, .end = 0x3ffff }, | |
| 289 | + { .start = 0x40000, .end = 0x9ffff } | |
| 290 | + }, | |
| 291 | + .initial_nr_ranges = 3, | |
| 292 | + .expected_nr_ranges = 0, | |
| 293 | + .expected_ret = 0, | |
| 294 | + }, | |
| 295 | + // Test data from https://lore.kernel.org/all/ZXrY7QbXAlxydsSC@MiWiFi-R3L-srv/T/#u | |
| 296 | + { | |
| 297 | + .description = "2.2: when range out of bound", | |
| 298 | + .exclude_start = 100, .exclude_end = 200, | |
| 299 | + .initial_max_ranges = 3, | |
| 300 | + .initial_ranges = (const struct range[]){ | |
| 301 | + { .start = 1, .end = 299 }, | |
| 302 | + { .start = 401, .end = 1000 }, | |
| 303 | + { .start = 1001, .end = 2000 } | |
| 304 | + }, | |
| 305 | + .initial_nr_ranges = 3, | |
| 306 | + .expected_ranges = NULL, // Not checked on error by assert_ranges_equal for content | |
| 307 | + .expected_nr_ranges = 3, // Should remain unchanged | |
| 308 | + .expected_ret = -ENOMEM | |
| 309 | + }, | |
| 310 | + | |
| 311 | +}; | |
| 312 | + | |
| 313 | + | |
| 314 | +static void exclude_range_regression_test(struct kunit *test) | |
| 315 | +{ | |
| 316 | + size_t i; | |
| 317 | + | |
| 318 | + for (i = 0; i < ARRAY_SIZE(exclude_range_regression_test_data); i++) { | |
| 319 | + kunit_log(KERN_INFO, test, "Running: %s", exclude_range_regression_test_data[i].description); | |
| 320 | + run_exclude_test_case(test, &exclude_range_regression_test_data[i]); | |
| 321 | + // KUnit will stop on first KUNIT_ASSERT failure within run_exclude_test_case | |
| 322 | + } | |
| 323 | +} | |
| 324 | + | |
| 325 | +/* | |
| 326 | + * KUnit Test Suite | |
| 327 | + */ | |
| 328 | +static struct kunit_case crash_exclude_mem_range_test_cases[] = { | |
| 329 | + KUNIT_CASE(exclude_single_range_test), | |
| 330 | + KUNIT_CASE(exclude_range_regression_test), | |
| 331 | + {} | |
| 332 | +}; | |
| 333 | + | |
| 334 | +static struct kunit_suite crash_exclude_mem_range_suite = { | |
| 335 | + .name = "crash_exclude_mem_range_tests", | |
| 336 | + .test_cases = crash_exclude_mem_range_test_cases, | |
| 337 | + // .init and .exit can be NULL if not needed globally for the suite | |
| 338 | +}; | |
| 339 | + | |
| 340 | +kunit_test_suite(crash_exclude_mem_range_suite); | |
| 341 | + | |
| 342 | +MODULE_DESCRIPTION("crash dump KUnit test suite"); | |
| 343 | +MODULE_LICENSE("GPL"); | |
| @@ -2132,9 +2132,7 @@ __latent_entropy struct task_struct *copy_process( · 2131 unmodified lines | ||
| 2132 | 2132 | |
| 2133 | 2133 | p->pagefault_disabled = 0; |
| 2134 | 2134 | |
| 2135 | −#ifdef CONFIG_LOCKDEP | |
| 2136 | 2135 | lockdep_init_task(p); |
| 2137 | −#endif | |
| 2138 | 2136 | |
| 2139 | 2137 | p->blocked_on = NULL; /* not blocked yet */ |
| 2140 | 2138 | |
| @@ -2547,11 +2545,9 @@ struct task_struct * __init fork_idle(int cpu) · 406 unmodified lines | ||
| 2547 | 2545 | struct task_struct *create_io_thread(int (*fn)(void *), void *arg, int node) |
| 2548 | 2546 | { |
| 2549 | 2547 | unsigned long flags = CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD| |
| 2550 | − CLONE_IO; | |
| 2548 | + CLONE_IO|CLONE_VM|CLONE_UNTRACED; | |
| 2551 | 2549 | struct kernel_clone_args args = { |
| 2552 | − .flags = ((lower_32_bits(flags) | CLONE_VM | | |
| 2553 | − CLONE_UNTRACED) & ~CSIGNAL), | |
| 2554 | − .exit_signal = (lower_32_bits(flags) & CSIGNAL), | |
| 2550 | + .flags = flags, | |
| 2555 | 2551 | .fn = fn, |
| 2556 | 2552 | .fn_arg = arg, |
| 2557 | 2553 | .io_thread = 1, |
| @@ -2663,9 +2659,8 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, const char *name, · 105 unmodified lines | ||
| 2663 | 2659 | unsigned long flags) |
| 2664 | 2660 | { |
| 2665 | 2661 | struct kernel_clone_args args = { |
| 2666 | − .flags = ((lower_32_bits(flags) | CLONE_VM | | |
| 2667 | − CLONE_UNTRACED) & ~CSIGNAL), | |
| 2668 | − .exit_signal = (lower_32_bits(flags) & CSIGNAL), | |
| 2662 | + .flags = ((flags | CLONE_VM | CLONE_UNTRACED) & ~CSIGNAL), | |
| 2663 | + .exit_signal = (flags & CSIGNAL), | |
| 2669 | 2664 | .fn = fn, |
| 2670 | 2665 | .fn_arg = arg, |
| 2671 | 2666 | .name = name, |
| @@ -2681,9 +2676,8 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, const char *name, · 9 unmodified lines | ||
| 2681 | 2676 | pid_t user_mode_thread(int (*fn)(void *), void *arg, unsigned long flags) |
| 2682 | 2677 | { |
| 2683 | 2678 | struct kernel_clone_args args = { |
| 2684 | − .flags = ((lower_32_bits(flags) | CLONE_VM | | |
| 2685 | − CLONE_UNTRACED) & ~CSIGNAL), | |
| 2686 | − .exit_signal = (lower_32_bits(flags) & CSIGNAL), | |
| 2679 | + .flags = ((flags | CLONE_VM | CLONE_UNTRACED) & ~CSIGNAL), | |
| 2680 | + .exit_signal = (flags & CSIGNAL), | |
| 2687 | 2681 | .fn = fn, |
| 2688 | 2682 | .fn_arg = arg, |
| 2689 | 2683 | }; |
| @@ -95,9 +95,41 @@ static struct notifier_block panic_block = { · 94 unmodified lines | ||
| 95 | 95 | .notifier_call = hung_task_panic, |
| 96 | 96 | }; |
| 97 | 97 | |
| 98 | +static bool task_is_hung(struct task_struct *t, unsigned long timeout) | |
| 99 | +{ | |
| 100 | + unsigned long switch_count = t->nvcsw + t->nivcsw; | |
| 101 | + unsigned int state = READ_ONCE(t->__state); | |
| 102 | + | |
| 103 | + /* | |
| 104 | + * skip the TASK_KILLABLE tasks -- these can be killed | |
| 105 | + * skip the TASK_IDLE tasks -- those are genuinely idle | |
| 106 | + * skip the TASK_FROZEN task -- it reasonably stops scheduling by freezer | |
| 107 | + */ | |
| 108 | + if (!(state & TASK_UNINTERRUPTIBLE) || | |
| 109 | + (state & (TASK_WAKEKILL | TASK_NOLOAD | TASK_FROZEN))) | |
| 110 | + return false; | |
| 111 | + | |
| 112 | + /* | |
| 113 | + * When a freshly created task is scheduled once, changes its state to | |
| 114 | + * TASK_UNINTERRUPTIBLE without having ever been switched out once, it | |
| 115 | + * musn't be checked. | |
| 116 | + */ | |
| 117 | + if (unlikely(!switch_count)) | |
| 118 | + return false; | |
| 119 | + | |
| 120 | + if (switch_count != t->last_switch_count) { | |
| 121 | + t->last_switch_count = switch_count; | |
| 122 | + t->last_switch_time = jiffies; | |
| 123 | + return false; | |
| 124 | + } | |
| 125 | + if (time_is_after_jiffies(t->last_switch_time + timeout * HZ)) | |
| 126 | + return false; | |
| 127 | + | |
| 128 | + return true; | |
| 129 | +} | |
| 98 | 130 | |
| 99 | 131 | #ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER |
| 100 | −static void debug_show_blocker(struct task_struct *task) | |
| 132 | +static void debug_show_blocker(struct task_struct *task, unsigned long timeout) | |
| 101 | 133 | { |
| 102 | 134 | struct task_struct *g, *t; |
| 103 | 135 | unsigned long owner, blocker, blocker_type; |
| @@ -174,41 +206,21 @@ static void debug_show_blocker(struct task_struct *task) · 70 unmodified lines | ||
| 174 | 206 | t->pid, rwsem_blocked_by); |
| 175 | 207 | break; |
| 176 | 208 | } |
| 177 | − sched_show_task(t); | |
| 209 | + /* Avoid duplicated task dump, skip if the task is also hung. */ | |
| 210 | + if (!task_is_hung(t, timeout)) | |
| 211 | + sched_show_task(t); | |
| 178 | 212 | return; |
| 179 | 213 | } |
| 180 | 214 | } |
| 181 | 215 | #else |
| 182 | −static inline void debug_show_blocker(struct task_struct *task) | |
| 216 | +static inline void debug_show_blocker(struct task_struct *task, unsigned long timeout) | |
| 183 | 217 | { |
| 184 | 218 | } |
| 185 | 219 | #endif |
| 186 | 220 | |
| 187 | 221 | static void check_hung_task(struct task_struct *t, unsigned long timeout) |
| 188 | 222 | { |
| 189 | − unsigned long switch_count = t->nvcsw + t->nivcsw; | |
| 190 | − | |
| 191 | − /* | |
| 192 | − * Ensure the task is not frozen. | |
| 193 | − * Also, skip vfork and any other user process that freezer should skip. | |
| 194 | − */ | |
| 195 | − if (unlikely(READ_ONCE(t->__state) & TASK_FROZEN)) | |
| 196 | − return; | |
| 197 | − | |
| 198 | − /* | |
| 199 | − * When a freshly created task is scheduled once, changes its state to | |
| 200 | − * TASK_UNINTERRUPTIBLE without having ever been switched out once, it | |
| 201 | − * musn't be checked. | |
| 202 | − */ | |
| 203 | − if (unlikely(!switch_count)) | |
| 204 | − return; | |
| 205 | − | |
| 206 | − if (switch_count != t->last_switch_count) { | |
| 207 | − t->last_switch_count = switch_count; | |
| 208 | − t->last_switch_time = jiffies; | |
| 209 | − return; | |
| 210 | − } | |
| 211 | − if (time_is_after_jiffies(t->last_switch_time + timeout * HZ)) | |
| 223 | + if (!task_is_hung(t, timeout)) | |
| 212 | 224 | return; |
| 213 | 225 | |
| 214 | 226 | /* |
| @@ -243,7 +255,7 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout) · 28 unmodified lines | ||
| 243 | 255 | pr_err("\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\"" |
| 244 | 256 | " disables this message.\n"); |
| 245 | 257 | sched_show_task(t); |
| 246 | − debug_show_blocker(t); | |
| 258 | + debug_show_blocker(t, timeout); | |
| 247 | 259 | hung_task_show_lock = true; |
| 248 | 260 | |
| 249 | 261 | if (sysctl_hung_task_all_cpu_backtrace) |
| @@ -299,7 +311,6 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) · 49 unmodified lines | ||
| 299 | 311 | hung_task_show_lock = false; |
| 300 | 312 | rcu_read_lock(); |
| 301 | 313 | for_each_process_thread(g, t) { |
| 302 | − unsigned int state; | |
| 303 | 314 | |
| 304 | 315 | if (!max_count--) |
| 305 | 316 | goto unlock; |
| @@ -308,15 +319,8 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) · 2 unmodified lines | ||
| 308 | 319 | goto unlock; |
| 309 | 320 | last_break = jiffies; |
| 310 | 321 | } |
| 311 | − /* | |
| 312 | − * skip the TASK_KILLABLE tasks -- these can be killed | |
| 313 | − * skip the TASK_IDLE tasks -- those are genuinely idle | |
| 314 | − */ | |
| 315 | − state = READ_ONCE(t->__state); | |
| 316 | − if ((state & TASK_UNINTERRUPTIBLE) && | |
| 317 | − !(state & TASK_WAKEKILL) && | |
| 318 | − !(state & TASK_NOLOAD)) | |
| 319 | − check_hung_task(t, timeout); | |
| 322 | + | |
| 323 | + check_hung_task(t, timeout); | |
| 320 | 324 | } |
| 321 | 325 | unlock: |
| 322 | 326 | rcu_read_unlock(); |
| @@ -264,7 +264,7 @@ static int test_kallsyms_basic_function(void) · 263 unmodified lines | ||
| 264 | 264 | char namebuf[KSYM_NAME_LEN]; |
| 265 | 265 | struct test_stat *stat, *stat2; |
| 266 | 266 | |
| 267 | − stat = kmalloc(sizeof(*stat) * 2, GFP_KERNEL); | |
| 267 | + stat = kmalloc_array(2, sizeof(*stat), GFP_KERNEL); | |
| 268 | 268 | if (!stat) |
| 269 | 269 | return -ENOMEM; |
| 270 | 270 | stat2 = stat + 1; |
| @@ -978,6 +978,15 @@ static void kcov_move_area(enum kcov_mode mode, void *dst_area, · 977 unmodified lines | ||
| 978 | 978 | memcpy(dst_entries, src_entries, bytes_to_move); |
| 979 | 979 | entries_moved = bytes_to_move >> entry_size_log; |
| 980 | 980 | |
| 981 | + /* | |
| 982 | + * A write memory barrier is required here, to ensure | |
| 983 | + * that the writes from the memcpy() are visible before | |
| 984 | + * the count is updated. Without this, it is possible for | |
| 985 | + * a user to observe a new count value but stale | |
| 986 | + * coverage data. | |
| 987 | + */ | |
| 988 | + smp_wmb(); | |
| 989 | + | |
| 981 | 990 | switch (mode) { |
| 982 | 991 | case KCOV_MODE_TRACE_PC: |
| 983 | 992 | WRITE_ONCE(*(unsigned long *)dst_area, dst_len + entries_moved); |
| @@ -233,7 +233,6 @@ struct kimage *do_kimage_alloc_init(void) · 232 unmodified lines | ||
| 233 | 233 | if (!image) |
| 234 | 234 | return NULL; |
| 235 | 235 | |
| 236 | − image->head = 0; | |
| 237 | 236 | image->entry = &image->head; |
| 238 | 237 | image->last_entry = &image->head; |
| 239 | 238 | image->control_page = ~0; /* By default this does not apply */ |
| @@ -255,6 +255,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd, · 254 unmodified lines | ||
| 255 | 255 | } |
| 256 | 256 | |
| 257 | 257 | image->no_cma = !!(flags & KEXEC_FILE_NO_CMA); |
| 258 | + image->force_dtb = flags & KEXEC_FILE_FORCE_DTB; | |
| 258 | 259 | |
| 259 | 260 | if (cmdline_len) { |
| 260 | 261 | image->cmdline_buf = memdup_user(cmdline_ptr, cmdline_len); |
| @@ -987,6 +987,26 @@ static const void *kho_get_fdt(void) · 986 unmodified lines | ||
| 987 | 987 | return kho_in.fdt_phys ? phys_to_virt(kho_in.fdt_phys) : NULL; |
| 988 | 988 | } |
| 989 | 989 | |
| 990 | +/** | |
| 991 | + * is_kho_boot - check if current kernel was booted via KHO-enabled | |
| 992 | + * kexec | |
| 993 | + * | |
| 994 | + * This function checks if the current kernel was loaded through a kexec | |
| 995 | + * operation with KHO enabled, by verifying that a valid KHO FDT | |
| 996 | + * was passed. | |
| 997 | + * | |
| 998 | + * Note: This function returns reliable results only after | |
| 999 | + * kho_populate() has been called during early boot. Before that, | |
| 1000 | + * it may return false even if KHO data is present. | |
| 1001 | + * | |
| 1002 | + * Return: true if booted via KHO-enabled kexec, false otherwise | |
| 1003 | + */ | |
| 1004 | +bool is_kho_boot(void) | |
| 1005 | +{ | |
| 1006 | + return !!kho_get_fdt(); | |
| 1007 | +} | |
| 1008 | +EXPORT_SYMBOL_GPL(is_kho_boot); | |
| 1009 | + | |
| 990 | 1010 | /** |
| 991 | 1011 | * kho_retrieve_subtree - retrieve a preserved sub FDT by its name. |
| 992 | 1012 | * @name: the name of the sub FDT passed to kho_add_subtree(). |
| @@ -1269,7 +1289,7 @@ int kho_fill_kimage(struct kimage *image) · 276 unmodified lines | ||
| 1269 | 1289 | int err = 0; |
| 1270 | 1290 | struct kexec_buf scratch; |
| 1271 | 1291 | |
| 1272 | − if (!kho_enable) | |
| 1292 | + if (!kho_out.finalized) | |
| 1273 | 1293 | return 0; |
| 1274 | 1294 | |
| 1275 | 1295 | image->kho.fdt = page_to_phys(kho_out.ser.fdt); |
| @@ -53,7 +53,7 @@ static unsigned int __read_mostly sysctl_oops_all_cpu_backtrace; · 52 unmodified lines | ||
| 53 | 53 | #define sysctl_oops_all_cpu_backtrace 0 |
| 54 | 54 | #endif /* CONFIG_SMP */ |
| 55 | 55 | |
| 56 | −int panic_on_oops = CONFIG_PANIC_ON_OOPS_VALUE; | |
| 56 | +int panic_on_oops = IS_ENABLED(CONFIG_PANIC_ON_OOPS); | |
| 57 | 57 | static unsigned long tainted_mask = |
| 58 | 58 | IS_ENABLED(CONFIG_RANDSTRUCT) ? (1 << TAINT_RANDSTRUCT) : 0; |
| 59 | 59 | static int pause_on_oops; |
| @@ -67,6 +67,7 @@ static unsigned int warn_limit __read_mostly; · 7 unmodified lines | ||
| 67 | 67 | static bool panic_console_replay; |
| 68 | 68 | |
| 69 | 69 | bool panic_triggering_all_cpu_backtrace; |
| 70 | +static bool panic_this_cpu_backtrace_printed; | |
| 70 | 71 | |
| 71 | 72 | int panic_timeout = CONFIG_PANIC_TIMEOUT; |
| 72 | 73 | EXPORT_SYMBOL_GPL(panic_timeout); |
| @@ -77,6 +78,11 @@ ATOMIC_NOTIFIER_HEAD(panic_notifier_list); · 4 unmodified lines | ||
| 77 | 78 | |
| 78 | 79 | EXPORT_SYMBOL(panic_notifier_list); |
| 79 | 80 | |
| 81 | +static void panic_print_deprecated(void) | |
| 82 | +{ | |
| 83 | + pr_info_once("Kernel: The 'panic_print' parameter is now deprecated. Please use 'panic_sys_info' and 'panic_console_replay' instead.\n"); | |
| 84 | +} | |
| 85 | + | |
| 80 | 86 | #ifdef CONFIG_SYSCTL |
| 81 | 87 | |
| 82 | 88 | /* |
| @@ -125,7 +131,7 @@ static int proc_taint(const struct ctl_table *table, int write, · 42 unmodified lines | ||
| 125 | 131 | static int sysctl_panic_print_handler(const struct ctl_table *table, int write, |
| 126 | 132 | void *buffer, size_t *lenp, loff_t *ppos) |
| 127 | 133 | { |
| 128 | − pr_info_once("Kernel: 'panic_print' sysctl interface will be obsoleted by both 'panic_sys_info' and 'panic_console_replay'\n"); | |
| 134 | + panic_print_deprecated(); | |
| 129 | 135 | return proc_doulongvec_minmax(table, write, buffer, lenp, ppos); |
| 130 | 136 | } |
| 131 | 137 | |
| @@ -294,6 +300,59 @@ void __weak crash_smp_send_stop(void) · 162 unmodified lines | ||
| 294 | 300 | |
| 295 | 301 | atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); |
| 296 | 302 | |
| 303 | +bool panic_try_start(void) | |
| 304 | +{ | |
| 305 | + int old_cpu, this_cpu; | |
| 306 | + | |
| 307 | + /* | |
| 308 | + * Only one CPU is allowed to execute the crash_kexec() code as with | |
| 309 | + * panic(). Otherwise parallel calls of panic() and crash_kexec() | |
| 310 | + * may stop each other. To exclude them, we use panic_cpu here too. | |
| 311 | + */ | |
| 312 | + old_cpu = PANIC_CPU_INVALID; | |
| 313 | + this_cpu = raw_smp_processor_id(); | |
| 314 | + | |
| 315 | + return atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu); | |
| 316 | +} | |
| 317 | +EXPORT_SYMBOL(panic_try_start); | |
| 318 | + | |
| 319 | +void panic_reset(void) | |
| 320 | +{ | |
| 321 | + atomic_set(&panic_cpu, PANIC_CPU_INVALID); | |
| 322 | +} | |
| 323 | +EXPORT_SYMBOL(panic_reset); | |
| 324 | + | |
| 325 | +bool panic_in_progress(void) | |
| 326 | +{ | |
| 327 | + return unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID); | |
| 328 | +} | |
| 329 | +EXPORT_SYMBOL(panic_in_progress); | |
| 330 | + | |
| 331 | +/* Return true if a panic is in progress on the current CPU. */ | |
| 332 | +bool panic_on_this_cpu(void) | |
| 333 | +{ | |
| 334 | + /* | |
| 335 | + * We can use raw_smp_processor_id() here because it is impossible for | |
| 336 | + * the task to be migrated to the panic_cpu, or away from it. If | |
| 337 | + * panic_cpu has already been set, and we're not currently executing on | |
| 338 | + * that CPU, then we never will be. | |
| 339 | + */ | |
| 340 | + return unlikely(atomic_read(&panic_cpu) == raw_smp_processor_id()); | |
| 341 | +} | |
| 342 | +EXPORT_SYMBOL(panic_on_this_cpu); | |
| 343 | + | |
| 344 | +/* | |
| 345 | + * Return true if a panic is in progress on a remote CPU. | |
| 346 | + * | |
| 347 | + * On true, the local CPU should immediately release any printing resources | |
| 348 | + * that may be needed by the panic CPU. | |
| 349 | + */ | |
| 350 | +bool panic_on_other_cpu(void) | |
| 351 | +{ | |
| 352 | + return (panic_in_progress() && !panic_on_this_cpu()); | |
| 353 | +} | |
| 354 | +EXPORT_SYMBOL(panic_on_other_cpu); | |
| 355 | + | |
| 297 | 356 | /* |
| 298 | 357 | * A variant of panic() called from NMI context. We return if we've already |
| 299 | 358 | * panicked on this CPU. If another CPU already panicked, loop in |
| @@ -302,15 +361,9 @@ atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); · 2 unmodified lines | ||
| 302 | 361 | */ |
| 303 | 362 | void nmi_panic(struct pt_regs *regs, const char *msg) |
| 304 | 363 | { |
| 305 | − int old_cpu, this_cpu; | |
| 306 | − | |
| 307 | − old_cpu = PANIC_CPU_INVALID; | |
| 308 | − this_cpu = raw_smp_processor_id(); | |
| 309 | − | |
| 310 | − /* atomic_try_cmpxchg updates old_cpu on failure */ | |
| 311 | − if (atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu)) | |
| 364 | + if (panic_try_start()) | |
| 312 | 365 | panic("%s", msg); |
| 313 | − else if (old_cpu != this_cpu) | |
| 366 | + else if (panic_on_other_cpu()) | |
| 314 | 367 | nmi_panic_self_stop(regs); |
| 315 | 368 | } |
| 316 | 369 | EXPORT_SYMBOL(nmi_panic); |
| @@ -328,6 +381,19 @@ void check_panic_on_warn(const char *origin) · 11 unmodified lines | ||
| 328 | 381 | origin, limit); |
| 329 | 382 | } |
| 330 | 383 | |
| 384 | +static void panic_trigger_all_cpu_backtrace(void) | |
| 385 | +{ | |
| 386 | + /* Temporary allow non-panic CPUs to write their backtraces. */ | |
| 387 | + panic_triggering_all_cpu_backtrace = true; | |
| 388 | + | |
| 389 | + if (panic_this_cpu_backtrace_printed) | |
| 390 | + trigger_allbutcpu_cpu_backtrace(raw_smp_processor_id()); | |
| 391 | + else | |
| 392 | + trigger_all_cpu_backtrace(); | |
| 393 | + | |
| 394 | + panic_triggering_all_cpu_backtrace = false; | |
| 395 | +} | |
| 396 | + | |
| 331 | 397 | /* |
| 332 | 398 | * Helper that triggers the NMI backtrace (if set in panic_print) |
| 333 | 399 | * and then performs the secondary CPUs shutdown - we cannot have |
| @@ -335,12 +401,8 @@ void check_panic_on_warn(const char *origin) · 1 unmodified line | ||
| 335 | 401 | */ |
| 336 | 402 | static void panic_other_cpus_shutdown(bool crash_kexec) |
| 337 | 403 | { |
| 338 | − if (panic_print & SYS_INFO_ALL_CPU_BT) { | |
| 339 | − /* Temporary allow non-panic CPUs to write their backtraces. */ | |
| 340 | − panic_triggering_all_cpu_backtrace = true; | |
| 341 | − trigger_all_cpu_backtrace(); | |
| 342 | − panic_triggering_all_cpu_backtrace = false; | |
| 343 | − } | |
| 404 | + if (panic_print & SYS_INFO_ALL_CPU_BT) | |
| 405 | + panic_trigger_all_cpu_backtrace(); | |
| 344 | 406 | |
| 345 | 407 | /* |
| 346 | 408 | * Note that smp_send_stop() is the usual SMP shutdown function, |
| @@ -368,7 +430,6 @@ void vpanic(const char *fmt, va_list args) · 21 unmodified lines | ||
| 368 | 430 | static char buf[1024]; |
| 369 | 431 | long i, i_next = 0, len; |
| 370 | 432 | int state = 0; |
| 371 | − int old_cpu, this_cpu; | |
| 372 | 433 | bool _crash_kexec_post_notifiers = crash_kexec_post_notifiers; |
| 373 | 434 | |
| 374 | 435 | if (panic_on_warn) { |
| @@ -405,13 +466,10 @@ void vpanic(const char *fmt, va_list args) · 30 unmodified lines | ||
| 405 | 466 | * `old_cpu == this_cpu' means we came from nmi_panic() which sets |
| 406 | 467 | * panic_cpu to this CPU. In this case, this is also the 1st CPU. |
| 407 | 468 | */ |
| 408 | − old_cpu = PANIC_CPU_INVALID; | |
| 409 | − this_cpu = raw_smp_processor_id(); | |
| 410 | − | |
| 411 | 469 | /* atomic_try_cmpxchg updates old_cpu on failure */ |
| 412 | − if (atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu)) { | |
| 470 | + if (panic_try_start()) { | |
| 413 | 471 | /* go ahead */ |
| 414 | − } else if (old_cpu != this_cpu) | |
| 472 | + } else if (panic_on_other_cpu()) | |
| 415 | 473 | panic_smp_self_stop(); |
| 416 | 474 | |
| 417 | 475 | console_verbose(); |
| @@ -422,13 +480,15 @@ void vpanic(const char *fmt, va_list args) · 4 unmodified lines | ||
| 422 | 480 | buf[len - 1] = '\0'; |
| 423 | 481 | |
| 424 | 482 | pr_emerg("Kernel panic - not syncing: %s\n", buf); |
| 425 | −#ifdef CONFIG_DEBUG_BUGVERBOSE | |
| 426 | 483 | /* |
| 427 | 484 | * Avoid nested stack-dumping if a panic occurs during oops processing |
| 428 | 485 | */ |
| 429 | − if (!test_taint(TAINT_DIE) && oops_in_progress <= 1) | |
| 486 | + if (test_taint(TAINT_DIE) || oops_in_progress > 1) { | |
| 487 | + panic_this_cpu_backtrace_printed = true; | |
| 488 | + } else if (IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE)) { | |
| 430 | 489 | dump_stack(); |
| 431 | −#endif | |
| 490 | + panic_this_cpu_backtrace_printed = true; | |
| 491 | + } | |
| 432 | 492 | |
| 433 | 493 | /* |
| 434 | 494 | * If kgdb is enabled, give it a chance to run before we stop all |
| @@ -937,12 +997,29 @@ EXPORT_SYMBOL(__stack_chk_fail); · 502 unmodified lines | ||
| 937 | 997 | #endif |
| 938 | 998 | |
| 939 | 999 | core_param(panic, panic_timeout, int, 0644); |
| 940 | −core_param(panic_print, panic_print, ulong, 0644); | |
| 941 | 1000 | core_param(pause_on_oops, pause_on_oops, int, 0644); |
| 942 | 1001 | core_param(panic_on_warn, panic_on_warn, int, 0644); |
| 943 | 1002 | core_param(crash_kexec_post_notifiers, crash_kexec_post_notifiers, bool, 0644); |
| 944 | 1003 | core_param(panic_console_replay, panic_console_replay, bool, 0644); |
| 945 | 1004 | |
| 1005 | +static int panic_print_set(const char *val, const struct kernel_param *kp) | |
| 1006 | +{ | |
| 1007 | + panic_print_deprecated(); | |
| 1008 | + return param_set_ulong(val, kp); | |
| 1009 | +} | |
| 1010 | + | |
| 1011 | +static int panic_print_get(char *val, const struct kernel_param *kp) | |
| 1012 | +{ | |
| 1013 | + panic_print_deprecated(); | |
| 1014 | + return param_get_ulong(val, kp); | |
| 1015 | +} | |
| 1016 | + | |
| 1017 | +static const struct kernel_param_ops panic_print_ops = { | |
| 1018 | + .set = panic_print_set, | |
| 1019 | + .get = panic_print_get, | |
| 1020 | +}; | |
| 1021 | +__core_param_cb(panic_print, &panic_print_ops, &panic_print, 0644); | |
| 1022 | + | |
| 946 | 1023 | static int __init oops_setup(char *s) |
| 947 | 1024 | { |
| 948 | 1025 | if (!s) |
| @@ -332,7 +332,6 @@ struct printk_message { · 331 unmodified lines | ||
| 332 | 332 | unsigned long dropped; |
| 333 | 333 | }; |
| 334 | 334 | |
| 335 | −bool other_cpu_in_panic(void); | |
| 336 | 335 | bool printk_get_next_message(struct printk_message *pmsg, u64 seq, |
| 337 | 336 | bool is_extended, bool may_supress); |
| 338 | 337 | |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | #include <linux/irqflags.h> |
| 13 | 13 | #include <linux/kthread.h> |
| 14 | 14 | #include <linux/minmax.h> |
| 15 | +#include <linux/panic.h> | |
| 15 | 16 | #include <linux/percpu.h> |
| 16 | 17 | #include <linux/preempt.h> |
| 17 | 18 | #include <linux/slab.h> |
| @@ -254,7 +255,7 @@ static int nbcon_context_try_acquire_direct(struct nbcon_context *ctxt, · 236 unmodified lines | ||
| 254 | 255 | * opportunity to perform any necessary cleanup if they were |
| 255 | 256 | * interrupted by the panic CPU while printing. |
| 256 | 257 | */ |
| 257 | − if (other_cpu_in_panic() && | |
| 258 | + if (panic_on_other_cpu() && | |
| 258 | 259 | (!is_reacquire || cur->unsafe_takeover)) { |
| 259 | 260 | return -EPERM; |
| 260 | 261 | } |
| @@ -309,7 +310,7 @@ static bool nbcon_waiter_matches(struct nbcon_state *cur, int expected_prio) · 48 unmodified lines | ||
| 309 | 310 | * Event #2 implies the new context is PANIC. |
| 310 | 311 | * Event #3 occurs when panic() has flushed the console. |
| 311 | 312 | * Event #4 occurs when a non-panic CPU reacquires. |
| 312 | − * Event #5 is not possible due to the other_cpu_in_panic() check | |
| 313 | + * Event #5 is not possible due to the panic_on_other_cpu() check | |
| 313 | 314 | * in nbcon_context_try_acquire_handover(). |
| 314 | 315 | */ |
| 315 | 316 | |
| @@ -348,7 +349,7 @@ static int nbcon_context_try_acquire_requested(struct nbcon_context *ctxt, · 32 unmodified lines | ||
| 348 | 349 | struct nbcon_state new; |
| 349 | 350 | |
| 350 | 351 | /* Note that the caller must still remove the request! */ |
| 351 | − if (other_cpu_in_panic()) | |
| 352 | + if (panic_on_other_cpu()) | |
| 352 | 353 | return -EPERM; |
| 353 | 354 | |
| 354 | 355 | /* |
| @@ -446,7 +447,7 @@ static int nbcon_context_try_acquire_handover(struct nbcon_context *ctxt, · 91 unmodified lines | ||
| 446 | 447 | * nbcon_waiter_matches(). In particular, the assumption that |
| 447 | 448 | * lower priorities are ignored during panic. |
| 448 | 449 | */ |
| 449 | − if (other_cpu_in_panic()) | |
| 450 | + if (panic_on_other_cpu()) | |
| 450 | 451 | return -EPERM; |
| 451 | 452 | |
| 452 | 453 | /* Handover is not possible on the same CPU. */ |
| @@ -589,7 +590,6 @@ static struct printk_buffers panic_nbcon_pbufs; · 136 unmodified lines | ||
| 589 | 590 | */ |
| 590 | 591 | static bool nbcon_context_try_acquire(struct nbcon_context *ctxt, bool is_reacquire) |
| 591 | 592 | { |
| 592 | − unsigned int cpu = smp_processor_id(); | |
| 593 | 593 | struct console *con = ctxt->console; |
| 594 | 594 | struct nbcon_state cur; |
| 595 | 595 | int err; |
| @@ -614,7 +614,7 @@ out: · 18 unmodified lines | ||
| 614 | 614 | /* Acquire succeeded. */ |
| 615 | 615 | |
| 616 | 616 | /* Assign the appropriate buffer for this context. */ |
| 617 | − if (atomic_read(&panic_cpu) == cpu) | |
| 617 | + if (panic_on_this_cpu()) | |
| 618 | 618 | ctxt->pbufs = &panic_nbcon_pbufs; |
| 619 | 619 | else |
| 620 | 620 | ctxt->pbufs = con->pbufs; |
| @@ -1394,7 +1394,7 @@ enum nbcon_prio nbcon_get_default_prio(void) · 773 unmodified lines | ||
| 1394 | 1394 | { |
| 1395 | 1395 | unsigned int *cpu_emergency_nesting; |
| 1396 | 1396 | |
| 1397 | − if (this_cpu_in_panic()) | |
| 1397 | + if (panic_on_this_cpu()) | |
| 1398 | 1398 | return NBCON_PRIO_PANIC; |
| 1399 | 1399 | |
| 1400 | 1400 | cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting(); |
| @@ -48,6 +48,7 @@ · 47 unmodified lines | ||
| 48 | 48 | #include <linux/sched/clock.h> |
| 49 | 49 | #include <linux/sched/debug.h> |
| 50 | 50 | #include <linux/sched/task_stack.h> |
| 51 | +#include <linux/panic.h> | |
| 51 | 52 | |
| 52 | 53 | #include <linux/uaccess.h> |
| 53 | 54 | #include <asm/sections.h> |
| @@ -345,34 +346,6 @@ static void __up_console_sem(unsigned long ip) · 291 unmodified lines | ||
| 345 | 346 | } |
| 346 | 347 | #define up_console_sem() __up_console_sem(_RET_IP_) |
| 347 | 348 | |
| 348 | −static bool panic_in_progress(void) | |
| 349 | −{ | |
| 350 | − return unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID); | |
| 351 | −} | |
| 352 | − | |
| 353 | −/* Return true if a panic is in progress on the current CPU. */ | |
| 354 | −bool this_cpu_in_panic(void) | |
| 355 | −{ | |
| 356 | − /* | |
| 357 | − * We can use raw_smp_processor_id() here because it is impossible for | |
| 358 | − * the task to be migrated to the panic_cpu, or away from it. If | |
| 359 | − * panic_cpu has already been set, and we're not currently executing on | |
| 360 | − * that CPU, then we never will be. | |
| 361 | − */ | |
| 362 | − return unlikely(atomic_read(&panic_cpu) == raw_smp_processor_id()); | |
| 363 | −} | |
| 364 | − | |
| 365 | −/* | |
| 366 | − * Return true if a panic is in progress on a remote CPU. | |
| 367 | − * | |
| 368 | − * On true, the local CPU should immediately release any printing resources | |
| 369 | − * that may be needed by the panic CPU. | |
| 370 | − */ | |
| 371 | −bool other_cpu_in_panic(void) | |
| 372 | −{ | |
| 373 | − return (panic_in_progress() && !this_cpu_in_panic()); | |
| 374 | −} | |
| 375 | − | |
| 376 | 349 | /* |
| 377 | 350 | * This is used for debugging the mess that is the VT code by |
| 378 | 351 | * keeping track if we have the console semaphore held. It's |
| @@ -2407,7 +2380,7 @@ asmlinkage int vprintk_emit(int facility, int level, · 2028 unmodified lines | ||
| 2407 | 2380 | * non-panic CPUs are generating any messages, they will be |
| 2408 | 2381 | * silently dropped. |
| 2409 | 2382 | */ |
| 2410 | − if (other_cpu_in_panic() && | |
| 2383 | + if (panic_on_other_cpu() && | |
| 2411 | 2384 | !debug_non_panic_cpus && |
| 2412 | 2385 | !panic_triggering_all_cpu_backtrace) |
| 2413 | 2386 | return 0; |
| @@ -2843,7 +2816,7 @@ void console_lock(void) · 429 unmodified lines | ||
| 2843 | 2816 | might_sleep(); |
| 2844 | 2817 | |
| 2845 | 2818 | /* On panic, the console_lock must be left to the panic cpu. */ |
| 2846 | − while (other_cpu_in_panic()) | |
| 2819 | + while (panic_on_other_cpu()) | |
| 2847 | 2820 | msleep(1000); |
| 2848 | 2821 | |
| 2849 | 2822 | down_console_sem(); |
| @@ -2863,7 +2836,7 @@ EXPORT_SYMBOL(console_lock); · 13 unmodified lines | ||
| 2863 | 2836 | int console_trylock(void) |
| 2864 | 2837 | { |
| 2865 | 2838 | /* On panic, the console_lock must be left to the panic cpu. */ |
| 2866 | − if (other_cpu_in_panic()) | |
| 2839 | + if (panic_on_other_cpu()) | |
| 2867 | 2840 | return 0; |
| 2868 | 2841 | if (down_trylock_console_sem()) |
| 2869 | 2842 | return 0; |
| @@ -3243,7 +3216,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove · 373 unmodified lines | ||
| 3243 | 3216 | any_progress = true; |
| 3244 | 3217 | |
| 3245 | 3218 | /* Allow panic_cpu to take over the consoles safely. */ |
| 3246 | − if (other_cpu_in_panic()) | |
| 3219 | + if (panic_on_other_cpu()) | |
| 3247 | 3220 | goto abandon; |
| 3248 | 3221 | |
| 3249 | 3222 | if (do_cond_resched) |
| @@ -2143,7 +2143,7 @@ static bool _prb_read_valid(struct printk_ringbuffer *rb, u64 *seq, · 2142 unmodified lines | ||
| 2143 | 2143 | * But it would have the sequence number returned |
| 2144 | 2144 | * by "prb_next_reserve_seq() - 1". |
| 2145 | 2145 | */ |
| 2146 | − if (this_cpu_in_panic() && | |
| 2146 | + if (panic_on_this_cpu() && | |
| 2147 | 2147 | (!debug_non_panic_cpus || legacy_allow_panic_sync) && |
| 2148 | 2148 | ((*seq + 1) < prb_next_reserve_seq(rb))) { |
| 2149 | 2149 | (*seq)++; |
| @@ -1734,6 +1734,7 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource, · 1733 unmodified lines | ||
| 1734 | 1734 | struct rlimit old, new; |
| 1735 | 1735 | struct task_struct *tsk; |
| 1736 | 1736 | unsigned int checkflags = 0; |
| 1737 | + bool need_tasklist; | |
| 1737 | 1738 | int ret; |
| 1738 | 1739 | |
| 1739 | 1740 | if (old_rlim) |
| @@ -1760,8 +1761,25 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource, · 20 unmodified lines | ||
| 1760 | 1761 | get_task_struct(tsk); |
| 1761 | 1762 | rcu_read_unlock(); |
| 1762 | 1763 | |
| 1763 | − ret = do_prlimit(tsk, resource, new_rlim ? &new : NULL, | |
| 1764 | − old_rlim ? &old : NULL); | |
| 1764 | + need_tasklist = !same_thread_group(tsk, current); | |
| 1765 | + if (need_tasklist) { | |
| 1766 | + /* | |
| 1767 | + * Ensure we can't race with group exit or de_thread(), | |
| 1768 | + * so tsk->group_leader can't be freed or changed until | |
| 1769 | + * read_unlock(tasklist_lock) below. | |
| 1770 | + */ | |
| 1771 | + read_lock(&tasklist_lock); | |
| 1772 | + if (!pid_alive(tsk)) | |
| 1773 | + ret = -ESRCH; | |
| 1774 | + } | |
| 1775 | + | |
| 1776 | + if (!ret) { | |
| 1777 | + ret = do_prlimit(tsk, resource, new_rlim ? &new : NULL, | |
| 1778 | + old_rlim ? &old : NULL); | |
| 1779 | + } | |
| 1780 | + | |
| 1781 | + if (need_tasklist) | |
| 1782 | + read_unlock(&tasklist_lock); | |
| 1765 | 1783 | |
| 1766 | 1784 | if (!ret && old_rlim) { |
| 1767 | 1785 | rlim_to_rlim64(&old, &old64); |
| @@ -2515,7 +2533,17 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, · 747 unmodified lines | ||
| 2515 | 2533 | error = -EINVAL; |
| 2516 | 2534 | break; |
| 2517 | 2535 | } |
| 2536 | + /* | |
| 2537 | + * Ensure that either: | |
| 2538 | + * | |
| 2539 | + * 1. Subsequent getppid() calls reflect the parent process having died. | |
| 2540 | + * 2. forget_original_parent() will send the new me->pdeath_signal. | |
| 2541 | + * | |
| 2542 | + * Also prevent the read of me->pdeath_signal from being a data race. | |
| 2543 | + */ | |
| 2544 | + read_lock(&tasklist_lock); | |
| 2518 | 2545 | me->pdeath_signal = arg2; |
| 2546 | + read_unlock(&tasklist_lock); | |
| 2519 | 2547 | break; |
| 2520 | 2548 | case PR_GET_PDEATHSIG: |
| 2521 | 2549 | error = put_user(me->pdeath_signal, (int __user *)arg2); |
| @@ -425,7 +425,11 @@ static DEFINE_PER_CPU(u8, cpustat_tail); · 424 unmodified lines | ||
| 425 | 425 | */ |
| 426 | 426 | static u16 get_16bit_precision(u64 data_ns) |
| 427 | 427 | { |
| 428 | − return data_ns >> 24LL; /* 2^24ns ~= 16.8ms */ | |
| 428 | + /* | |
| 429 | + * 2^24ns ~= 16.8ms | |
| 430 | + * Round to the nearest multiple of 16.8 milliseconds. | |
| 431 | + */ | |
| 432 | + return (data_ns + (1 << 23)) >> 24LL; | |
| 429 | 433 | } |
| 430 | 434 | |
| 431 | 435 | static void update_cpustat(void) |
| @@ -444,6 +448,14 @@ static void update_cpustat(void) · 12 unmodified lines | ||
| 444 | 448 | old_stat = __this_cpu_read(cpustat_old[i]); |
| 445 | 449 | new_stat = get_16bit_precision(cpustat[tracked_stats[i]]); |
| 446 | 450 | util = DIV_ROUND_UP(100 * (new_stat - old_stat), sample_period_16); |
| 451 | + /* | |
| 452 | + * Since we use 16-bit precision, the raw data will undergo | |
| 453 | + * integer division, which may sometimes result in data loss, | |
| 454 | + * and then result might exceed 100%. To avoid confusion, | |
| 455 | + * we enforce a 100% display cap when calculations exceed this threshold. | |
| 456 | + */ | |
| 457 | + if (util > 100) | |
| 458 | + util = 100; | |
| 447 | 459 | __this_cpu_write(cpustat_util[tail][i], util); |
| 448 | 460 | __this_cpu_write(cpustat_old[i], new_stat); |
| 449 | 461 | } |
| @@ -455,17 +467,17 @@ static void print_cpustat(void) · 5 unmodified lines | ||
| 455 | 467 | { |
| 456 | 468 | int i, group; |
| 457 | 469 | u8 tail = __this_cpu_read(cpustat_tail); |
| 458 | − u64 sample_period_second = sample_period; | |
| 470 | + u64 sample_period_msecond = sample_period; | |
| 459 | 471 | |
| 460 | − do_div(sample_period_second, NSEC_PER_SEC); | |
| 472 | + do_div(sample_period_msecond, NSEC_PER_MSEC); | |
| 461 | 473 | |
| 462 | 474 | /* |
| 463 | 475 | * Outputting the "watchdog" prefix on every line is redundant and not |
| 464 | 476 | * concise, and the original alarm information is sufficient for |
| 465 | 477 | * positioning in logs, hence here printk() is used instead of pr_crit(). |
| 466 | 478 | */ |
| 467 | − printk(KERN_CRIT "CPU#%d Utilization every %llus during lockup:\n", | |
| 468 | − smp_processor_id(), sample_period_second); | |
| 479 | + printk(KERN_CRIT "CPU#%d Utilization every %llums during lockup:\n", | |
| 480 | + smp_processor_id(), sample_period_msecond); | |
| 469 | 481 | |
| 470 | 482 | for (i = 0; i < NUM_SAMPLE_PERIODS; i++) { |
| 471 | 483 | group = (tail + i) % NUM_SAMPLE_PERIODS; |
| @@ -740,6 +752,12 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) · 268 unmodified lines | ||
| 740 | 752 | if (!watchdog_enabled) |
| 741 | 753 | return HRTIMER_NORESTART; |
| 742 | 754 | |
| 755 | + /* | |
| 756 | + * pass the buddy check if a panic is in process | |
| 757 | + */ | |
| 758 | + if (panic_in_progress()) | |
| 759 | + return HRTIMER_NORESTART; | |
| 760 | + | |
| 743 | 761 | watchdog_hardlockup_kick(); |
| 744 | 762 | |
| 745 | 763 | /* kick the softlockup detector */ |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | |
| 13 | 13 | #define pr_fmt(fmt) "NMI watchdog: " fmt |
| 14 | 14 | |
| 15 | +#include <linux/panic.h> | |
| 15 | 16 | #include <linux/nmi.h> |
| 16 | 17 | #include <linux/atomic.h> |
| 17 | 18 | #include <linux/module.h> |
| @@ -108,6 +109,9 @@ static void watchdog_overflow_callback(struct perf_event *event, · 90 unmodified lines | ||
| 108 | 109 | /* Ensure the watchdog never gets throttled */ |
| 109 | 110 | event->hw.interrupts = 0; |
| 110 | 111 | |
| 112 | + if (panic_in_progress()) | |
| 113 | + return; | |
| 114 | + | |
| 111 | 115 | if (!watchdog_check_timestamp()) |
| 112 | 116 | return; |
| 113 | 117 | |
| @@ -1067,12 +1067,6 @@ config PANIC_ON_OOPS · 1066 unmodified lines | ||
| 1067 | 1067 | |
| 1068 | 1068 | Say N if unsure. |
| 1069 | 1069 | |
| 1070 | −config PANIC_ON_OOPS_VALUE | |
| 1071 | − int | |
| 1072 | − range 0 1 | |
| 1073 | − default 0 if !PANIC_ON_OOPS | |
| 1074 | − default 1 if PANIC_ON_OOPS | |
| 1075 | − | |
| 1076 | 1070 | config PANIC_TIMEOUT |
| 1077 | 1071 | int "panic timeout" |
| 1078 | 1072 | default 0 |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | #include <linux/proc_fs.h> |
| 10 | 10 | #include <linux/seq_buf.h> |
| 11 | 11 | #include <linux/seq_file.h> |
| 12 | +#include <linux/string_choices.h> | |
| 12 | 13 | #include <linux/vmalloc.h> |
| 13 | 14 | #include <linux/kmemleak.h> |
| 14 | 15 | |
| @@ -728,7 +729,7 @@ static int __init setup_early_mem_profiling(char *str) · 713 unmodified lines | ||
| 728 | 729 | } |
| 729 | 730 | mem_profiling_support = true; |
| 730 | 731 | pr_info("Memory allocation profiling is enabled %s compression and is turned %s!\n", |
| 731 | − compressed ? "with" : "without", enable ? "on" : "off"); | |
| 732 | + compressed ? "with" : "without", str_on_off(enable)); | |
| 732 | 733 | } |
| 733 | 734 | |
| 734 | 735 | if (enable != mem_alloc_profiling_enabled()) { |
| @@ -653,9 +653,9 @@ int btree_merge(struct btree_head *target, struct btree_head *victim, · 652 unmodified lines | ||
| 653 | 653 | * walks to remove a single object from the victim. |
| 654 | 654 | */ |
| 655 | 655 | for (;;) { |
| 656 | − if (!btree_last(victim, geo, key)) | |
| 656 | + val = btree_last(victim, geo, key); | |
| 657 | + if (!val) | |
| 657 | 658 | break; |
| 658 | − val = btree_lookup(victim, geo, key); | |
| 659 | 659 | err = btree_insert(target, geo, key, val, gfp); |
| 660 | 660 | if (err) |
| 661 | 661 | return err; |
| @@ -49,15 +49,15 @@ struct compress_format { · 48 unmodified lines | ||
| 49 | 49 | }; |
| 50 | 50 | |
| 51 | 51 | static const struct compress_format compressed_formats[] __initconst = { |
| 52 | − { {0x1f, 0x8b}, "gzip", gunzip }, | |
| 53 | − { {0x1f, 0x9e}, "gzip", gunzip }, | |
| 54 | − { {0x42, 0x5a}, "bzip2", bunzip2 }, | |
| 55 | − { {0x5d, 0x00}, "lzma", unlzma }, | |
| 56 | − { {0xfd, 0x37}, "xz", unxz }, | |
| 57 | − { {0x89, 0x4c}, "lzo", unlzo }, | |
| 58 | − { {0x02, 0x21}, "lz4", unlz4 }, | |
| 59 | − { {0x28, 0xb5}, "zstd", unzstd }, | |
| 60 | − { {0, 0}, NULL, NULL } | |
| 52 | + { .magic = {0x1f, 0x8b}, .name = "gzip", .decompressor = gunzip }, | |
| 53 | + { .magic = {0x1f, 0x9e}, .name = "gzip", .decompressor = gunzip }, | |
| 54 | + { .magic = {0x42, 0x5a}, .name = "bzip2", .decompressor = bunzip2 }, | |
| 55 | + { .magic = {0x5d, 0x00}, .name = "lzma", .decompressor = unlzma }, | |
| 56 | + { .magic = {0xfd, 0x37}, .name = "xz", .decompressor = unxz }, | |
| 57 | + { .magic = {0x89, 0x4c}, .name = "lzo", .decompressor = unlzo }, | |
| 58 | + { .magic = {0x02, 0x21}, .name = "lz4", .decompressor = unlz4 }, | |
| 59 | + { .magic = {0x28, 0xb5}, .name = "zstd", .decompressor = unzstd }, | |
| 60 | + { /* sentinel */ } | |
| 61 | 61 | }; |
| 62 | 62 | |
| 63 | 63 | decompress_fn __init decompress_method(const unsigned char *inbuf, long len, |
| @@ -73,11 +73,10 @@ decompress_fn __init decompress_method(const unsigned char *inbuf, long len, · 9 unmodified lines | ||
| 73 | 73 | |
| 74 | 74 | pr_debug("Compressed data magic: %#.2x %#.2x\n", inbuf[0], inbuf[1]); |
| 75 | 75 | |
| 76 | − for (cf = compressed_formats; cf->name; cf++) { | |
| 76 | + for (cf = compressed_formats; cf->name; cf++) | |
| 77 | 77 | if (!memcmp(inbuf, cf->magic, 2)) |
| 78 | 78 | break; |
| 79 | 79 | |
| 80 | − } | |
| 81 | 80 | if (name) |
| 82 | 81 | *name = cf->name; |
| 83 | 82 | return cf->decompressor; |
| @@ -159,7 +159,6 @@ static int digsig_verify_rsa(struct key *key, · 158 unmodified lines | ||
| 159 | 159 | |
| 160 | 160 | len = mlen; |
| 161 | 161 | head = len - l; |
| 162 | − memset(out1, 0, head); | |
| 163 | 162 | memcpy(out1 + head, p, l); |
| 164 | 163 | |
| 165 | 164 | kfree(p); |
| @@ -102,7 +102,7 @@ static void __dump_stack(const char *log_lvl) · 101 unmodified lines | ||
| 102 | 102 | */ |
| 103 | 103 | asmlinkage __visible void dump_stack_lvl(const char *log_lvl) |
| 104 | 104 | { |
| 105 | − bool in_panic = this_cpu_in_panic(); | |
| 105 | + bool in_panic = panic_on_this_cpu(); | |
| 106 | 106 | unsigned long flags; |
| 107 | 107 | |
| 108 | 108 | /* |
| @@ -22,10 +22,8 @@ static int __init fail_usercopy_debugfs(void) · 21 unmodified lines | ||
| 22 | 22 | |
| 23 | 23 | dir = fault_create_debugfs_attr("fail_usercopy", NULL, |
| 24 | 24 | &fail_usercopy.attr); |
| 25 | − if (IS_ERR(dir)) | |
| 26 | − return PTR_ERR(dir); | |
| 27 | 25 | |
| 28 | − return 0; | |
| 26 | + return PTR_ERR_OR_ZERO(dir); | |
| 29 | 27 | } |
| 30 | 28 | |
| 31 | 29 | late_initcall(fail_usercopy_debugfs); |
| @@ -899,8 +899,11 @@ struct gen_pool *of_gen_pool_get(struct device_node *np, · 898 unmodified lines | ||
| 899 | 899 | if (!name) |
| 900 | 900 | name = of_node_full_name(np_pool); |
| 901 | 901 | } |
| 902 | − if (pdev) | |
| 902 | + if (pdev) { | |
| 903 | 903 | pool = gen_pool_get(&pdev->dev, name); |
| 904 | + put_device(&pdev->dev); | |
| 905 | + } | |
| 906 | + | |
| 904 | 907 | of_node_put(np_pool); |
| 905 | 908 | |
| 906 | 909 | return pool; |
| @@ -75,7 +75,7 @@ ref_tracker_get_stats(struct ref_tracker_dir *dir, unsigned int limit) · 74 unmodified lines | ||
| 75 | 75 | struct ref_tracker *tracker; |
| 76 | 76 | |
| 77 | 77 | stats = kmalloc(struct_size(stats, stacks, limit), |
| 78 | − GFP_NOWAIT | __GFP_NOWARN); | |
| 78 | + GFP_NOWAIT); | |
| 79 | 79 | if (!stats) |
| 80 | 80 | return ERR_PTR(-ENOMEM); |
| 81 | 81 | stats->total = 0; |
| @@ -159,7 +159,7 @@ __ref_tracker_dir_pr_ostream(struct ref_tracker_dir *dir, · 77 unmodified lines | ||
| 159 | 159 | return; |
| 160 | 160 | } |
| 161 | 161 | |
| 162 | − sbuf = kmalloc(STACK_BUF_SIZE, GFP_NOWAIT | __GFP_NOWARN); | |
| 162 | + sbuf = kmalloc(STACK_BUF_SIZE, GFP_NOWAIT); | |
| 163 | 163 | |
| 164 | 164 | for (i = 0, skipped = stats->total; i < stats->count; ++i) { |
| 165 | 165 | stack = stats->stacks[i].stack_handle; |
| @@ -306,7 +306,7 @@ int ref_tracker_free(struct ref_tracker_dir *dir, · 140 unmodified lines | ||
| 306 | 306 | } |
| 307 | 307 | nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1); |
| 308 | 308 | stack_handle = stack_depot_save(entries, nr_entries, |
| 309 | − GFP_NOWAIT | __GFP_NOWARN); | |
| 309 | + GFP_NOWAIT); | |
| 310 | 310 | |
| 311 | 311 | spin_lock_irqsave(&dir->lock, flags); |
| 312 | 312 | if (tracker->dead) { |
| @@ -55,7 +55,7 @@ int sysctl_sys_info_handler(const struct ctl_table *ro_table, int write, · 54 unmodified lines | ||
| 55 | 55 | void *buffer, size_t *lenp, |
| 56 | 56 | loff_t *ppos) |
| 57 | 57 | { |
| 58 | − char names[sizeof(sys_info_avail) + 1]; | |
| 58 | + char names[sizeof(sys_info_avail)]; | |
| 59 | 59 | struct ctl_table table; |
| 60 | 60 | unsigned long *si_bits_global; |
| 61 | 61 | |
| @@ -81,6 +81,7 @@ int sysctl_sys_info_handler(const struct ctl_table *ro_table, int write, · 19 unmodified lines | ||
| 81 | 81 | char *delim = ""; |
| 82 | 82 | int i, len = 0; |
| 83 | 83 | |
| 84 | + names[0] = '\0'; | |
| 84 | 85 | for (i = 0; i < ARRAY_SIZE(si_names); i++) { |
| 85 | 86 | if (*si_bits_global & si_names[i].bit) { |
| 86 | 87 | len += scnprintf(names + len, sizeof(names) - len, |
| @@ -26,6 +26,7 @@ · 25 unmodified lines | ||
| 26 | 26 | #include <linux/kthread.h> |
| 27 | 27 | #include <linux/vmalloc.h> |
| 28 | 28 | #include <linux/efi_embedded_fw.h> |
| 29 | +#include <linux/string_choices.h> | |
| 29 | 30 | |
| 30 | 31 | MODULE_IMPORT_NS("TEST_FIRMWARE"); |
| 31 | 32 | |
| @@ -304,17 +305,17 @@ static ssize_t config_show(struct device *dev, · 272 unmodified lines | ||
| 304 | 305 | "FW_ACTION_NOUEVENT"); |
| 305 | 306 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 306 | 307 | "into_buf:\t\t%s\n", |
| 307 | − test_fw_config->into_buf ? "true" : "false"); | |
| 308 | + str_true_false(test_fw_config->into_buf)); | |
| 308 | 309 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 309 | 310 | "buf_size:\t%zu\n", test_fw_config->buf_size); |
| 310 | 311 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 311 | 312 | "file_offset:\t%zu\n", test_fw_config->file_offset); |
| 312 | 313 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 313 | 314 | "partial:\t\t%s\n", |
| 314 | − test_fw_config->partial ? "true" : "false"); | |
| 315 | + str_true_false(test_fw_config->partial)); | |
| 315 | 316 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 316 | 317 | "sync_direct:\t\t%s\n", |
| 317 | − test_fw_config->sync_direct ? "true" : "false"); | |
| 318 | + str_true_false(test_fw_config->sync_direct)); | |
| 318 | 319 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 319 | 320 | "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx); |
| 320 | 321 | if (test_fw_config->upload_name) |
| @@ -2636,6 +2636,11 @@ sub exclude_global_initialisers { · 2635 unmodified lines | ||
| 2636 | 2636 | $realfile =~ m@/bpf/.*\.bpf\.c$@; |
| 2637 | 2637 | } |
| 2638 | 2638 | |
| 2639 | +sub is_userspace { | |
| 2640 | + my ($realfile) = @_; | |
| 2641 | + return ($realfile =~ m@^tools/@ || $realfile =~ m@^scripts/@); | |
| 2642 | +} | |
| 2643 | + | |
| 2639 | 2644 | sub process { |
| 2640 | 2645 | my $filename = shift; |
| 2641 | 2646 | |
| @@ -3294,7 +3299,7 @@ sub process { · 652 unmodified lines | ||
| 3294 | 3299 | # file delta changes |
| 3295 | 3300 | $line =~ /^\s*(?:[\w\.\-\+]*\/)++[\w\.\-\+]+:/ || |
| 3296 | 3301 | # filename then : |
| 3297 | − $line =~ /^\s*(?:Fixes:|$link_tags_search|$signature_tags)/i || | |
| 3302 | + $line =~ /^\s*(?:Fixes:|https?:|$link_tags_search|$signature_tags)/i || | |
| 3298 | 3303 | # A Fixes:, link or signature tag line |
| 3299 | 3304 | $commit_log_possible_stack_dump)) { |
| 3300 | 3305 | WARN("COMMIT_LOG_LONG_LINE", |
| @@ -7018,21 +7023,20 @@ sub process { · 3717 unmodified lines | ||
| 7018 | 7023 | # } |
| 7019 | 7024 | # } |
| 7020 | 7025 | # } |
| 7021 | − | |
| 7022 | 7026 | # strcpy uses that should likely be strscpy |
| 7023 | − if ($line =~ /\bstrcpy\s*\(/) { | |
| 7027 | + if ($line =~ /\bstrcpy\s*\(/ && !is_userspace($realfile)) { | |
| 7024 | 7028 | WARN("STRCPY", |
| 7025 | 7029 | "Prefer strscpy over strcpy - see: https://github.com/KSPP/linux/issues/88\n" . $herecurr); |
| 7026 | 7030 | } |
| 7027 | 7031 | |
| 7028 | 7032 | # strlcpy uses that should likely be strscpy |
| 7029 | − if ($line =~ /\bstrlcpy\s*\(/) { | |
| 7033 | + if ($line =~ /\bstrlcpy\s*\(/ && !is_userspace($realfile)) { | |
| 7030 | 7034 | WARN("STRLCPY", |
| 7031 | 7035 | "Prefer strscpy over strlcpy - see: https://github.com/KSPP/linux/issues/89\n" . $herecurr); |
| 7032 | 7036 | } |
| 7033 | 7037 | |
| 7034 | 7038 | # strncpy uses that should likely be strscpy or strscpy_pad |
| 7035 | − if ($line =~ /\bstrncpy\s*\(/) { | |
| 7039 | + if ($line =~ /\bstrncpy\s*\(/ && !is_userspace($realfile)) { | |
| 7036 | 7040 | WARN("STRNCPY", |
| 7037 | 7041 | "Prefer strscpy, strscpy_pad, or __nonstring over strncpy - see: https://github.com/KSPP/linux/issues/90\n" . $herecurr); |
| 7038 | 7042 | } |
| @@ -10,12 +10,21 @@ virtual org · 9 unmodified lines | ||
| 10 | 10 | virtual report |
| 11 | 11 | |
| 12 | 12 | @match1@ |
| 13 | +declarer name builtin_i2c_driver; | |
| 14 | +declarer name builtin_platform_driver; | |
| 15 | +declarer name builtin_platform_driver_probe; | |
| 13 | 16 | declarer name module_i2c_driver; |
| 14 | 17 | declarer name module_platform_driver; |
| 15 | 18 | declarer name module_platform_driver_probe; |
| 16 | 19 | identifier __driver; |
| 17 | 20 | @@ |
| 18 | 21 | ( |
| 22 | + builtin_i2c_driver(__driver); | |
| 23 | +| | |
| 24 | + builtin_platform_driver(__driver); | |
| 25 | +| | |
| 26 | + builtin_platform_driver_probe(__driver, ...); | |
| 27 | +| | |
| 19 | 28 | module_i2c_driver(__driver); |
| 20 | 29 | | |
| 21 | 30 | module_platform_driver(__driver); |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | −/// Make sure (of/i2c/platform)_device_id tables are NULL terminated | |
| 2 | +/// Make sure (of/i2c/platform/spi)_device_id tables are NULL terminated | |
| 3 | 3 | // |
| 4 | 4 | // Keywords: of_table i2c_table platform_table |
| 5 | 5 | // Confidence: Medium |
| @@ -15,14 +15,14 @@ identifier var, arr; · 9 unmodified lines | ||
| 15 | 15 | expression E; |
| 16 | 16 | @@ |
| 17 | 17 | ( |
| 18 | −struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { | |
| 18 | +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { | |
| 19 | 19 | ..., |
| 20 | 20 | { |
| 21 | 21 | .var = E, |
| 22 | 22 | * } |
| 23 | 23 | }; |
| 24 | 24 | | |
| 25 | −struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { | |
| 25 | +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { | |
| 26 | 26 | ..., |
| 27 | 27 | * { ..., E, ... }, |
| 28 | 28 | }; |
| @@ -33,7 +33,7 @@ identifier var, arr; · 4 unmodified lines | ||
| 33 | 33 | expression E; |
| 34 | 34 | @@ |
| 35 | 35 | ( |
| 36 | −struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { | |
| 36 | +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { | |
| 37 | 37 | ..., |
| 38 | 38 | { |
| 39 | 39 | .var = E, |
| @@ -42,7 +42,7 @@ struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { · 2 unmodified lines | ||
| 42 | 42 | + { } |
| 43 | 43 | }; |
| 44 | 44 | | |
| 45 | −struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { | |
| 45 | +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { | |
| 46 | 46 | ..., |
| 47 | 47 | { ..., E, ... }, |
| 48 | 48 | + { }, |
| @@ -55,7 +55,7 @@ identifier var, arr; · 6 unmodified lines | ||
| 55 | 55 | expression E; |
| 56 | 56 | @@ |
| 57 | 57 | ( |
| 58 | −struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { | |
| 58 | +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { | |
| 59 | 59 | ..., |
| 60 | 60 | { |
| 61 | 61 | .var = E, |
| @@ -63,7 +63,7 @@ struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { · 1 unmodified line | ||
| 63 | 63 | @p1 |
| 64 | 64 | }; |
| 65 | 65 | | |
| 66 | −struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { | |
| 66 | +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { | |
| 67 | 67 | ..., |
| 68 | 68 | { ..., E, ... } |
| 69 | 69 | @p1 |
| @@ -42,14 +42,13 @@ · 41 unmodified lines | ||
| 42 | 42 | #include <linux/genetlink.h> |
| 43 | 43 | #include <linux/taskstats.h> |
| 44 | 44 | #include <linux/cgroupstats.h> |
| 45 | +#include <stddef.h> | |
| 45 | 46 | |
| 46 | −#define PSI_CPU_SOME "/proc/pressure/cpu" | |
| 47 | −#define PSI_CPU_FULL "/proc/pressure/cpu" | |
| 48 | −#define PSI_MEMORY_SOME "/proc/pressure/memory" | |
| 49 | −#define PSI_MEMORY_FULL "/proc/pressure/memory" | |
| 50 | −#define PSI_IO_SOME "/proc/pressure/io" | |
| 51 | −#define PSI_IO_FULL "/proc/pressure/io" | |
| 52 | −#define PSI_IRQ_FULL "/proc/pressure/irq" | |
| 47 | +#define PSI_PATH "/proc/pressure" | |
| 48 | +#define PSI_CPU_PATH "/proc/pressure/cpu" | |
| 49 | +#define PSI_MEMORY_PATH "/proc/pressure/memory" | |
| 50 | +#define PSI_IO_PATH "/proc/pressure/io" | |
| 51 | +#define PSI_IRQ_PATH "/proc/pressure/irq" | |
| 53 | 52 | |
| 54 | 53 | #define NLA_NEXT(na) ((struct nlattr *)((char *)(na) + NLA_ALIGN((na)->nla_len))) |
| 55 | 54 | #define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN)) |
| @@ -61,24 +60,28 @@ · 5 unmodified lines | ||
| 61 | 60 | #define TASK_COMM_LEN 16 |
| 62 | 61 | #define MAX_MSG_SIZE 1024 |
| 63 | 62 | #define MAX_TASKS 1000 |
| 63 | +#define MAX_BUF_LEN 256 | |
| 64 | 64 | #define SET_TASK_STAT(task_count, field) tasks[task_count].field = stats.field |
| 65 | 65 | #define BOOL_FPRINT(stream, fmt, ...) \ |
| 66 | 66 | ({ \ |
| 67 | 67 | int ret = fprintf(stream, fmt, ##__VA_ARGS__); \ |
| 68 | 68 | ret >= 0; \ |
| 69 | 69 | }) |
| 70 | +#define TASK_AVG(task, field) average_ms((task).field##_delay_total, (task).field##_count) | |
| 70 | 71 | #define PSI_LINE_FORMAT "%-12s %6.1f%%/%6.1f%%/%6.1f%%/%8llu(ms)\n" |
| 71 | − | |
| 72 | −/* Program settings structure */ | |
| 73 | −struct config { | |
| 74 | − int delay; /* Update interval in seconds */ | |
| 75 | − int iterations; /* Number of iterations, 0 == infinite */ | |
| 76 | − int max_processes; /* Maximum number of processes to show */ | |
| 77 | − char sort_field; /* Field to sort by */ | |
| 78 | − int output_one_time; /* Output once and exit */ | |
| 79 | − int monitor_pid; /* Monitor specific PID */ | |
| 80 | − char *container_path; /* Path to container cgroup */ | |
| 81 | −}; | |
| 72 | +#define DELAY_FMT_DEFAULT "%8.2f %8.2f %8.2f %8.2f\n" | |
| 73 | +#define DELAY_FMT_MEMVERBOSE "%8.2f %8.2f %8.2f %8.2f %8.2f %8.2f\n" | |
| 74 | +#define SORT_FIELD(name, cmd, modes) \ | |
| 75 | + {#name, #cmd, \ | |
| 76 | + offsetof(struct task_info, name##_delay_total), \ | |
| 77 | + offsetof(struct task_info, name##_count), \ | |
| 78 | + modes} | |
| 79 | +#define END_FIELD {NULL, 0, 0} | |
| 80 | + | |
| 81 | +/* Display mode types */ | |
| 82 | +#define MODE_TYPE_ALL (0xFFFFFFFF) | |
| 83 | +#define MODE_DEFAULT (1 << 0) | |
| 84 | +#define MODE_MEMVERBOSE (1 << 1) | |
| 82 | 85 | |
| 83 | 86 | /* PSI statistics structure */ |
| 84 | 87 | struct psi_stats { |
| @@ -119,6 +122,8 @@ struct task_info { · 34 unmodified lines | ||
| 119 | 122 | unsigned long long wpcopy_delay_total; |
| 120 | 123 | unsigned long long irq_count; |
| 121 | 124 | unsigned long long irq_delay_total; |
| 125 | + unsigned long long mem_count; | |
| 126 | + unsigned long long mem_delay_total; | |
| 122 | 127 | }; |
| 123 | 128 | |
| 124 | 129 | /* Container statistics structure */ |
| @@ -130,6 +135,27 @@ struct container_stats { · 5 unmodified lines | ||
| 130 | 135 | int nr_io_wait; /* Number of processes in IO wait */ |
| 131 | 136 | }; |
| 132 | 137 | |
| 138 | +/* Delay field structure */ | |
| 139 | +struct field_desc { | |
| 140 | + const char *name; /* Field name for cmdline argument */ | |
| 141 | + const char *cmd_char; /* Interactive command */ | |
| 142 | + unsigned long total_offset; /* Offset of total delay in task_info */ | |
| 143 | + unsigned long count_offset; /* Offset of count in task_info */ | |
| 144 | + size_t supported_modes; /* Supported display modes */ | |
| 145 | +}; | |
| 146 | + | |
| 147 | +/* Program settings structure */ | |
| 148 | +struct config { | |
| 149 | + int delay; /* Update interval in seconds */ | |
| 150 | + int iterations; /* Number of iterations, 0 == infinite */ | |
| 151 | + int max_processes; /* Maximum number of processes to show */ | |
| 152 | + int output_one_time; /* Output once and exit */ | |
| 153 | + int monitor_pid; /* Monitor specific PID */ | |
| 154 | + char *container_path; /* Path to container cgroup */ | |
| 155 | + const struct field_desc *sort_field; /* Current sort field */ | |
| 156 | + size_t display_mode; /* Current display mode */ | |
| 157 | +}; | |
| 158 | + | |
| 133 | 159 | /* Global variables */ |
| 134 | 160 | static struct config cfg; |
| 135 | 161 | static struct psi_stats psi; |
| @@ -137,6 +163,19 @@ static struct task_info tasks[MAX_TASKS]; · 1 unmodified line | ||
| 137 | 163 | static int task_count; |
| 138 | 164 | static int running = 1; |
| 139 | 165 | static struct container_stats container_stats; |
| 166 | +static const struct field_desc sort_fields[] = { | |
| 167 | + SORT_FIELD(cpu, c, MODE_DEFAULT), | |
| 168 | + SORT_FIELD(blkio, i, MODE_DEFAULT), | |
| 169 | + SORT_FIELD(irq, q, MODE_DEFAULT), | |
| 170 | + SORT_FIELD(mem, m, MODE_DEFAULT | MODE_MEMVERBOSE), | |
| 171 | + SORT_FIELD(swapin, s, MODE_MEMVERBOSE), | |
| 172 | + SORT_FIELD(freepages, r, MODE_MEMVERBOSE), | |
| 173 | + SORT_FIELD(thrashing, t, MODE_MEMVERBOSE), | |
| 174 | + SORT_FIELD(compact, p, MODE_MEMVERBOSE), | |
| 175 | + SORT_FIELD(wpcopy, w, MODE_MEMVERBOSE), | |
| 176 | + END_FIELD | |
| 177 | +}; | |
| 178 | +static int sort_selected; | |
| 140 | 179 | |
| 141 | 180 | /* Netlink socket variables */ |
| 142 | 181 | static int nl_sd = -1; |
| @@ -158,18 +197,75 @@ static void disable_raw_mode(void) · 15 unmodified lines | ||
| 158 | 197 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); |
| 159 | 198 | } |
| 160 | 199 | |
| 200 | +/* Find field descriptor by command line */ | |
| 201 | +static const struct field_desc *get_field_by_cmd_char(char ch) | |
| 202 | +{ | |
| 203 | + const struct field_desc *field; | |
| 204 | + | |
| 205 | + for (field = sort_fields; field->name != NULL; field++) { | |
| 206 | + if (field->cmd_char[0] == ch) | |
| 207 | + return field; | |
| 208 | + } | |
| 209 | + | |
| 210 | + return NULL; | |
| 211 | +} | |
| 212 | + | |
| 213 | +/* Find field descriptor by name with string comparison */ | |
| 214 | +static const struct field_desc *get_field_by_name(const char *name) | |
| 215 | +{ | |
| 216 | + const struct field_desc *field; | |
| 217 | + size_t field_len; | |
| 218 | + | |
| 219 | + for (field = sort_fields; field->name != NULL; field++) { | |
| 220 | + field_len = strlen(field->name); | |
| 221 | + if (field_len != strlen(name)) | |
| 222 | + continue; | |
| 223 | + if (strncmp(field->name, name, field_len) == 0) | |
| 224 | + return field; | |
| 225 | + } | |
| 226 | + | |
| 227 | + return NULL; | |
| 228 | +} | |
| 229 | + | |
| 230 | +/* Find display name for a field descriptor */ | |
| 231 | +static const char *get_name_by_field(const struct field_desc *field) | |
| 232 | +{ | |
| 233 | + return field ? field->name : "UNKNOWN"; | |
| 234 | +} | |
| 235 | + | |
| 236 | +/* Generate string of available field names */ | |
| 237 | +static void display_available_fields(size_t mode) | |
| 238 | +{ | |
| 239 | + const struct field_desc *field; | |
| 240 | + char buf[MAX_BUF_LEN]; | |
| 241 | + | |
| 242 | + buf[0] = '\0'; | |
| 243 | + | |
| 244 | + for (field = sort_fields; field->name != NULL; field++) { | |
| 245 | + if (!(field->supported_modes & mode)) | |
| 246 | + continue; | |
| 247 | + strncat(buf, "|", MAX_BUF_LEN - strlen(buf) - 1); | |
| 248 | + strncat(buf, field->name, MAX_BUF_LEN - strlen(buf) - 1); | |
| 249 | + buf[MAX_BUF_LEN - 1] = '\0'; | |
| 250 | + } | |
| 251 | + | |
| 252 | + fprintf(stderr, "Available fields: %s\n", buf); | |
| 253 | +} | |
| 254 | + | |
| 161 | 255 | /* Display usage information and command line options */ |
| 162 | 256 | static void usage(void) |
| 163 | 257 | { |
| 164 | 258 | printf("Usage: delaytop [Options]\n" |
| 165 | 259 | "Options:\n" |
| 166 | − " -h, --help Show this help message and exit\n" | |
| 167 | − " -d, --delay=SECONDS Set refresh interval (default: 2 seconds, min: 1)\n" | |
| 168 | − " -n, --iterations=COUNT Set number of updates (default: 0 = infinite)\n" | |
| 169 | − " -P, --processes=NUMBER Set maximum number of processes to show (default: 20, max: 1000)\n" | |
| 170 | − " -o, --once Display once and exit\n" | |
| 171 | − " -p, --pid=PID Monitor only the specified PID\n" | |
| 172 | − " -C, --container=PATH Monitor the container at specified cgroup path\n"); | |
| 260 | + " -h, --help Show this help message and exit\n" | |
| 261 | + " -d, --delay=SECONDS Set refresh interval (default: 2 seconds, min: 1)\n" | |
| 262 | + " -n, --iterations=COUNT Set number of updates (default: 0 = infinite)\n" | |
| 263 | + " -P, --processes=NUMBER Set maximum number of processes to show (default: 20, max: 1000)\n" | |
| 264 | + " -o, --once Display once and exit\n" | |
| 265 | + " -p, --pid=PID Monitor only the specified PID\n" | |
| 266 | + " -C, --container=PATH Monitor the container at specified cgroup path\n" | |
| 267 | + " -s, --sort=FIELD Sort by delay field (default: cpu)\n" | |
| 268 | + " -M, --memverbose Display memory detailed information\n"); | |
| 173 | 269 | exit(0); |
| 174 | 270 | } |
| 175 | 271 | |
| @@ -177,6 +273,7 @@ static void usage(void) · 1 unmodified line | ||
| 177 | 273 | static void parse_args(int argc, char **argv) |
| 178 | 274 | { |
| 179 | 275 | int c; |
| 276 | + const struct field_desc *field; | |
| 180 | 277 | struct option long_options[] = { |
| 181 | 278 | {"help", no_argument, 0, 'h'}, |
| 182 | 279 | {"delay", required_argument, 0, 'd'}, |
| @@ -184,7 +281,9 @@ static void parse_args(int argc, char **argv) · 1 unmodified line | ||
| 184 | 281 | {"pid", required_argument, 0, 'p'}, |
| 185 | 282 | {"once", no_argument, 0, 'o'}, |
| 186 | 283 | {"processes", required_argument, 0, 'P'}, |
| 284 | + {"sort", required_argument, 0, 's'}, | |
| 187 | 285 | {"container", required_argument, 0, 'C'}, |
| 286 | + {"memverbose", no_argument, 0, 'M'}, | |
| 188 | 287 | {0, 0, 0, 0} |
| 189 | 288 | }; |
| 190 | 289 | |
| @@ -192,15 +291,16 @@ static void parse_args(int argc, char **argv) · 1 unmodified line | ||
| 192 | 291 | cfg.delay = 2; |
| 193 | 292 | cfg.iterations = 0; |
| 194 | 293 | cfg.max_processes = 20; |
| 195 | − cfg.sort_field = 'c'; /* Default sort by CPU delay */ | |
| 294 | + cfg.sort_field = &sort_fields[0]; /* Default sorted by CPU delay */ | |
| 196 | 295 | cfg.output_one_time = 0; |
| 197 | 296 | cfg.monitor_pid = 0; /* 0 means monitor all PIDs */ |
| 198 | 297 | cfg.container_path = NULL; |
| 298 | + cfg.display_mode = MODE_DEFAULT; | |
| 199 | 299 | |
| 200 | 300 | while (1) { |
| 201 | 301 | int option_index = 0; |
| 202 | 302 | |
| 203 | − c = getopt_long(argc, argv, "hd:n:p:oP:C:", long_options, &option_index); | |
| 303 | + c = getopt_long(argc, argv, "hd:n:p:oP:C:s:M", long_options, &option_index); | |
| 204 | 304 | if (c == -1) |
| 205 | 305 | break; |
| 206 | 306 | |
| @@ -247,6 +347,26 @@ static void parse_args(int argc, char **argv) · 40 unmodified lines | ||
| 247 | 347 | case 'C': |
| 248 | 348 | cfg.container_path = strdup(optarg); |
| 249 | 349 | break; |
| 350 | + case 's': | |
| 351 | + if (strlen(optarg) == 0) { | |
| 352 | + fprintf(stderr, "Error: empty sort field\n"); | |
| 353 | + exit(1); | |
| 354 | + } | |
| 355 | + | |
| 356 | + field = get_field_by_name(optarg); | |
| 357 | + /* Show available fields if invalid option provided */ | |
| 358 | + if (!field) { | |
| 359 | + fprintf(stderr, "Error: invalid sort field '%s'\n", optarg); | |
| 360 | + display_available_fields(MODE_TYPE_ALL); | |
| 361 | + exit(1); | |
| 362 | + } | |
| 363 | + | |
| 364 | + cfg.sort_field = field; | |
| 365 | + break; | |
| 366 | + case 'M': | |
| 367 | + cfg.display_mode = MODE_MEMVERBOSE; | |
| 368 | + cfg.sort_field = get_field_by_name("mem"); | |
| 369 | + break; | |
| 250 | 370 | default: |
| 251 | 371 | fprintf(stderr, "Try 'delaytop --help' for more information.\n"); |
| 252 | 372 | exit(1); |
| @@ -254,6 +374,25 @@ static void parse_args(int argc, char **argv) · 1 unmodified line | ||
| 254 | 374 | } |
| 255 | 375 | } |
| 256 | 376 | |
| 377 | +/* Calculate average delay in milliseconds for overall memory */ | |
| 378 | +static void set_mem_delay_total(struct task_info *t) | |
| 379 | +{ | |
| 380 | + t->mem_delay_total = t->swapin_delay_total + | |
| 381 | + t->freepages_delay_total + | |
| 382 | + t->thrashing_delay_total + | |
| 383 | + t->compact_delay_total + | |
| 384 | + t->wpcopy_delay_total; | |
| 385 | +} | |
| 386 | + | |
| 387 | +static void set_mem_count(struct task_info *t) | |
| 388 | +{ | |
| 389 | + t->mem_count = t->swapin_count + | |
| 390 | + t->freepages_count + | |
| 391 | + t->thrashing_count + | |
| 392 | + t->compact_count + | |
| 393 | + t->wpcopy_count; | |
| 394 | +} | |
| 395 | + | |
| 257 | 396 | /* Create a raw netlink socket and bind */ |
| 258 | 397 | static int create_nl_socket(void) |
| 259 | 398 | { |
| @@ -358,87 +497,134 @@ static int get_family_id(int sd) · 98 unmodified lines | ||
| 358 | 497 | return id; |
| 359 | 498 | } |
| 360 | 499 | |
| 361 | −static void read_psi_stats(void) | |
| 500 | +static int read_psi_stats(void) | |
| 362 | 501 | { |
| 363 | 502 | FILE *fp; |
| 364 | 503 | char line[256]; |
| 365 | 504 | int ret = 0; |
| 505 | + int error_count = 0; | |
| 506 | + | |
| 507 | + /* Check if PSI path exists */ | |
| 508 | + if (access(PSI_PATH, F_OK) != 0) { | |
| 509 | + fprintf(stderr, "Error: PSI interface not found at %s\n", PSI_PATH); | |
| 510 | + fprintf(stderr, "Please ensure your kernel supports PSI (Pressure Stall Information)\n"); | |
| 511 | + return -1; | |
| 512 | + } | |
| 513 | + | |
| 366 | 514 | /* Zero all fields */ |
| 367 | 515 | memset(&psi, 0, sizeof(psi)); |
| 516 | + | |
| 368 | 517 | /* CPU pressure */ |
| 369 | − fp = fopen(PSI_CPU_SOME, "r"); | |
| 518 | + fp = fopen(PSI_CPU_PATH, "r"); | |
| 370 | 519 | if (fp) { |
| 371 | 520 | while (fgets(line, sizeof(line), fp)) { |
| 372 | 521 | if (strncmp(line, "some", 4) == 0) { |
| 373 | 522 | ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 374 | 523 | &psi.cpu_some_avg10, &psi.cpu_some_avg60, |
| 375 | 524 | &psi.cpu_some_avg300, &psi.cpu_some_total); |
| 376 | − if (ret != 4) | |
| 525 | + if (ret != 4) { | |
| 377 | 526 | fprintf(stderr, "Failed to parse CPU some PSI data\n"); |
| 527 | + error_count++; | |
| 528 | + } | |
| 378 | 529 | } else if (strncmp(line, "full", 4) == 0) { |
| 379 | 530 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 380 | 531 | &psi.cpu_full_avg10, &psi.cpu_full_avg60, |
| 381 | 532 | &psi.cpu_full_avg300, &psi.cpu_full_total); |
| 382 | − if (ret != 4) | |
| 533 | + if (ret != 4) { | |
| 383 | 534 | fprintf(stderr, "Failed to parse CPU full PSI data\n"); |
| 535 | + error_count++; | |
| 536 | + } | |
| 384 | 537 | } |
| 385 | 538 | } |
| 386 | 539 | fclose(fp); |
| 540 | + } else { | |
| 541 | + fprintf(stderr, "Warning: Failed to open %s\n", PSI_CPU_PATH); | |
| 542 | + error_count++; | |
| 387 | 543 | } |
| 544 | + | |
| 388 | 545 | /* Memory pressure */ |
| 389 | − fp = fopen(PSI_MEMORY_SOME, "r"); | |
| 546 | + fp = fopen(PSI_MEMORY_PATH, "r"); | |
| 390 | 547 | if (fp) { |
| 391 | 548 | while (fgets(line, sizeof(line), fp)) { |
| 392 | 549 | if (strncmp(line, "some", 4) == 0) { |
| 393 | 550 | ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 394 | 551 | &psi.memory_some_avg10, &psi.memory_some_avg60, |
| 395 | 552 | &psi.memory_some_avg300, &psi.memory_some_total); |
| 396 | − if (ret != 4) | |
| 553 | + if (ret != 4) { | |
| 397 | 554 | fprintf(stderr, "Failed to parse Memory some PSI data\n"); |
| 555 | + error_count++; | |
| 556 | + } | |
| 398 | 557 | } else if (strncmp(line, "full", 4) == 0) { |
| 399 | 558 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 400 | 559 | &psi.memory_full_avg10, &psi.memory_full_avg60, |
| 401 | 560 | &psi.memory_full_avg300, &psi.memory_full_total); |
| 402 | − } | |
| 403 | − if (ret != 4) | |
| 561 | + if (ret != 4) { | |
| 404 | 562 | fprintf(stderr, "Failed to parse Memory full PSI data\n"); |
| 563 | + error_count++; | |
| 564 | + } | |
| 565 | + } | |
| 405 | 566 | } |
| 406 | 567 | fclose(fp); |
| 568 | + } else { | |
| 569 | + fprintf(stderr, "Warning: Failed to open %s\n", PSI_MEMORY_PATH); | |
| 570 | + error_count++; | |
| 407 | 571 | } |
| 572 | + | |
| 408 | 573 | /* IO pressure */ |
| 409 | − fp = fopen(PSI_IO_SOME, "r"); | |
| 574 | + fp = fopen(PSI_IO_PATH, "r"); | |
| 410 | 575 | if (fp) { |
| 411 | 576 | while (fgets(line, sizeof(line), fp)) { |
| 412 | 577 | if (strncmp(line, "some", 4) == 0) { |
| 413 | 578 | ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 414 | 579 | &psi.io_some_avg10, &psi.io_some_avg60, |
| 415 | 580 | &psi.io_some_avg300, &psi.io_some_total); |
| 416 | − if (ret != 4) | |
| 581 | + if (ret != 4) { | |
| 417 | 582 | fprintf(stderr, "Failed to parse IO some PSI data\n"); |
| 583 | + error_count++; | |
| 584 | + } | |
| 418 | 585 | } else if (strncmp(line, "full", 4) == 0) { |
| 419 | 586 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 420 | 587 | &psi.io_full_avg10, &psi.io_full_avg60, |
| 421 | 588 | &psi.io_full_avg300, &psi.io_full_total); |
| 422 | − if (ret != 4) | |
| 589 | + if (ret != 4) { | |
| 423 | 590 | fprintf(stderr, "Failed to parse IO full PSI data\n"); |
| 591 | + error_count++; | |
| 592 | + } | |
| 424 | 593 | } |
| 425 | 594 | } |
| 426 | 595 | fclose(fp); |
| 596 | + } else { | |
| 597 | + fprintf(stderr, "Warning: Failed to open %s\n", PSI_IO_PATH); | |
| 598 | + error_count++; | |
| 427 | 599 | } |
| 600 | + | |
| 428 | 601 | /* IRQ pressure (only full) */ |
| 429 | − fp = fopen(PSI_IRQ_FULL, "r"); | |
| 602 | + fp = fopen(PSI_IRQ_PATH, "r"); | |
| 430 | 603 | if (fp) { |
| 431 | 604 | while (fgets(line, sizeof(line), fp)) { |
| 432 | 605 | if (strncmp(line, "full", 4) == 0) { |
| 433 | 606 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 434 | 607 | &psi.irq_full_avg10, &psi.irq_full_avg60, |
| 435 | 608 | &psi.irq_full_avg300, &psi.irq_full_total); |
| 436 | − if (ret != 4) | |
| 609 | + if (ret != 4) { | |
| 437 | 610 | fprintf(stderr, "Failed to parse IRQ full PSI data\n"); |
| 611 | + error_count++; | |
| 612 | + } | |
| 438 | 613 | } |
| 439 | 614 | } |
| 440 | 615 | fclose(fp); |
| 616 | + } else { | |
| 617 | + fprintf(stderr, "Warning: Failed to open %s\n", PSI_IRQ_PATH); | |
| 618 | + error_count++; | |
| 619 | + } | |
| 620 | + | |
| 621 | + /* Return error count: 0 means success, >0 means warnings, -1 means fatal error */ | |
| 622 | + if (error_count > 0) { | |
| 623 | + fprintf(stderr, "PSI stats reading completed with %d warnings\n", error_count); | |
| 624 | + return error_count; | |
| 441 | 625 | } |
| 626 | + | |
| 627 | + return 0; | |
| 442 | 628 | } |
| 443 | 629 | |
| 444 | 630 | static int read_comm(int pid, char *comm_buf, size_t buf_size) |
| @@ -527,6 +713,8 @@ static void fetch_and_fill_task_info(int pid, const char *comm) · 82 unmodified lines | ||
| 527 | 713 | SET_TASK_STAT(task_count, wpcopy_delay_total); |
| 528 | 714 | SET_TASK_STAT(task_count, irq_count); |
| 529 | 715 | SET_TASK_STAT(task_count, irq_delay_total); |
| 716 | + set_mem_count(&tasks[task_count]); | |
| 717 | + set_mem_delay_total(&tasks[task_count]); | |
| 530 | 718 | task_count++; |
| 531 | 719 | } |
| 532 | 720 | break; |
| @@ -587,19 +775,23 @@ static int compare_tasks(const void *a, const void *b) · 54 unmodified lines | ||
| 587 | 775 | { |
| 588 | 776 | const struct task_info *t1 = (const struct task_info *)a; |
| 589 | 777 | const struct task_info *t2 = (const struct task_info *)b; |
| 778 | + unsigned long long total1; | |
| 779 | + unsigned long long total2; | |
| 780 | + unsigned long count1; | |
| 781 | + unsigned long count2; | |
| 590 | 782 | double avg1, avg2; |
| 591 | 783 | |
| 592 | − switch (cfg.sort_field) { | |
| 593 | − case 'c': /* CPU */ | |
| 594 | − avg1 = average_ms(t1->cpu_delay_total, t1->cpu_count); | |
| 595 | − avg2 = average_ms(t2->cpu_delay_total, t2->cpu_count); | |
| 596 | − if (avg1 != avg2) | |
| 597 | − return avg2 > avg1 ? 1 : -1; | |
| 598 | − return t2->cpu_delay_total > t1->cpu_delay_total ? 1 : -1; | |
| 784 | + total1 = *(unsigned long long *)((char *)t1 + cfg.sort_field->total_offset); | |
| 785 | + total2 = *(unsigned long long *)((char *)t2 + cfg.sort_field->total_offset); | |
| 786 | + count1 = *(unsigned long *)((char *)t1 + cfg.sort_field->count_offset); | |
| 787 | + count2 = *(unsigned long *)((char *)t2 + cfg.sort_field->count_offset); | |
| 599 | 788 | |
| 600 | − default: | |
| 601 | − return t2->cpu_delay_total > t1->cpu_delay_total ? 1 : -1; | |
| 602 | − } | |
| 789 | + avg1 = average_ms(total1, count1); | |
| 790 | + avg2 = average_ms(total2, count2); | |
| 791 | + if (avg1 != avg2) | |
| 792 | + return avg2 > avg1 ? 1 : -1; | |
| 793 | + | |
| 794 | + return 0; | |
| 603 | 795 | } |
| 604 | 796 | |
| 605 | 797 | /* Sort tasks by selected field */ |
| @@ -673,7 +865,7 @@ static void get_container_stats(void) · 67 unmodified lines | ||
| 673 | 865 | } |
| 674 | 866 | |
| 675 | 867 | /* Display results to stdout or log file */ |
| 676 | −static void display_results(void) | |
| 868 | +static void display_results(int psi_ret) | |
| 677 | 869 | { |
| 678 | 870 | time_t now = time(NULL); |
| 679 | 871 | struct tm *tm_now = localtime(&now); |
| @@ -686,49 +878,53 @@ static void display_results(void) · 6 unmodified lines | ||
| 686 | 878 | suc &= BOOL_FPRINT(out, "\033[H\033[J"); |
| 687 | 879 | |
| 688 | 880 | /* PSI output (one-line, no cat style) */ |
| 689 | − suc &= BOOL_FPRINT(out, "System Pressure Information: (avg10/avg60/avg300/total)\n"); | |
| 690 | − suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 691 | − "CPU some:", | |
| 692 | − psi.cpu_some_avg10, | |
| 693 | − psi.cpu_some_avg60, | |
| 694 | − psi.cpu_some_avg300, | |
| 695 | − psi.cpu_some_total / 1000); | |
| 696 | − suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 697 | − "CPU full:", | |
| 698 | − psi.cpu_full_avg10, | |
| 699 | − psi.cpu_full_avg60, | |
| 700 | − psi.cpu_full_avg300, | |
| 701 | − psi.cpu_full_total / 1000); | |
| 702 | − suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 703 | − "Memory full:", | |
| 704 | − psi.memory_full_avg10, | |
| 705 | − psi.memory_full_avg60, | |
| 706 | − psi.memory_full_avg300, | |
| 707 | − psi.memory_full_total / 1000); | |
| 708 | − suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 709 | − "Memory some:", | |
| 710 | − psi.memory_some_avg10, | |
| 711 | − psi.memory_some_avg60, | |
| 712 | − psi.memory_some_avg300, | |
| 713 | − psi.memory_some_total / 1000); | |
| 714 | − suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 715 | − "IO full:", | |
| 716 | − psi.io_full_avg10, | |
| 717 | − psi.io_full_avg60, | |
| 718 | − psi.io_full_avg300, | |
| 719 | − psi.io_full_total / 1000); | |
| 720 | − suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 721 | − "IO some:", | |
| 722 | − psi.io_some_avg10, | |
| 723 | − psi.io_some_avg60, | |
| 724 | − psi.io_some_avg300, | |
| 725 | − psi.io_some_total / 1000); | |
| 726 | − suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 727 | − "IRQ full:", | |
| 728 | − psi.irq_full_avg10, | |
| 729 | − psi.irq_full_avg60, | |
| 730 | − psi.irq_full_avg300, | |
| 731 | − psi.irq_full_total / 1000); | |
| 881 | + suc &= BOOL_FPRINT(out, "System Pressure Information: (avg10/avg60vg300/total)\n"); | |
| 882 | + if (psi_ret) { | |
| 883 | + suc &= BOOL_FPRINT(out, " PSI not found: check if psi=1 enabled in cmdline\n"); | |
| 884 | + } else { | |
| 885 | + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 886 | + "CPU some:", | |
| 887 | + psi.cpu_some_avg10, | |
| 888 | + psi.cpu_some_avg60, | |
| 889 | + psi.cpu_some_avg300, | |
| 890 | + psi.cpu_some_total / 1000); | |
| 891 | + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 892 | + "CPU full:", | |
| 893 | + psi.cpu_full_avg10, | |
| 894 | + psi.cpu_full_avg60, | |
| 895 | + psi.cpu_full_avg300, | |
| 896 | + psi.cpu_full_total / 1000); | |
| 897 | + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 898 | + "Memory full:", | |
| 899 | + psi.memory_full_avg10, | |
| 900 | + psi.memory_full_avg60, | |
| 901 | + psi.memory_full_avg300, | |
| 902 | + psi.memory_full_total / 1000); | |
| 903 | + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 904 | + "Memory some:", | |
| 905 | + psi.memory_some_avg10, | |
| 906 | + psi.memory_some_avg60, | |
| 907 | + psi.memory_some_avg300, | |
| 908 | + psi.memory_some_total / 1000); | |
| 909 | + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 910 | + "IO full:", | |
| 911 | + psi.io_full_avg10, | |
| 912 | + psi.io_full_avg60, | |
| 913 | + psi.io_full_avg300, | |
| 914 | + psi.io_full_total / 1000); | |
| 915 | + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 916 | + "IO some:", | |
| 917 | + psi.io_some_avg10, | |
| 918 | + psi.io_some_avg60, | |
| 919 | + psi.io_some_avg300, | |
| 920 | + psi.io_some_total / 1000); | |
| 921 | + suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | |
| 922 | + "IRQ full:", | |
| 923 | + psi.irq_full_avg10, | |
| 924 | + psi.irq_full_avg60, | |
| 925 | + psi.irq_full_avg300, | |
| 926 | + psi.irq_full_total / 1000); | |
| 927 | + } | |
| 732 | 928 | |
| 733 | 929 | if (cfg.container_path) { |
| 734 | 930 | suc &= BOOL_FPRINT(out, "Container Information (%s):\n", cfg.container_path); |
| @@ -738,29 +934,59 @@ static void display_results(void) · 3 unmodified lines | ||
| 738 | 934 | container_stats.nr_stopped, container_stats.nr_uninterruptible, |
| 739 | 935 | container_stats.nr_io_wait); |
| 740 | 936 | } |
| 741 | − suc &= BOOL_FPRINT(out, "Top %d processes (sorted by CPU delay):\n", | |
| 742 | − cfg.max_processes); | |
| 743 | − suc &= BOOL_FPRINT(out, "%5s %5s %-17s", "PID", "TGID", "COMMAND"); | |
| 744 | − suc &= BOOL_FPRINT(out, "%7s %7s %7s %7s %7s %7s %7s %7s\n", | |
| 745 | − "CPU(ms)", "IO(ms)", "SWAP(ms)", "RCL(ms)", | |
| 746 | − "THR(ms)", "CMP(ms)", "WP(ms)", "IRQ(ms)"); | |
| 747 | 937 | |
| 748 | − suc &= BOOL_FPRINT(out, "-----------------------------------------------"); | |
| 749 | − suc &= BOOL_FPRINT(out, "----------------------------------------------\n"); | |
| 938 | + /* Interacive command */ | |
| 939 | + suc &= BOOL_FPRINT(out, "[o]sort [M]memverbose [q]quit\n"); | |
| 940 | + if (sort_selected) { | |
| 941 | + if (cfg.display_mode == MODE_MEMVERBOSE) | |
| 942 | + suc &= BOOL_FPRINT(out, | |
| 943 | + "sort selection: [m]MEM [r]RCL [t]THR [p]CMP [w]WP\n"); | |
| 944 | + else | |
| 945 | + suc &= BOOL_FPRINT(out, | |
| 946 | + "sort selection: [c]CPU [i]IO [m]MEM [q]IRQ\n"); | |
| 947 | + } | |
| 948 | + | |
| 949 | + /* Task delay output */ | |
| 950 | + suc &= BOOL_FPRINT(out, "Top %d processes (sorted by %s delay):\n", | |
| 951 | + cfg.max_processes, get_name_by_field(cfg.sort_field)); | |
| 952 | + | |
| 953 | + suc &= BOOL_FPRINT(out, "%8s %8s %-17s", "PID", "TGID", "COMMAND"); | |
| 954 | + if (cfg.display_mode == MODE_MEMVERBOSE) { | |
| 955 | + suc &= BOOL_FPRINT(out, "%8s %8s %8s %8s %8s %8s\n", | |
| 956 | + "MEM(ms)", "SWAP(ms)", "RCL(ms)", | |
| 957 | + "THR(ms)", "CMP(ms)", "WP(ms)"); | |
| 958 | + suc &= BOOL_FPRINT(out, "-----------------------"); | |
| 959 | + suc &= BOOL_FPRINT(out, "-----------------------"); | |
| 960 | + suc &= BOOL_FPRINT(out, "-----------------------"); | |
| 961 | + suc &= BOOL_FPRINT(out, "---------------------\n"); | |
| 962 | + } else { | |
| 963 | + suc &= BOOL_FPRINT(out, "%8s %8s %8s %8s\n", | |
| 964 | + "CPU(ms)", "IO(ms)", "IRQ(ms)", "MEM(ms)"); | |
| 965 | + suc &= BOOL_FPRINT(out, "-----------------------"); | |
| 966 | + suc &= BOOL_FPRINT(out, "-----------------------"); | |
| 967 | + suc &= BOOL_FPRINT(out, "--------------------------\n"); | |
| 968 | + } | |
| 969 | + | |
| 750 | 970 | count = task_count < cfg.max_processes ? task_count : cfg.max_processes; |
| 751 | 971 | |
| 752 | 972 | for (i = 0; i < count; i++) { |
| 753 | − suc &= BOOL_FPRINT(out, "%5d %5d %-15s", | |
| 973 | + suc &= BOOL_FPRINT(out, "%8d %8d %-15s", | |
| 754 | 974 | tasks[i].pid, tasks[i].tgid, tasks[i].command); |
| 755 | − suc &= BOOL_FPRINT(out, "%7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", | |
| 756 | − average_ms(tasks[i].cpu_delay_total, tasks[i].cpu_count), | |
| 757 | − average_ms(tasks[i].blkio_delay_total, tasks[i].blkio_count), | |
| 758 | − average_ms(tasks[i].swapin_delay_total, tasks[i].swapin_count), | |
| 759 | − average_ms(tasks[i].freepages_delay_total, tasks[i].freepages_count), | |
| 760 | − average_ms(tasks[i].thrashing_delay_total, tasks[i].thrashing_count), | |
| 761 | − average_ms(tasks[i].compact_delay_total, tasks[i].compact_count), | |
| 762 | − average_ms(tasks[i].wpcopy_delay_total, tasks[i].wpcopy_count), | |
| 763 | − average_ms(tasks[i].irq_delay_total, tasks[i].irq_count)); | |
| 975 | + if (cfg.display_mode == MODE_MEMVERBOSE) { | |
| 976 | + suc &= BOOL_FPRINT(out, DELAY_FMT_MEMVERBOSE, | |
| 977 | + TASK_AVG(tasks[i], mem), | |
| 978 | + TASK_AVG(tasks[i], swapin), | |
| 979 | + TASK_AVG(tasks[i], freepages), | |
| 980 | + TASK_AVG(tasks[i], thrashing), | |
| 981 | + TASK_AVG(tasks[i], compact), | |
| 982 | + TASK_AVG(tasks[i], wpcopy)); | |
| 983 | + } else { | |
| 984 | + suc &= BOOL_FPRINT(out, DELAY_FMT_DEFAULT, | |
| 985 | + TASK_AVG(tasks[i], cpu), | |
| 986 | + TASK_AVG(tasks[i], blkio), | |
| 987 | + TASK_AVG(tasks[i], irq), | |
| 988 | + TASK_AVG(tasks[i], mem)); | |
| 989 | + } | |
| 764 | 990 | } |
| 765 | 991 | |
| 766 | 992 | suc &= BOOL_FPRINT(out, "\n"); |
| @@ -769,11 +995,79 @@ static void display_results(void) · 2 unmodified lines | ||
| 769 | 995 | perror("Error writing to output"); |
| 770 | 996 | } |
| 771 | 997 | |
| 998 | +/* Check for keyboard input with timeout based on cfg.delay */ | |
| 999 | +static char check_for_keypress(void) | |
| 1000 | +{ | |
| 1001 | + struct timeval tv = {cfg.delay, 0}; | |
| 1002 | + fd_set readfds; | |
| 1003 | + char ch = 0; | |
| 1004 | + | |
| 1005 | + FD_ZERO(&readfds); | |
| 1006 | + FD_SET(STDIN_FILENO, &readfds); | |
| 1007 | + int r = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv); | |
| 1008 | + | |
| 1009 | + if (r > 0 && FD_ISSET(STDIN_FILENO, &readfds)) { | |
| 1010 | + read(STDIN_FILENO, &ch, 1); | |
| 1011 | + return ch; | |
| 1012 | + } | |
| 1013 | + | |
| 1014 | + return 0; | |
| 1015 | +} | |
| 1016 | + | |
| 1017 | +#define MAX_MODE_SIZE 2 | |
| 1018 | +static void toggle_display_mode(void) | |
| 1019 | +{ | |
| 1020 | + static const size_t modes[MAX_MODE_SIZE] = {MODE_DEFAULT, MODE_MEMVERBOSE}; | |
| 1021 | + static size_t cur_index; | |
| 1022 | + | |
| 1023 | + cur_index = (cur_index + 1) % MAX_MODE_SIZE; | |
| 1024 | + cfg.display_mode = modes[cur_index]; | |
| 1025 | +} | |
| 1026 | + | |
| 1027 | +/* Handle keyboard input: sorting selection, mode toggle, or quit */ | |
| 1028 | +static void handle_keypress(char ch, int *running) | |
| 1029 | +{ | |
| 1030 | + const struct field_desc *field; | |
| 1031 | + | |
| 1032 | + /* Change sort field */ | |
| 1033 | + if (sort_selected) { | |
| 1034 | + field = get_field_by_cmd_char(ch); | |
| 1035 | + if (field && (field->supported_modes & cfg.display_mode)) | |
| 1036 | + cfg.sort_field = field; | |
| 1037 | + | |
| 1038 | + sort_selected = 0; | |
| 1039 | + /* Handle mode changes or quit */ | |
| 1040 | + } else { | |
| 1041 | + switch (ch) { | |
| 1042 | + case 'o': | |
| 1043 | + sort_selected = 1; | |
| 1044 | + break; | |
| 1045 | + case 'M': | |
| 1046 | + toggle_display_mode(); | |
| 1047 | + for (field = sort_fields; field->name != NULL; field++) { | |
| 1048 | + if (field->supported_modes & cfg.display_mode) { | |
| 1049 | + cfg.sort_field = field; | |
| 1050 | + break; | |
| 1051 | + } | |
| 1052 | + } | |
| 1053 | + break; | |
| 1054 | + case 'q': | |
| 1055 | + case 'Q': | |
| 1056 | + *running = 0; | |
| 1057 | + break; | |
| 1058 | + default: | |
| 1059 | + break; | |
| 1060 | + } | |
| 1061 | + } | |
| 1062 | +} | |
| 1063 | + | |
| 772 | 1064 | /* Main function */ |
| 773 | 1065 | int main(int argc, char **argv) |
| 774 | 1066 | { |
| 1067 | + const struct field_desc *field; | |
| 775 | 1068 | int iterations = 0; |
| 776 | − int use_q_quit = 0; | |
| 1069 | + int psi_ret = 0; | |
| 1070 | + char keypress; | |
| 777 | 1071 | |
| 778 | 1072 | /* Parse command line arguments */ |
| 779 | 1073 | parse_args(argc, argv); |
| @@ -793,17 +1087,24 @@ int main(int argc, char **argv) · 13 unmodified lines | ||
| 793 | 1087 | exit(1); |
| 794 | 1088 | } |
| 795 | 1089 | |
| 796 | − if (!cfg.output_one_time) { | |
| 797 | − use_q_quit = 1; | |
| 798 | − enable_raw_mode(); | |
| 799 | − printf("Press 'q' to quit.\n"); | |
| 800 | − fflush(stdout); | |
| 801 | − } | |
| 1090 | + /* Set terminal to non-canonical mode for interaction */ | |
| 1091 | + enable_raw_mode(); | |
| 802 | 1092 | |
| 803 | 1093 | /* Main loop */ |
| 804 | 1094 | while (running) { |
| 1095 | + /* Auto-switch sort field when not matching display mode */ | |
| 1096 | + if (!(cfg.sort_field->supported_modes & cfg.display_mode)) { | |
| 1097 | + for (field = sort_fields; field->name != NULL; field++) { | |
| 1098 | + if (field->supported_modes & cfg.display_mode) { | |
| 1099 | + cfg.sort_field = field; | |
| 1100 | + printf("Auto-switched sort field to: %s\n", field->name); | |
| 1101 | + break; | |
| 1102 | + } | |
| 1103 | + } | |
| 1104 | + } | |
| 1105 | + | |
| 805 | 1106 | /* Read PSI statistics */ |
| 806 | − read_psi_stats(); | |
| 1107 | + psi_ret = read_psi_stats(); | |
| 807 | 1108 | |
| 808 | 1109 | /* Get container stats if container path provided */ |
| 809 | 1110 | if (cfg.container_path) |
| @@ -816,7 +1117,7 @@ int main(int argc, char **argv) · 6 unmodified lines | ||
| 816 | 1117 | sort_tasks(); |
| 817 | 1118 | |
| 818 | 1119 | /* Display results to stdout or log file */ |
| 819 | − display_results(); | |
| 1120 | + display_results(psi_ret); | |
| 820 | 1121 | |
| 821 | 1122 | /* Check for iterations */ |
| 822 | 1123 | if (cfg.iterations > 0 && ++iterations >= cfg.iterations) |
| @@ -826,32 +1127,14 @@ int main(int argc, char **argv) · 3 unmodified lines | ||
| 826 | 1127 | if (cfg.output_one_time) |
| 827 | 1128 | break; |
| 828 | 1129 | |
| 829 | − /* Check for 'q' key to quit */ | |
| 830 | − if (use_q_quit) { | |
| 831 | − struct timeval tv = {cfg.delay, 0}; | |
| 832 | − fd_set readfds; | |
| 833 | − | |
| 834 | − FD_ZERO(&readfds); | |
| 835 | − FD_SET(STDIN_FILENO, &readfds); | |
| 836 | − int r = select(STDIN_FILENO+1, &readfds, NULL, NULL, &tv); | |
| 837 | − | |
| 838 | − if (r > 0 && FD_ISSET(STDIN_FILENO, &readfds)) { | |
| 839 | − char ch = 0; | |
| 840 | − | |
| 841 | − read(STDIN_FILENO, &ch, 1); | |
| 842 | − if (ch == 'q' || ch == 'Q') { | |
| 843 | − running = 0; | |
| 844 | − break; | |
| 845 | − } | |
| 846 | − } | |
| 847 | − } else { | |
| 848 | − sleep(cfg.delay); | |
| 849 | − } | |
| 1130 | + /* Keypress for interactive usage */ | |
| 1131 | + keypress = check_for_keypress(); | |
| 1132 | + if (keypress) | |
| 1133 | + handle_keypress(keypress, &running); | |
| 850 | 1134 | } |
| 851 | 1135 | |
| 852 | 1136 | /* Restore terminal mode */ |
| 853 | − if (use_q_quit) | |
| 854 | − disable_raw_mode(); | |
| 1137 | + disable_raw_mode(); | |
| 855 | 1138 | |
| 856 | 1139 | /* Cleanup */ |
| 857 | 1140 | close(nl_sd); |
| @@ -499,19 +499,17 @@ void ida_check_random(void) · 498 unmodified lines | ||
| 499 | 499 | goto repeat; |
| 500 | 500 | } |
| 501 | 501 | |
| 502 | −void ida_simple_get_remove_test(void) | |
| 502 | +void ida_alloc_free_test(void) | |
| 503 | 503 | { |
| 504 | 504 | DEFINE_IDA(ida); |
| 505 | 505 | unsigned long i; |
| 506 | 506 | |
| 507 | − for (i = 0; i < 10000; i++) { | |
| 508 | − assert(ida_simple_get(&ida, 0, 20000, GFP_KERNEL) == i); | |
| 509 | − } | |
| 510 | − assert(ida_simple_get(&ida, 5, 30, GFP_KERNEL) < 0); | |
| 507 | + for (i = 0; i < 10000; i++) | |
| 508 | + assert(ida_alloc_max(&ida, 20000, GFP_KERNEL) == i); | |
| 509 | + assert(ida_alloc_range(&ida, 5, 30, GFP_KERNEL) < 0); | |
| 511 | 510 | |
| 512 | − for (i = 0; i < 10000; i++) { | |
| 513 | − ida_simple_remove(&ida, i); | |
| 514 | − } | |
| 511 | + for (i = 0; i < 10000; i++) | |
| 512 | + ida_free(&ida, i); | |
| 515 | 513 | assert(ida_is_empty(&ida)); |
| 516 | 514 | |
| 517 | 515 | ida_destroy(&ida); |
| @@ -524,7 +522,7 @@ void user_ida_checks(void) · 6 unmodified lines | ||
| 524 | 522 | ida_check_nomem(); |
| 525 | 523 | ida_check_conv_user(); |
| 526 | 524 | ida_check_random(); |
| 527 | − ida_simple_get_remove_test(); | |
| 525 | + ida_alloc_free_test(); | |
| 528 | 526 | |
| 529 | 527 | radix_tree_cpu_dead(1); |
| 530 | 528 | } |
| @@ -7,6 +7,7 @@ · 6 unmodified lines | ||
| 7 | 7 | /proc-loadavg-001 |
| 8 | 8 | /proc-maps-race |
| 9 | 9 | /proc-multiple-procfs |
| 10 | +/proc-net-dev-lseek | |
| 10 | 11 | /proc-empty-vm |
| 11 | 12 | /proc-pid-vm |
| 12 | 13 | /proc-self-map-files-001 |
| @@ -10,6 +10,7 @@ TEST_GEN_PROGS += fd-003-kthread · 9 unmodified lines | ||
| 10 | 10 | TEST_GEN_PROGS += proc-2-is-kthread |
| 11 | 11 | TEST_GEN_PROGS += proc-loadavg-001 |
| 12 | 12 | TEST_GEN_PROGS += proc-maps-race |
| 13 | +TEST_GEN_PROGS += proc-net-dev-lseek | |
| 13 | 14 | TEST_GEN_PROGS += proc-empty-vm |
| 14 | 15 | TEST_GEN_PROGS += proc-pid-vm |
| 15 | 16 | TEST_GEN_PROGS += proc-self-map-files-001 |
| @@ -0,0 +1,68 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2025 Alexey Dobriyan <adobriyan@gmail.com> | |
| 3 | + * | |
| 4 | + * Permission to use, copy, modify, and distribute this software for any | |
| 5 | + * purpose with or without fee is hereby granted, provided that the above | |
| 6 | + * copyright notice and this permission notice appear in all copies. | |
| 7 | + * | |
| 8 | + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| 9 | + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| 10 | + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
| 11 | + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| 12 | + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
| 13 | + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
| 14 | + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
| 15 | + */ | |
| 16 | +#undef _GNU_SOURCE | |
| 17 | +#define _GNU_SOURCE | |
| 18 | +#undef NDEBUG | |
| 19 | +#include <assert.h> | |
| 20 | +#include <errno.h> | |
| 21 | +#include <fcntl.h> | |
| 22 | +#include <string.h> | |
| 23 | +#include <unistd.h> | |
| 24 | +#include <sched.h> | |
| 25 | +/* | |
| 26 | + * Test that lseek("/proc/net/dev/", 0, SEEK_SET) | |
| 27 | + * a) works, | |
| 28 | + * b) does what you think it does. | |
| 29 | + */ | |
| 30 | +int main(void) | |
| 31 | +{ | |
| 32 | + /* /proc/net/dev output is deterministic in fresh netns only. */ | |
| 33 | + if (unshare(CLONE_NEWNET) == -1) { | |
| 34 | + if (errno == ENOSYS || errno == EPERM) { | |
| 35 | + return 4; | |
| 36 | + } | |
| 37 | + return 1; | |
| 38 | + } | |
| 39 | + | |
| 40 | + const int fd = open("/proc/net/dev", O_RDONLY); | |
| 41 | + assert(fd >= 0); | |
| 42 | + | |
| 43 | + char buf1[4096]; | |
| 44 | + const ssize_t rv1 = read(fd, buf1, sizeof(buf1)); | |
| 45 | + /* | |
| 46 | + * Not "<=", this file can't be empty: | |
| 47 | + * there is header, "lo" interface with some zeroes. | |
| 48 | + */ | |
| 49 | + assert(0 < rv1); | |
| 50 | + assert(rv1 <= sizeof(buf1)); | |
| 51 | + | |
| 52 | + /* Believe it or not, this line broke one day. */ | |
| 53 | + assert(lseek(fd, 0, SEEK_SET) == 0); | |
| 54 | + | |
| 55 | + char buf2[4096]; | |
| 56 | + const ssize_t rv2 = read(fd, buf2, sizeof(buf2)); | |
| 57 | + /* Not "<=", see above. */ | |
| 58 | + assert(0 < rv2); | |
| 59 | + assert(rv2 <= sizeof(buf2)); | |
| 60 | + | |
| 61 | + /* Test that lseek rewinds to the beginning of the file. */ | |
| 62 | + assert(rv1 == rv2); | |
| 63 | + assert(memcmp(buf1, buf2, rv1) == 0); | |
| 64 | + | |
| 65 | + /* Contents of the file is not validated: this test is about lseek(). */ | |
| 66 | + | |
| 67 | + return 0; | |
| 68 | +} | |
| @@ -47,6 +47,10 @@ · 46 unmodified lines | ||
| 47 | 47 | #include <sys/resource.h> |
| 48 | 48 | #include <linux/fs.h> |
| 49 | 49 | |
| 50 | +#ifndef __maybe_unused | |
| 51 | +#define __maybe_unused __attribute__((__unused__)) | |
| 52 | +#endif | |
| 53 | + | |
| 50 | 54 | #include "../kselftest.h" |
| 51 | 55 | |
| 52 | 56 | static inline long sys_execveat(int dirfd, const char *pathname, char **argv, char **envp, int flags) |
| @@ -218,12 +222,12 @@ static int make_exe(const uint8_t *payload, size_t len) · 165 unmodified lines | ||
| 218 | 222 | * 2: vsyscall VMA is r-xp vsyscall=emulate |
| 219 | 223 | */ |
| 220 | 224 | static volatile int g_vsyscall; |
| 221 | −static const char *str_vsyscall; | |
| 225 | +static const char *str_vsyscall __maybe_unused; | |
| 222 | 226 | |
| 223 | −static const char str_vsyscall_0[] = ""; | |
| 224 | −static const char str_vsyscall_1[] = | |
| 227 | +static const char str_vsyscall_0[] __maybe_unused = ""; | |
| 228 | +static const char str_vsyscall_1[] __maybe_unused = | |
| 225 | 229 | "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n"; |
| 226 | −static const char str_vsyscall_2[] = | |
| 230 | +static const char str_vsyscall_2[] __maybe_unused = | |
| 227 | 231 | "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n"; |
| 228 | 232 | |
| 229 | 233 | #ifdef __x86_64__ |