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 | Shuah Khan <shuah@kernel.org> <shuah.khan@hp.com> | 721 | Shuah Khan <shuah@kernel.org> <shuah.khan@hp.com> |
| 722 | Shuah Khan <shuah@kernel.org> <shuahkh@osg.samsung.com> | 722 | Shuah Khan <shuah@kernel.org> <shuahkh@osg.samsung.com> |
| 723 | Shuah Khan <shuah@kernel.org> <shuah.kh@samsung.com> | 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 | Sid Manning <quic_sidneym@quicinc.com> <sidneym@codeaurora.org> | 726 | Sid Manning <quic_sidneym@quicinc.com> <sidneym@codeaurora.org> |
| 726 | Simon Arlott <simon@octiron.net> <simon@fire.lp0.eu> | 727 | Simon Arlott <simon@octiron.net> <simon@fire.lp0.eu> |
| 727 | Simona Vetter <simona.vetter@ffwll.ch> <daniel.vetter@ffwll.ch> | 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 | After the system starts, use `delaytop` to get the system-wide delay information, | 135 | After the system starts, use `delaytop` to get the system-wide delay information, |
| 136 | which includes system-wide PSI information and Top-N high-latency tasks. | 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 | `delaytop` is an interactive tool for monitoring system pressure and task delays. |
| 139 | displays the top 20 high-latency tasks by default, and refreshes the latency | 140 | It supports multiple sorting options, display modes, and real-time keyboard controls. |
| 140 | data every 2 seconds by default. | ||
| 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 | bash# ./delaytop | 144 | bash# ./delaytop |
| 145 | System Pressure Information: (avg10/avg60/avg300/total) | 145 | System Pressure Information: (avg10/avg60vg300/total) |
| 146 | CPU some: 0.0%/ 0.0%/ 0.0%/ 345(ms) | 146 | CPU some: 0.0%/ 0.0%/ 0.0%/ 106137(ms) |
| 147 | CPU full: 0.0%/ 0.0%/ 0.0%/ 0(ms) | 147 | CPU full: 0.0%/ 0.0%/ 0.0%/ 0(ms) |
| 148 | Memory full: 0.0%/ 0.0%/ 0.0%/ 0(ms) | 148 | Memory full: 0.0%/ 0.0%/ 0.0%/ 0(ms) |
| 149 | Memory some: 0.0%/ 0.0%/ 0.0%/ 0(ms) | 149 | Memory some: 0.0%/ 0.0%/ 0.0%/ 0(ms) |
| 150 | IO full: 0.0%/ 0.0%/ 0.0%/ 65(ms) | 150 | IO full: 0.0%/ 0.0%/ 0.0%/ 2240(ms) |
| 151 | IO some: 0.0%/ 0.0%/ 0.0%/ 79(ms) | 151 | IO some: 0.0%/ 0.0%/ 0.0%/ 2783(ms) |
| 152 | IRQ full: 0.0%/ 0.0%/ 0.0%/ 0(ms) | 152 | IRQ full: 0.0%/ 0.0%/ 0.0%/ 0(ms) |
| 153 | Top 20 processes (sorted by CPU delay): | 153 | [o]sort [M]memverbose [q]quit |
| 154 | PID TGID COMMAND CPU(ms) IO(ms) SWAP(ms) RCL(ms) THR(ms) CMP(ms) WP(ms) IRQ(ms) | 154 | Top 20 processes (sorted by cpu delay): |
| 155 | ---------------------------------------------------------------------------------------------- | 155 | PID TGID COMMAND CPU(ms) IO(ms) IRQ(ms) MEM(ms) |
| 156 | 161 161 zombie_memcg_re 1.40 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | 156 | ------------------------------------------------------------------------ |
| 157 | 130 130 blkcg_punt_bio 1.37 0.00 0.00 0.00 0.00 0.00 0.00 0.00 | 157 | 110 110 kworker/15:0H-s 27.91 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 | 158 | 57 57 cpuhp/7 3.18 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 | 159 | 99 99 cpuhp/14 2.97 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 | 160 | 51 51 cpuhp/6 0.90 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 | 161 | 44 44 kworker/4:0H-sy 0.80 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 | 162 | 60 60 ksoftirqd/7 0.74 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 | 163 | 76 76 idle_inject/10 0.31 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 | 164 | 100 100 idle_inject/14 0.30 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 | 165 | 1309 1309 systemsettings 0.29 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 | 166 | 45 45 cpuhp/5 0.22 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 | 167 | 63 63 cpuhp/8 0.20 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 | 168 | 87 87 cpuhp/12 0.18 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 | 169 | 93 93 cpuhp/13 0.17 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 | 170 | 1265 1265 acpid 0.17 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 | 171 | 1552 1552 sshd 0.17 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 | 172 | 2584 2584 sddm-helper 0.16 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 | 173 | 1284 1284 rtkit-daemon 0.15 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 | 174 | 1326 1326 nde-netfilter 0.14 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 | 175 | 27 27 cpuhp/2 0.13 0.00 0.00 0.00 |
| 176 | 176 | 631 631 kworker/11:2-rc 0.11 0.00 0.00 0.00 | |
| 177 | Dynamic interactive interface of delaytop:: | 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 | # ./delaytop -p pid | 204 | # ./delaytop -p pid |
| 180 | Print delayacct stats | 205 | Print delayacct stats |
| @@ -4603,7 +4603,7 @@ · 4602 unmodified lines | |||
| 4603 | bit 2: print timer info | 4603 | bit 2: print timer info |
| 4604 | bit 3: print locks info if CONFIG_LOCKDEP is on | 4604 | bit 3: print locks info if CONFIG_LOCKDEP is on |
| 4605 | bit 4: print ftrace buffer | 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 | bit 6: print all CPUs backtrace (if available in the arch) | 4607 | bit 6: print all CPUs backtrace (if available in the arch) |
| 4608 | bit 7: print only tasks in uninterruptible (blocked) state | 4608 | bit 7: print only tasks in uninterruptible (blocked) state |
| 4609 | *Be aware* that this option may print a _lot_ of lines, | 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 | bit 2 print timer info | 890 | bit 2 print timer info |
| 891 | bit 3 print locks info if ``CONFIG_LOCKDEP`` is on | 891 | bit 3 print locks info if ``CONFIG_LOCKDEP`` is on |
| 892 | bit 4 print ftrace buffer | 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 | bit 6 print all CPUs backtrace (if available in the arch) | 894 | bit 6 print all CPUs backtrace (if available in the arch) |
| 895 | bit 7 print only tasks in uninterruptible (blocked) state | 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 | sleep(2); | 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 | for (i = 0; i < n; i++) | 370 | for (i = 0; i < n; i++) |
| 366 | printf("0x%lx\n", cover[i + 1]); | 371 | printf("0x%lx\n", cover[i + 1]); |
| 367 | if (ioctl(fd, KCOV_DISABLE, 0)) | 372 | if (ioctl(fd, KCOV_DISABLE, 0)) |
| @@ -20987,7 +20987,7 @@ F: Documentation/devicetree/bindings/power/avs/qcom,cpr.yaml · 20986 unmodified lines | |||
| 20987 | F: drivers/pmdomain/qcom/cpr.c | 20987 | F: drivers/pmdomain/qcom/cpr.c |
| 20988 | 20988 | ||
| 20989 | QUALCOMM CPUCP MAILBOX DRIVER | 20989 | QUALCOMM CPUCP MAILBOX DRIVER |
| 20990 | M: Sibi Sankar <quic_sibis@quicinc.com> | 20990 | M: Sibi Sankar <sibi.sankar@oss.qualcomm.com> |
| 20991 | L: linux-arm-msm@vger.kernel.org | 20991 | L: linux-arm-msm@vger.kernel.org |
| 20992 | S: Supported | 20992 | S: Supported |
| 20993 | F: Documentation/devicetree/bindings/mailbox/qcom,cpucp-mbox.yaml | 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 | * Exclusion of crash region, crashk_low_res and/or crashk_cma_ranges | 166 | * Exclusion of crash region, crashk_low_res and/or crashk_cma_ranges |
| 167 | * may cause range splits. So add extra slots here. | 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 | cmem = vzalloc(struct_size(cmem, ranges, nr_ranges)); | 180 | cmem = vzalloc(struct_size(cmem, ranges, nr_ranges)); |
| 171 | if (!cmem) | 181 | if (!cmem) |
| 172 | return NULL; | 182 | return NULL; |
| 173 | 183 | ||
| 174 | cmem->max_nr_ranges = nr_ranges; | 184 | cmem->max_nr_ranges = nr_ranges; |
| 175 | cmem->nr_ranges = 0; | ||
| 176 | 185 | ||
| 177 | return cmem; | 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 | struct crash_mem *cmem; | 332 | struct crash_mem *cmem; |
| 324 | 333 | ||
| 325 | /* | 334 | /* |
| 326 | * Using random kexec_buf for passing dm crypt keys may cause a range | 335 | * In the current x86 architecture code, the elfheader is always |
| 327 | * split. So use two slots here. | 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 | cmem = vzalloc(struct_size(cmem, ranges, nr_ranges)); | 344 | cmem = vzalloc(struct_size(cmem, ranges, nr_ranges)); |
| 331 | if (!cmem) | 345 | if (!cmem) |
| 332 | return -ENOMEM; | 346 | return -ENOMEM; |
| 333 | 347 | ||
| 334 | cmem->max_nr_ranges = nr_ranges; | 348 | cmem->max_nr_ranges = nr_ranges; |
| 335 | cmem->nr_ranges = 0; | ||
| 336 | 349 | ||
| 337 | memset(&cmd, 0, sizeof(struct crash_memmap_data)); | 350 | memset(&cmd, 0, sizeof(struct crash_memmap_data)); |
| 338 | cmd.params = params; | 351 | cmd.params = params; |
| @@ -16,6 +16,8 @@ · 15 unmodified lines | |||
| 16 | #include <linux/kexec.h> | 16 | #include <linux/kexec.h> |
| 17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
| 18 | #include <linux/mm.h> | 18 | #include <linux/mm.h> |
| 19 | #include <linux/libfdt.h> | ||
| 20 | #include <linux/of_fdt.h> | ||
| 19 | #include <linux/efi.h> | 21 | #include <linux/efi.h> |
| 20 | #include <linux/random.h> | 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 | #endif /* CONFIG_EFI */ | 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 | static void | 239 | static void |
| 216 | setup_ima_state(const struct kimage *image, struct boot_params *params, | 240 | setup_ima_state(const struct kimage *image, struct boot_params *params, |
| 217 | unsigned long params_load_addr, | 241 | unsigned long params_load_addr, |
| @@ -336,6 +360,17 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params, · 118 unmodified lines | |||
| 336 | sizeof(struct efi_setup_data); | 360 | sizeof(struct efi_setup_data); |
| 337 | #endif | 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 | if (IS_ENABLED(CONFIG_IMA_KEXEC)) { | 374 | if (IS_ENABLED(CONFIG_IMA_KEXEC)) { |
| 340 | /* Setup IMA log buffer state */ | 375 | /* Setup IMA log buffer state */ |
| 341 | setup_ima_state(image, params, params_load_addr, | 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 | sizeof(struct setup_data) + | 564 | sizeof(struct setup_data) + |
| 530 | RNG_SEED_LENGTH; | 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 | if (IS_ENABLED(CONFIG_IMA_KEXEC)) | 573 | if (IS_ENABLED(CONFIG_IMA_KEXEC)) |
| 533 | kbuf.bufsz += sizeof(struct setup_data) + | 574 | kbuf.bufsz += sizeof(struct setup_data) + |
| 534 | sizeof(struct ima_setup_data); | 575 | sizeof(struct ima_setup_data); |
| @@ -537,7 +578,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel, · 2 unmodified lines | |||
| 537 | kbuf.bufsz += sizeof(struct setup_data) + | 578 | kbuf.bufsz += sizeof(struct setup_data) + |
| 538 | sizeof(struct kho_data); | 579 | sizeof(struct kho_data); |
| 539 | 580 | ||
| 540 | params = kzalloc(kbuf.bufsz, GFP_KERNEL); | 581 | params = kvzalloc(kbuf.bufsz, GFP_KERNEL); |
| 541 | if (!params) | 582 | if (!params) |
| 542 | return ERR_PTR(-ENOMEM); | 583 | return ERR_PTR(-ENOMEM); |
| 543 | efi_map_offset = params_cmdline_sz; | 584 | efi_map_offset = params_cmdline_sz; |
| @@ -647,7 +688,7 @@ static void *bzImage64_load(struct kimage *image, char *kernel, · 103 unmodified lines | |||
| 647 | return ldata; | 688 | return ldata; |
| 648 | 689 | ||
| 649 | out_free_params: | 690 | out_free_params: |
| 650 | kfree(params); | 691 | kvfree(params); |
| 651 | return ERR_PTR(ret); | 692 | return ERR_PTR(ret); |
| 652 | } | 693 | } |
| 653 | 694 | ||
| @@ -659,7 +700,7 @@ static int bzImage64_cleanup(void *loader_data) · 5 unmodified lines | |||
| 659 | if (!ldata) | 700 | if (!ldata) |
| 660 | return 0; | 701 | return 0; |
| 661 | 702 | ||
| 662 | kfree(ldata->bootparams_buf); | 703 | kvfree(ldata->bootparams_buf); |
| 663 | ldata->bootparams_buf = NULL; | 704 | ldata->bootparams_buf = NULL; |
| 664 | 705 | ||
| 665 | return 0; | 706 | return 0; |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | |||
| 12 | #include <linux/efi.h> | 12 | #include <linux/efi.h> |
| 13 | #include <linux/fwnode.h> | 13 | #include <linux/fwnode.h> |
| 14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
| 15 | #include <linux/kexec_handover.h> | ||
| 15 | #include <linux/memblock.h> | 16 | #include <linux/memblock.h> |
| 16 | #include <linux/mm_types.h> | 17 | #include <linux/mm_types.h> |
| 17 | #include <linux/of.h> | 18 | #include <linux/of.h> |
| @@ -164,12 +165,32 @@ static __init void reserve_regions(void) · 146 unmodified lines | |||
| 164 | pr_info("Processing EFI memory map:\n"); | 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 | * Discard memblocks discovered so far except for KHO scratch |
| 168 | * point, they originate from memory nodes in the DT, and UEFI | 169 | * regions. Most memblocks at this point originate from memory nodes |
| 169 | * uses its own memory map instead. | 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 | memblock_dump_all(); | 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 | for_each_efi_memory_desc(md) { | 195 | for_each_efi_memory_desc(md) { |
| 175 | paddr = md->phys_addr; | 196 | paddr = md->phys_addr; |
| @@ -279,14 +279,7 @@ static int fbcon_get_rotate(struct fb_info *info) · 278 unmodified lines | |||
| 279 | 279 | ||
| 280 | static bool fbcon_skip_panic(struct fb_info *info) | 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 | 282 | return (info->skip_panic && unlikely(panic_in_progress())); |
| 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 | ||
| 290 | } | 283 | } |
| 291 | 284 | ||
| 292 | static inline bool fbcon_is_active(struct vc_data *vc, struct fb_info *info) | 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 | vm_fault_t vmf; | 421 | vm_fault_t vmf; |
| 422 | unsigned long off = i * PAGE_SIZE; | 422 | unsigned long off = i * PAGE_SIZE; |
| 423 | vmf = vmf_insert_mixed(vma, vma->vm_start + off, | 423 | vmf = vmf_insert_mixed(vma, vma->vm_start + off, |
| 424 | address + off); | 424 | PHYS_PFN(address + off)); |
| 425 | if (vmf & VM_FAULT_ERROR) | 425 | if (vmf & VM_FAULT_ERROR) |
| 426 | ret = vm_fault_to_errno(vmf, 0); | 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 | static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, | 1210 | static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, |
| 1211 | int *nr_cluster, struct msdos_dir_entry **de, | 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 | struct super_block *sb = dir->i_sb; | 1214 | struct super_block *sb = dir->i_sb; |
| 1215 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | 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 | get_bh(bhs[n]); | 1269 | get_bh(bhs[n]); |
| 1270 | *bh = bhs[n]; | 1270 | *bh = bhs[n]; |
| 1271 | *de = (struct msdos_dir_entry *)((*bh)->b_data + offset); | 1271 | *de = (struct msdos_dir_entry *)((*bh)->b_data + offset); |
| 1272 | *i_pos = fat_make_i_pos(sb, *bh, *de); | ||
| 1273 | 1272 | ||
| 1274 | /* Second stage: clear the rest of cluster, and write outs */ | 1273 | /* Second stage: clear the rest of cluster, and write outs */ |
| 1275 | err = fat_zeroed_cluster(dir, start_blknr, ++n, bhs, MAX_BUF_PER_PAGE); | 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 | struct buffer_head *bh, *prev, *bhs[3]; /* 32*slots (672bytes) */ | 1297 | struct buffer_head *bh, *prev, *bhs[3]; /* 32*slots (672bytes) */ |
| 1299 | struct msdos_dir_entry *de; | 1298 | struct msdos_dir_entry *de; |
| 1300 | int err, free_slots, i, nr_bhs; | 1299 | int err, free_slots, i, nr_bhs; |
| 1301 | loff_t pos, i_pos; | 1300 | loff_t pos; |
| 1302 | 1301 | ||
| 1303 | sinfo->nr_slots = nr_slots; | 1302 | sinfo->nr_slots = nr_slots; |
| 1304 | 1303 | ||
| @@ -1386,7 +1385,7 @@ found: · 81 unmodified lines | |||
| 1386 | * add the cluster to dir. | 1385 | * add the cluster to dir. |
| 1387 | */ | 1386 | */ |
| 1388 | cluster = fat_add_new_entries(dir, slots, nr_slots, &nr_cluster, | 1387 | cluster = fat_add_new_entries(dir, slots, nr_slots, &nr_cluster, |
| 1389 | &de, &bh, &i_pos); | 1388 | &de, &bh); |
| 1390 | if (cluster < 0) { | 1389 | if (cluster < 0) { |
| 1391 | err = cluster; | 1390 | err = cluster; |
| 1392 | goto error_remove; | 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 | out: | 6929 | out: |
| 6930 | if (ret != 0) { | 6930 | if (ret != 0) { |
| 6931 | if (folios) | 6931 | ocfs2_unlock_and_free_folios(folios, numfolios); |
| 6932 | ocfs2_unlock_and_free_folios(folios, numfolios); | ||
| 6933 | numfolios = 0; | 6932 | numfolios = 0; |
| 6934 | } | 6933 | } |
| 6935 | 6934 | ||
| @@ -1477,7 +1477,6 @@ way_up_top: · 1476 unmodified lines | |||
| 1477 | goto send_response; | 1477 | goto send_response; |
| 1478 | } else if (res->owner != DLM_LOCK_RES_OWNER_UNKNOWN) { | 1478 | } else if (res->owner != DLM_LOCK_RES_OWNER_UNKNOWN) { |
| 1479 | spin_unlock(&res->spinlock); | 1479 | spin_unlock(&res->spinlock); |
| 1480 | // mlog(0, "node %u is the master\n", res->owner); | ||
| 1481 | response = DLM_MASTER_RESP_NO; | 1480 | response = DLM_MASTER_RESP_NO; |
| 1482 | if (mle) | 1481 | if (mle) |
| 1483 | kmem_cache_free(dlm_mle_cache, mle); | 1482 | kmem_cache_free(dlm_mle_cache, mle); |
| @@ -1493,7 +1492,6 @@ way_up_top: · 9 unmodified lines | |||
| 1493 | BUG(); | 1492 | BUG(); |
| 1494 | } | 1493 | } |
| 1495 | 1494 | ||
| 1496 | // mlog(0, "lockres is in progress...\n"); | ||
| 1497 | spin_lock(&dlm->master_lock); | 1495 | spin_lock(&dlm->master_lock); |
| 1498 | found = dlm_find_mle(dlm, &tmpmle, name, namelen); | 1496 | found = dlm_find_mle(dlm, &tmpmle, name, namelen); |
| 1499 | if (!found) { | 1497 | if (!found) { |
| @@ -1503,8 +1501,6 @@ way_up_top: · 3 unmodified lines | |||
| 1503 | set_maybe = 1; | 1501 | set_maybe = 1; |
| 1504 | spin_lock(&tmpmle->spinlock); | 1502 | spin_lock(&tmpmle->spinlock); |
| 1505 | if (tmpmle->type == DLM_MLE_BLOCK) { | 1503 | if (tmpmle->type == DLM_MLE_BLOCK) { |
| 1506 | // mlog(0, "this node is waiting for " | ||
| 1507 | // "lockres to be mastered\n"); | ||
| 1508 | response = DLM_MASTER_RESP_NO; | 1504 | response = DLM_MASTER_RESP_NO; |
| 1509 | } else if (tmpmle->type == DLM_MLE_MIGRATION) { | 1505 | } else if (tmpmle->type == DLM_MLE_MIGRATION) { |
| 1510 | mlog(0, "node %u is master, but trying to migrate to " | 1506 | mlog(0, "node %u is master, but trying to migrate to " |
| @@ -1531,8 +1527,6 @@ way_up_top: · 20 unmodified lines | |||
| 1531 | } else | 1527 | } else |
| 1532 | response = DLM_MASTER_RESP_NO; | 1528 | response = DLM_MASTER_RESP_NO; |
| 1533 | } else { | 1529 | } else { |
| 1534 | // mlog(0, "this node is attempting to " | ||
| 1535 | // "master lockres\n"); | ||
| 1536 | response = DLM_MASTER_RESP_MAYBE; | 1530 | response = DLM_MASTER_RESP_MAYBE; |
| 1537 | } | 1531 | } |
| 1538 | if (set_maybe) | 1532 | if (set_maybe) |
| @@ -1559,7 +1553,6 @@ way_up_top: · 20 unmodified lines | |||
| 1559 | found = dlm_find_mle(dlm, &tmpmle, name, namelen); | 1553 | found = dlm_find_mle(dlm, &tmpmle, name, namelen); |
| 1560 | if (!found) { | 1554 | if (!found) { |
| 1561 | /* this lockid has never been seen on this node yet */ | 1555 | /* this lockid has never been seen on this node yet */ |
| 1562 | // mlog(0, "no mle found\n"); | ||
| 1563 | if (!mle) { | 1556 | if (!mle) { |
| 1564 | spin_unlock(&dlm->master_lock); | 1557 | spin_unlock(&dlm->master_lock); |
| 1565 | spin_unlock(&dlm->spinlock); | 1558 | spin_unlock(&dlm->spinlock); |
| @@ -1573,8 +1566,6 @@ way_up_top: · 7 unmodified lines | |||
| 1573 | goto way_up_top; | 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 | dlm_init_mle(mle, DLM_MLE_BLOCK, dlm, NULL, name, namelen); | 1569 | dlm_init_mle(mle, DLM_MLE_BLOCK, dlm, NULL, name, namelen); |
| 1579 | set_bit(request->node_idx, mle->maybe_map); | 1570 | set_bit(request->node_idx, mle->maybe_map); |
| 1580 | __dlm_insert_mle(dlm, mle); | 1571 | __dlm_insert_mle(dlm, mle); |
| @@ -1897,8 +1888,6 @@ ok: · 316 unmodified lines | |||
| 1897 | spin_unlock(&res->spinlock); | 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 | if (mle) { | 1891 | if (mle) { |
| 1903 | int extra_ref = 0; | 1892 | int extra_ref = 0; |
| 1904 | int nn = -1; | 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 | if (dlm->reco.dead_node == O2NM_INVALID_NODE_NUM) { | 466 | if (dlm->reco.dead_node == O2NM_INVALID_NODE_NUM) { |
| 467 | // mlog(0, "nothing to recover! sleeping now!\n"); | ||
| 468 | spin_unlock(&dlm->spinlock); | 467 | spin_unlock(&dlm->spinlock); |
| 469 | /* return to main thread loop and sleep. */ | 468 | /* return to main thread loop and sleep. */ |
| 470 | return 0; | 469 | return 0; |
| @@ -1495,6 +1495,14 @@ int ocfs2_validate_inode_block(struct super_block *sb, · 1494 unmodified lines | |||
| 1495 | goto bail; | 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 | rc = 0; | 1506 | rc = 0; |
| 1499 | 1507 | ||
| 1500 | bail: | 1508 | bail: |
| @@ -358,13 +358,11 @@ static int ocfs2_info_handle_freeinode(struct inode *inode, · 357 unmodified lines | |||
| 358 | goto bail; | 358 | goto bail; |
| 359 | } | 359 | } |
| 360 | } else { | 360 | } else { |
| 361 | ocfs2_sprintf_system_inode_name(namebuf, | 361 | int len = ocfs2_sprintf_system_inode_name(namebuf, |
| 362 | sizeof(namebuf), | 362 | sizeof(namebuf), |
| 363 | type, i); | 363 | type, i); |
| 364 | status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, | 364 | status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, |
| 365 | namebuf, | 365 | namebuf, len, &blkno); |
| 366 | strlen(namebuf), | ||
| 367 | &blkno); | ||
| 368 | if (status < 0) { | 366 | if (status < 0) { |
| 369 | status = -ENOENT; | 367 | status = -ENOENT; |
| 370 | goto bail; | 368 | goto bail; |
| @@ -651,12 +649,10 @@ static int ocfs2_info_handle_freefrag(struct inode *inode, · 280 unmodified lines | |||
| 651 | goto bail; | 649 | goto bail; |
| 652 | } | 650 | } |
| 653 | } else { | 651 | } else { |
| 654 | ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, | 652 | int len = ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), |
| 655 | OCFS2_INVALID_SLOT); | 653 | type, OCFS2_INVALID_SLOT); |
| 656 | status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, | 654 | status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, |
| 657 | namebuf, | 655 | namebuf, len, &blkno); |
| 658 | strlen(namebuf), | ||
| 659 | &blkno); | ||
| 660 | if (status < 0) { | 656 | if (status < 0) { |
| 661 | status = -ENOENT; | 657 | status = -ENOENT; |
| 662 | goto bail; | 658 | goto bail; |
| @@ -364,7 +364,7 @@ static int ocfs2_find_victim_alloc_group(struct inode *inode, · 363 unmodified lines | |||
| 364 | int *vict_bit, | 364 | int *vict_bit, |
| 365 | struct buffer_head **ret_bh) | 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 | u64 blkno; | 368 | u64 blkno; |
| 369 | char namebuf[40]; | 369 | char namebuf[40]; |
| 370 | 370 | ||
| @@ -375,9 +375,9 @@ static int ocfs2_find_victim_alloc_group(struct inode *inode, · 4 unmodified lines | |||
| 375 | struct ocfs2_dinode *ac_dinode; | 375 | struct ocfs2_dinode *ac_dinode; |
| 376 | struct ocfs2_group_desc *bg; | 376 | struct ocfs2_group_desc *bg; |
| 377 | 377 | ||
| 378 | ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, slot); | 378 | len = ocfs2_sprintf_system_inode_name(namebuf, sizeof(namebuf), type, slot); |
| 379 | ret = ocfs2_lookup_ino_from_name(osb->sys_root_inode, namebuf, | 379 | ret = ocfs2_lookup_ino_from_name(osb->sys_root_inode, namebuf, len, &blkno); |
| 380 | strlen(namebuf), &blkno); | 380 | |
| 381 | if (ret) { | 381 | if (ret) { |
| 382 | ret = -ENOENT; | 382 | ret = -ENOENT; |
| 383 | goto out; | 383 | goto out; |
| @@ -614,7 +614,7 @@ struct ocfs2_super_block { · 613 unmodified lines | |||
| 614 | __le16 s_reserved0; | 614 | __le16 s_reserved0; |
| 615 | __le32 s_dx_seed[3]; /* seed[0-2] for dx dir hash. | 615 | __le32 s_dx_seed[3]; /* seed[0-2] for dx dir hash. |
| 616 | * s_uuid_hash serves as seed[3]. */ | 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 | /*140*/ | 618 | /*140*/ |
| 619 | 619 | ||
| 620 | /* | 620 | /* |
| @@ -1011,6 +1011,7 @@ static int user_cluster_connect(struct ocfs2_cluster_connection *conn) · 1010 unmodified lines | |||
| 1011 | printk(KERN_ERR "ocfs2: Could not determine" | 1011 | printk(KERN_ERR "ocfs2: Could not determine" |
| 1012 | " locking version\n"); | 1012 | " locking version\n"); |
| 1013 | user_cluster_disconnect(conn); | 1013 | user_cluster_disconnect(conn); |
| 1014 | lc = NULL; | ||
| 1014 | goto out; | 1015 | goto out; |
| 1015 | } | 1016 | } |
| 1016 | wait_event(lc->oc_wait, (atomic_read(&lc->oc_this_node) > 0)); | 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 | char namebuf[40]; | 127 | char namebuf[40]; |
| 128 | struct inode *inode = NULL; | 128 | struct inode *inode = NULL; |
| 129 | u64 blkno; | 129 | u64 blkno; |
| 130 | int status = 0; | 130 | int len, status = 0; |
| 131 | 131 | ||
| 132 | ocfs2_sprintf_system_inode_name(namebuf, | 132 | len = ocfs2_sprintf_system_inode_name(namebuf, |
| 133 | sizeof(namebuf), | 133 | sizeof(namebuf), |
| 134 | type, slot); | 134 | type, slot); |
| 135 | 135 | ||
| 136 | status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, namebuf, | 136 | status = ocfs2_lookup_ino_from_name(osb->sys_root_inode, |
| 137 | strlen(namebuf), &blkno); | 137 | namebuf, len, &blkno); |
| 138 | if (status < 0) { | 138 | if (status < 0) { |
| 139 | goto bail; | 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 | tid = task_pid_nr_ns(task, ns); | 3947 | tid = task_pid_nr_ns(task, ns); |
| 3948 | if (!tid) | 3948 | if (!tid) |
| 3949 | continue; /* The task has just exited. */ | 3949 | continue; /* The task has just exited. */ |
| 3950 | len = snprintf(name, sizeof(name), "%u", tid); | 3950 | len = snprintf(name, sizeof(name), "%d", tid); |
| 3951 | if (!proc_fill_cache(file, ctx, name, len, | 3951 | if (!proc_fill_cache(file, ctx, name, len, |
| 3952 | proc_task_instantiate, task, NULL)) { | 3952 | proc_task_instantiate, task, NULL)) { |
| 3953 | /* returning this tgid failed, save it as the first | 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 | all_done: | 307 | all_done: |
| 308 | *index_block = cur_index_block; | 308 | *index_block = cur_index_block; |
| 309 | *index_offset = cur_offset; | 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 | * Scale cache index (cache slot entry) to index | 314 | * Scale cache index (cache slot entry) to index |
| @@ -324,17 +325,15 @@ failed: · 10 unmodified lines | |||
| 324 | * Get the on-disk location and compressed size of the datablock | 325 | * Get the on-disk location and compressed size of the datablock |
| 325 | * specified by index. Fill_meta_index() does most of the work. | 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 | long long blks; | 331 | long long blks; |
| 331 | int offset; | ||
| 332 | __le32 size; | 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" | 335 | TRACE("read_blocklist: res %d, index %d, start 0x%llx, offset 0x%x, block 0x%llx\n", |
| 336 | " 0x%x, block 0x%llx\n", res, index, start, offset, | 336 | res, index, *start, *offset, block ? *block : 0); |
| 337 | *block); | ||
| 338 | 337 | ||
| 339 | if (res < 0) | 338 | if (res < 0) |
| 340 | return res; | 339 | return res; |
| @@ -346,22 +345,31 @@ static int read_blocklist(struct inode *inode, int index, u64 *block) · 5 unmodified lines | |||
| 346 | * extra block indexes needed. | 345 | * extra block indexes needed. |
| 347 | */ | 346 | */ |
| 348 | if (res < index) { | 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 | if (blks < 0) | 349 | if (blks < 0) |
| 351 | return (int) blks; | 350 | return (int) blks; |
| 352 | *block += blks; | 351 | if (block) |
| 352 | *block += blks; | ||
| 353 | } | 353 | } |
| 354 | 354 | ||
| 355 | /* | 355 | /* |
| 356 | * Read length of block specified by index. | 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 | sizeof(size)); | 359 | sizeof(size)); |
| 360 | if (res < 0) | 360 | if (res < 0) |
| 361 | return res; | 361 | return res; |
| 362 | return squashfs_block_size(size); | 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 | static bool squashfs_fill_page(struct folio *folio, | 373 | static bool squashfs_fill_page(struct folio *folio, |
| 366 | struct squashfs_cache_entry *buffer, size_t offset, | 374 | struct squashfs_cache_entry *buffer, size_t offset, |
| 367 | size_t avail) | 375 | size_t avail) |
| @@ -658,7 +666,114 @@ skip_pages: · 290 unmodified lines | |||
| 658 | kfree(pages); | 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 | const struct address_space_operations squashfs_aops = { | 769 | const struct address_space_operations squashfs_aops = { |
| 662 | .read_folio = squashfs_read_folio, | 770 | .read_folio = squashfs_read_folio, |
| 663 | .readahead = squashfs_readahead | 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 | inode->i_mode = le16_to_cpu(sqsh_ino->mode); | 68 | inode->i_mode = le16_to_cpu(sqsh_ino->mode); |
| 69 | inode->i_size = 0; | 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 | return err; | 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 | if (err < 0) | 144 | if (err < 0) |
| 141 | goto failed_read; | 145 | goto failed_read; |
| 142 | 146 | ||
| 147 | inode->i_size = le32_to_cpu(sqsh_ino->file_size); | ||
| 143 | frag = le32_to_cpu(sqsh_ino->fragment); | 148 | frag = le32_to_cpu(sqsh_ino->fragment); |
| 144 | if (frag != SQUASHFS_INVALID_FRAG) { | 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 | frag_offset = le32_to_cpu(sqsh_ino->offset); | 158 | frag_offset = le32_to_cpu(sqsh_ino->offset); |
| 146 | frag_size = squashfs_frag_lookup(sb, frag, &frag_blk); | 159 | frag_size = squashfs_frag_lookup(sb, frag, &frag_blk); |
| 147 | if (frag_size < 0) { | 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 | set_nlink(inode, 1); | 170 | set_nlink(inode, 1); |
| 158 | inode->i_size = le32_to_cpu(sqsh_ino->file_size); | 171 | inode->i_fop = &squashfs_file_operations; |
| 159 | inode->i_fop = &generic_ro_fops; | ||
| 160 | inode->i_mode |= S_IFREG; | 172 | inode->i_mode |= S_IFREG; |
| 161 | inode->i_blocks = ((inode->i_size - 1) >> 9) + 1; | 173 | inode->i_blocks = ((inode->i_size - 1) >> 9) + 1; |
| 162 | squashfs_i(inode)->fragment_block = frag_blk; | 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 | squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block); | 177 | squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block); |
| 166 | squashfs_i(inode)->block_list_start = block; | 178 | squashfs_i(inode)->block_list_start = block; |
| 167 | squashfs_i(inode)->offset = offset; | 179 | squashfs_i(inode)->offset = offset; |
| 180 | squashfs_i(inode)->parent = 0; | ||
| 168 | inode->i_data.a_ops = &squashfs_aops; | 181 | inode->i_data.a_ops = &squashfs_aops; |
| 169 | 182 | ||
| 170 | TRACE("File inode %x:%x, start_block %llx, block_list_start " | 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 | if (err < 0) | 196 | if (err < 0) |
| 184 | goto failed_read; | 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 | frag = le32_to_cpu(sqsh_ino->fragment); | 204 | frag = le32_to_cpu(sqsh_ino->fragment); |
| 187 | if (frag != SQUASHFS_INVALID_FRAG) { | 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 | frag_offset = le32_to_cpu(sqsh_ino->offset); | 214 | frag_offset = le32_to_cpu(sqsh_ino->offset); |
| 189 | frag_size = squashfs_frag_lookup(sb, frag, &frag_blk); | 215 | frag_size = squashfs_frag_lookup(sb, frag, &frag_blk); |
| 190 | if (frag_size < 0) { | 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 | xattr_id = le32_to_cpu(sqsh_ino->xattr); | 226 | xattr_id = le32_to_cpu(sqsh_ino->xattr); |
| 201 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); | 227 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); |
| 202 | inode->i_size = le64_to_cpu(sqsh_ino->file_size); | ||
| 203 | inode->i_op = &squashfs_inode_ops; | 228 | inode->i_op = &squashfs_inode_ops; |
| 204 | inode->i_fop = &generic_ro_fops; | 229 | inode->i_fop = &squashfs_file_operations; |
| 205 | inode->i_mode |= S_IFREG; | 230 | inode->i_mode |= S_IFREG; |
| 206 | inode->i_blocks = (inode->i_size - | 231 | inode->i_blocks = (inode->i_size - |
| 207 | le64_to_cpu(sqsh_ino->sparse) + 511) >> 9; | 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 | squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block); | 237 | squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block); |
| 213 | squashfs_i(inode)->block_list_start = block; | 238 | squashfs_i(inode)->block_list_start = block; |
| 214 | squashfs_i(inode)->offset = offset; | 239 | squashfs_i(inode)->offset = offset; |
| 240 | squashfs_i(inode)->parent = 0; | ||
| 215 | inode->i_data.a_ops = &squashfs_aops; | 241 | inode->i_data.a_ops = &squashfs_aops; |
| 216 | 242 | ||
| 217 | TRACE("File inode %x:%x, start_block %llx, block_list_start " | 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 | inode->i_mode |= S_IFLNK; | 318 | inode->i_mode |= S_IFLNK; |
| 293 | squashfs_i(inode)->start = block; | 319 | squashfs_i(inode)->start = block; |
| 294 | squashfs_i(inode)->offset = offset; | 320 | squashfs_i(inode)->offset = offset; |
| 321 | squashfs_i(inode)->parent = 0; | ||
| 295 | 322 | ||
| 296 | if (type == SQUASHFS_LSYMLINK_TYPE) { | 323 | if (type == SQUASHFS_LSYMLINK_TYPE) { |
| 297 | __le32 xattr; | 324 | __le32 xattr; |
| @@ -329,6 +356,7 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 31 unmodified lines | |||
| 329 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); | 356 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); |
| 330 | rdev = le32_to_cpu(sqsh_ino->rdev); | 357 | rdev = le32_to_cpu(sqsh_ino->rdev); |
| 331 | init_special_inode(inode, inode->i_mode, new_decode_dev(rdev)); | 358 | init_special_inode(inode, inode->i_mode, new_decode_dev(rdev)); |
| 359 | squashfs_i(inode)->parent = 0; | ||
| 332 | 360 | ||
| 333 | TRACE("Device inode %x:%x, rdev %x\n", | 361 | TRACE("Device inode %x:%x, rdev %x\n", |
| 334 | SQUASHFS_INODE_BLK(ino), offset, rdev); | 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 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); | 381 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); |
| 354 | rdev = le32_to_cpu(sqsh_ino->rdev); | 382 | rdev = le32_to_cpu(sqsh_ino->rdev); |
| 355 | init_special_inode(inode, inode->i_mode, new_decode_dev(rdev)); | 383 | init_special_inode(inode, inode->i_mode, new_decode_dev(rdev)); |
| 384 | squashfs_i(inode)->parent = 0; | ||
| 356 | 385 | ||
| 357 | TRACE("Device inode %x:%x, rdev %x\n", | 386 | TRACE("Device inode %x:%x, rdev %x\n", |
| 358 | SQUASHFS_INODE_BLK(ino), offset, rdev); | 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 | inode->i_mode |= S_IFSOCK; | 402 | inode->i_mode |= S_IFSOCK; |
| 374 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); | 403 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); |
| 375 | init_special_inode(inode, inode->i_mode, 0); | 404 | init_special_inode(inode, inode->i_mode, 0); |
| 405 | squashfs_i(inode)->parent = 0; | ||
| 376 | break; | 406 | break; |
| 377 | } | 407 | } |
| 378 | case SQUASHFS_LFIFO_TYPE: | 408 | case SQUASHFS_LFIFO_TYPE: |
| @@ -392,6 +422,7 @@ int squashfs_read_inode(struct inode *inode, long long ino) · 13 unmodified lines | |||
| 392 | inode->i_op = &squashfs_inode_ops; | 422 | inode->i_op = &squashfs_inode_ops; |
| 393 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); | 423 | set_nlink(inode, le32_to_cpu(sqsh_ino->nlink)); |
| 394 | init_special_inode(inode, inode->i_mode, 0); | 424 | init_special_inode(inode, inode->i_mode, 0); |
| 425 | squashfs_i(inode)->parent = 0; | ||
| 395 | break; | 426 | break; |
| 396 | } | 427 | } |
| 397 | default: | 428 | default: |
| @@ -107,6 +107,7 @@ extern const struct address_space_operations squashfs_aops; · 106 unmodified lines | |||
| 107 | 107 | ||
| 108 | /* inode.c */ | 108 | /* inode.c */ |
| 109 | extern const struct inode_operations squashfs_inode_ops; | 109 | extern const struct inode_operations squashfs_inode_ops; |
| 110 | extern const struct file_operations squashfs_file_operations; | ||
| 110 | 111 | ||
| 111 | /* namei.c */ | 112 | /* namei.c */ |
| 112 | extern const struct inode_operations squashfs_dir_inode_ops; | 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 | #define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) | 208 | #define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) |
| 209 | #define SQUASHFS_META_ENTRIES 127 | 209 | #define SQUASHFS_META_ENTRIES 127 |
| 210 | #define SQUASHFS_META_SLOTS 8 | 210 | #define SQUASHFS_META_SLOTS 8 |
| 211 | #define SQUASHFS_SCAN_INDEXES 1024 | ||
| 211 | 212 | ||
| 212 | struct meta_entry { | 213 | struct meta_entry { |
| 213 | u64 data_block; | 214 | u64 data_block; |
| @@ -16,6 +16,7 @@ struct squashfs_inode_info { · 15 unmodified lines | |||
| 16 | u64 xattr; | 16 | u64 xattr; |
| 17 | unsigned int xattr_size; | 17 | unsigned int xattr_size; |
| 18 | int xattr_count; | 18 | int xattr_count; |
| 19 | int parent; | ||
| 19 | union { | 20 | union { |
| 20 | struct { | 21 | struct { |
| 21 | u64 fragment_block; | 22 | u64 fragment_block; |
| @@ -27,7 +28,6 @@ struct squashfs_inode_info { · 5 unmodified lines | |||
| 27 | u64 dir_idx_start; | 28 | u64 dir_idx_start; |
| 28 | int dir_idx_offset; | 29 | int dir_idx_offset; |
| 29 | int dir_idx_cnt; | 30 | int dir_idx_cnt; |
| 30 | int parent; | ||
| 31 | }; | 31 | }; |
| 32 | }; | 32 | }; |
| 33 | struct inode vfs_inode; | 33 | struct inode vfs_inode; |
| @@ -334,14 +334,6 @@ static inline void ida_init(struct ida *ida) · 333 unmodified lines | |||
| 334 | xa_init_flags(&ida->xa, IDA_INIT_FLAGS); | 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 | static inline bool ida_is_empty(const struct ida *ida) | 337 | static inline bool ida_is_empty(const struct ida *ida) |
| 346 | { | 338 | { |
| 347 | return xa_empty(&ida->xa); | 339 | return xa_empty(&ida->xa); |
| @@ -164,11 +164,23 @@ extern int root_mountflags; · 163 unmodified lines | |||
| 164 | 164 | ||
| 165 | extern bool early_boot_irqs_disabled; | 165 | extern bool early_boot_irqs_disabled; |
| 166 | 166 | ||
| 167 | /* | 167 | /** |
| 168 | * Values used for system_state. Ordering of the states must not be changed | 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 | * as code checks for <, <=, >, >= STATE. | 181 | * as code checks for <, <=, >, >= STATE. |
| 170 | */ | 182 | */ |
| 171 | extern enum system_states { | 183 | enum system_states { |
| 172 | SYSTEM_BOOTING, | 184 | SYSTEM_BOOTING, |
| 173 | SYSTEM_SCHEDULING, | 185 | SYSTEM_SCHEDULING, |
| 174 | SYSTEM_FREEING_INITMEM, | 186 | SYSTEM_FREEING_INITMEM, |
| @@ -177,7 +189,8 @@ extern enum system_states { · 2 unmodified lines | |||
| 177 | SYSTEM_POWER_OFF, | 189 | SYSTEM_POWER_OFF, |
| 178 | SYSTEM_RESTART, | 190 | SYSTEM_RESTART, |
| 179 | SYSTEM_SUSPEND, | 191 | SYSTEM_SUSPEND, |
| 180 | } system_state; | 192 | }; |
| 193 | extern enum system_states system_state; | ||
| 181 | 194 | ||
| 182 | /* | 195 | /* |
| 183 | * General tracing related utility functions - trace_printk(), | 196 | * General tracing related utility functions - trace_printk(), |
| @@ -395,6 +395,9 @@ struct kimage { · 394 unmodified lines | |||
| 395 | 395 | ||
| 396 | /* Information for loading purgatory */ | 396 | /* Information for loading purgatory */ |
| 397 | struct purgatory_info purgatory_info; | 397 | struct purgatory_info purgatory_info; |
| 398 | |||
| 399 | /* Force carrying over the DTB from the current boot */ | ||
| 400 | bool force_dtb; | ||
| 398 | #endif | 401 | #endif |
| 399 | 402 | ||
| 400 | #ifdef CONFIG_CRASH_HOTPLUG | 403 | #ifdef CONFIG_CRASH_HOTPLUG |
| @@ -461,7 +464,7 @@ bool kexec_load_permitted(int kexec_image_type); · 60 unmodified lines | |||
| 461 | /* List of defined/legal kexec file flags */ | 464 | /* List of defined/legal kexec file flags */ |
| 462 | #define KEXEC_FILE_FLAGS (KEXEC_FILE_UNLOAD | KEXEC_FILE_ON_CRASH | \ | 465 | #define KEXEC_FILE_FLAGS (KEXEC_FILE_UNLOAD | KEXEC_FILE_ON_CRASH | \ |
| 463 | KEXEC_FILE_NO_INITRAMFS | KEXEC_FILE_DEBUG | \ | 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 | /* flag to track if kexec reboot is in progress */ | 469 | /* flag to track if kexec reboot is in progress */ |
| 467 | extern bool kexec_in_progress; | 470 | extern bool kexec_in_progress; |
| @@ -40,6 +40,7 @@ struct kho_serialization; · 39 unmodified lines | |||
| 40 | 40 | ||
| 41 | #ifdef CONFIG_KEXEC_HANDOVER | 41 | #ifdef CONFIG_KEXEC_HANDOVER |
| 42 | bool kho_is_enabled(void); | 42 | bool kho_is_enabled(void); |
| 43 | bool is_kho_boot(void); | ||
| 43 | 44 | ||
| 44 | int kho_preserve_folio(struct folio *folio); | 45 | int kho_preserve_folio(struct folio *folio); |
| 45 | int kho_preserve_phys(phys_addr_t phys, size_t size); | 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 | return false; | 61 | return false; |
| 61 | } | 62 | } |
| 62 | 63 | ||
| 64 | static inline bool is_kho_boot(void) | ||
| 65 | { | ||
| 66 | return false; | ||
| 67 | } | ||
| 68 | |||
| 63 | static inline int kho_preserve_folio(struct folio *folio) | 69 | static inline int kho_preserve_folio(struct folio *folio) |
| 64 | { | 70 | { |
| 65 | return -EOPNOTSUPP; | 71 | return -EOPNOTSUPP; |
| @@ -20,8 +20,16 @@ · 19 unmodified lines | |||
| 20 | * using the generic single-entry routines. | 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 | #define LIST_HEAD_INIT(name) { &(name), &(name) } | 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 | #define LIST_HEAD(name) \ | 33 | #define LIST_HEAD(name) \ |
| 26 | struct list_head name = LIST_HEAD_INIT(name) | 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 | __module_param_call("", name, ¶m_ops_##type, &var, perm, \ | 349 | __module_param_call("", name, ¶m_ops_##type, &var, perm, \ |
| 350 | -1, KERNEL_PARAM_FL_UNSAFE) | 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 | #endif /* !MODULE */ | 365 | #endif /* !MODULE */ |
| 353 | 366 | ||
| 354 | /** | 367 | /** |
| @@ -103,7 +103,7 @@ struct nvmem_cell_info { · 102 unmodified lines | |||
| 103 | * | 103 | * |
| 104 | * Note: A default "nvmem<id>" name will be assigned to the device if | 104 | * Note: A default "nvmem<id>" name will be assigned to the device if |
| 105 | * no name is specified in its configuration. In such case "<id>" is | 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 | * Note: Specifying name and setting id to -1 implies a unique device | 108 | * Note: Specifying name and setting id to -1 implies a unique device |
| 109 | * whose name is provided as-is (kept unaltered). | 109 | * whose name is provided as-is (kept unaltered). |
| @@ -43,6 +43,12 @@ void abort(void); · 42 unmodified lines | |||
| 43 | extern atomic_t panic_cpu; | 43 | extern atomic_t panic_cpu; |
| 44 | #define PANIC_CPU_INVALID -1 | 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 | * Only to be used by arch init code. If the user over-wrote the default | 53 | * Only to be used by arch init code. If the user over-wrote the default |
| 48 | * CONFIG_PANIC_TIMEOUT, honor it. | 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 | #endif | 331 | #endif |
| 332 | 332 | ||
| 333 | bool this_cpu_in_panic(void); | ||
| 334 | |||
| 335 | #ifdef CONFIG_SMP | 333 | #ifdef CONFIG_SMP |
| 336 | extern int __printk_cpu_sync_try_get(void); | 334 | extern int __printk_cpu_sync_try_get(void); |
| 337 | extern void __printk_cpu_sync_wait(void); | 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 | * pins the final release of task.io_context. Also protects ->cpuset and | 210 | * pins the final release of task.io_context. Also protects ->cpuset and |
| 211 | * ->cgroup.subsys[]. And ->vfork_done. And ->sysvshm.shm_clist. | 211 | * ->cgroup.subsys[]. And ->vfork_done. And ->sysvshm.shm_clist. |
| 212 | * | 212 | * |
| 213 | * Nests both inside and outside of read_lock(&tasklist_lock). | 213 | * Nests inside of read_lock(&tasklist_lock). It must not be nested with |
| 214 | * It must not be nested with write_lock_irq(&tasklist_lock), | 214 | * write_lock_irq(&tasklist_lock), neither inside nor outside. |
| 215 | * neither inside nor outside. | ||
| 216 | */ | 215 | */ |
| 217 | static inline void task_lock(struct task_struct *p) | 216 | static inline void task_lock(struct task_struct *p) |
| 218 | { | 217 | { |
| @@ -22,12 +22,16 @@ · 21 unmodified lines | |||
| 22 | * KEXEC_FILE_ON_CRASH : Load/unload operation belongs to kdump image. | 22 | * KEXEC_FILE_ON_CRASH : Load/unload operation belongs to kdump image. |
| 23 | * KEXEC_FILE_NO_INITRAMFS : No initramfs is being loaded. Ignore the initrd | 23 | * KEXEC_FILE_NO_INITRAMFS : No initramfs is being loaded. Ignore the initrd |
| 24 | * fd field. | 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 | #define KEXEC_FILE_UNLOAD 0x00000001 | 29 | #define KEXEC_FILE_UNLOAD 0x00000001 |
| 27 | #define KEXEC_FILE_ON_CRASH 0x00000002 | 30 | #define KEXEC_FILE_ON_CRASH 0x00000002 |
| 28 | #define KEXEC_FILE_NO_INITRAMFS 0x00000004 | 31 | #define KEXEC_FILE_NO_INITRAMFS 0x00000004 |
| 29 | #define KEXEC_FILE_DEBUG 0x00000008 | 32 | #define KEXEC_FILE_DEBUG 0x00000008 |
| 30 | #define KEXEC_FILE_NO_CMA 0x00000010 | 33 | #define KEXEC_FILE_NO_CMA 0x00000010 |
| 34 | #define KEXEC_FILE_FORCE_DTB 0x00000020 | ||
| 31 | 35 | ||
| 32 | /* These values match the ELF architecture values. | 36 | /* These values match the ELF architecture values. |
| 33 | * Unless there is a good reason that should continue to be the case. | 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 | const char *unused, void *arg) | 545 | const char *unused, void *arg) |
| 546 | { | 546 | { |
| 547 | size_t len = strlen(param); | 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 | /* Handle params aliased to sysctls */ | 555 | /* Handle params aliased to sysctls */ |
| 550 | if (sysctl_is_alias(param)) | 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 | repair_env_string(param, val); | 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 | /* Handle obsolete-style parameters */ | 567 | /* Handle obsolete-style parameters */ |
| 556 | if (obsolete_checksetup(param)) | 568 | if (obsolete_checksetup(param)) |
| 557 | return 0; | 569 | return 0; |
| @@ -148,6 +148,17 @@ config CRASH_DM_CRYPT_CONFIGS · 147 unmodified lines | |||
| 148 | CRASH_DM_CRYPT cannot directly select CONFIGFS_FS, because that | 148 | CRASH_DM_CRYPT cannot directly select CONFIGFS_FS, because that |
| 149 | is required to be built-in. | 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 | config CRASH_HOTPLUG | 162 | config CRASH_HOTPLUG |
| 152 | bool "Update the crash elfcorehdr on system configuration changes" | 163 | bool "Update the crash elfcorehdr on system configuration changes" |
| 153 | default y | 164 | default y |
| @@ -78,6 +78,7 @@ obj-$(CONFIG_CRASH_RESERVE) += crash_reserve.o · 77 unmodified lines | |||
| 78 | obj-$(CONFIG_KEXEC_CORE) += kexec_core.o | 78 | obj-$(CONFIG_KEXEC_CORE) += kexec_core.o |
| 79 | obj-$(CONFIG_CRASH_DUMP) += crash_core.o | 79 | obj-$(CONFIG_CRASH_DUMP) += crash_core.o |
| 80 | obj-$(CONFIG_CRASH_DM_CRYPT) += crash_dump_dm_crypt.o | 80 | obj-$(CONFIG_CRASH_DM_CRYPT) += crash_dump_dm_crypt.o |
| 81 | obj-$(CONFIG_CRASH_DUMP_KUNIT_TEST) += crash_core_test.o | ||
| 81 | obj-$(CONFIG_KEXEC) += kexec.o | 82 | obj-$(CONFIG_KEXEC) += kexec.o |
| 82 | obj-$(CONFIG_KEXEC_FILE) += kexec_file.o | 83 | obj-$(CONFIG_KEXEC_FILE) += kexec_file.o |
| 83 | obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o | 84 | obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o |
| @@ -22,6 +22,7 @@ · 21 unmodified lines | |||
| 22 | #include <linux/btf.h> | 22 | #include <linux/btf.h> |
| 23 | #include <linux/objtool.h> | 23 | #include <linux/objtool.h> |
| 24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
| 25 | #include <linux/panic.h> | ||
| 25 | 26 | ||
| 26 | #include <asm/page.h> | 27 | #include <asm/page.h> |
| 27 | #include <asm/sections.h> | 28 | #include <asm/sections.h> |
| @@ -143,17 +144,7 @@ STACK_FRAME_NON_STANDARD(__crash_kexec); · 115 unmodified lines | |||
| 143 | 144 | ||
| 144 | __bpf_kfunc void crash_kexec(struct pt_regs *regs) | 145 | __bpf_kfunc void crash_kexec(struct pt_regs *regs) |
| 145 | { | 146 | { |
| 146 | int old_cpu, this_cpu; | 147 | if (panic_try_start()) { |
| 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)) { | ||
| 157 | /* This is the 1st CPU which comes here, so go ahead. */ | 148 | /* This is the 1st CPU which comes here, so go ahead. */ |
| 158 | __crash_kexec(regs); | 149 | __crash_kexec(regs); |
| 159 | 150 | ||
| @@ -161,7 +152,7 @@ __bpf_kfunc void crash_kexec(struct pt_regs *regs) · 1 unmodified line | |||
| 161 | * Reset panic_cpu to allow another panic()/crash_kexec() | 152 | * Reset panic_cpu to allow another panic()/crash_kexec() |
| 162 | * call. | 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 | return 0; | 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 | int crash_exclude_mem_range(struct crash_mem *mem, | 282 | int crash_exclude_mem_range(struct crash_mem *mem, |
| 278 | unsigned long long mstart, unsigned long long mend) | 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 | return 0; | 339 | return 0; |
| 335 | } | 340 | } |
| 341 | EXPORT_SYMBOL_GPL(crash_exclude_mem_range); | ||
| 336 | 342 | ||
| 337 | ssize_t crash_get_memory_size(void) | 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 | p->pagefault_disabled = 0; | 2133 | p->pagefault_disabled = 0; |
| 2134 | 2134 | ||
| 2135 | #ifdef CONFIG_LOCKDEP | ||
| 2136 | lockdep_init_task(p); | 2135 | lockdep_init_task(p); |
| 2137 | #endif | ||
| 2138 | 2136 | ||
| 2139 | p->blocked_on = NULL; /* not blocked yet */ | 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 | struct task_struct *create_io_thread(int (*fn)(void *), void *arg, int node) | 2545 | struct task_struct *create_io_thread(int (*fn)(void *), void *arg, int node) |
| 2548 | { | 2546 | { |
| 2549 | unsigned long flags = CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD| | 2547 | unsigned long flags = CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD| |
| 2550 | CLONE_IO; | 2548 | CLONE_IO|CLONE_VM|CLONE_UNTRACED; |
| 2551 | struct kernel_clone_args args = { | 2549 | struct kernel_clone_args args = { |
| 2552 | .flags = ((lower_32_bits(flags) | CLONE_VM | | 2550 | .flags = flags, |
| 2553 | CLONE_UNTRACED) & ~CSIGNAL), | ||
| 2554 | .exit_signal = (lower_32_bits(flags) & CSIGNAL), | ||
| 2555 | .fn = fn, | 2551 | .fn = fn, |
| 2556 | .fn_arg = arg, | 2552 | .fn_arg = arg, |
| 2557 | .io_thread = 1, | 2553 | .io_thread = 1, |
| @@ -2663,9 +2659,8 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, const char *name, · 105 unmodified lines | |||
| 2663 | unsigned long flags) | 2659 | unsigned long flags) |
| 2664 | { | 2660 | { |
| 2665 | struct kernel_clone_args args = { | 2661 | struct kernel_clone_args args = { |
| 2666 | .flags = ((lower_32_bits(flags) | CLONE_VM | | 2662 | .flags = ((flags | CLONE_VM | CLONE_UNTRACED) & ~CSIGNAL), |
| 2667 | CLONE_UNTRACED) & ~CSIGNAL), | 2663 | .exit_signal = (flags & CSIGNAL), |
| 2668 | .exit_signal = (lower_32_bits(flags) & CSIGNAL), | ||
| 2669 | .fn = fn, | 2664 | .fn = fn, |
| 2670 | .fn_arg = arg, | 2665 | .fn_arg = arg, |
| 2671 | .name = name, | 2666 | .name = name, |
| @@ -2681,9 +2676,8 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, const char *name, · 9 unmodified lines | |||
| 2681 | pid_t user_mode_thread(int (*fn)(void *), void *arg, unsigned long flags) | 2676 | pid_t user_mode_thread(int (*fn)(void *), void *arg, unsigned long flags) |
| 2682 | { | 2677 | { |
| 2683 | struct kernel_clone_args args = { | 2678 | struct kernel_clone_args args = { |
| 2684 | .flags = ((lower_32_bits(flags) | CLONE_VM | | 2679 | .flags = ((flags | CLONE_VM | CLONE_UNTRACED) & ~CSIGNAL), |
| 2685 | CLONE_UNTRACED) & ~CSIGNAL), | 2680 | .exit_signal = (flags & CSIGNAL), |
| 2686 | .exit_signal = (lower_32_bits(flags) & CSIGNAL), | ||
| 2687 | .fn = fn, | 2681 | .fn = fn, |
| 2688 | .fn_arg = arg, | 2682 | .fn_arg = arg, |
| 2689 | }; | 2683 | }; |
| @@ -95,9 +95,41 @@ static struct notifier_block panic_block = { · 94 unmodified lines | |||
| 95 | .notifier_call = hung_task_panic, | 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 | #ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER | 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 | struct task_struct *g, *t; | 134 | struct task_struct *g, *t; |
| 103 | unsigned long owner, blocker, blocker_type; | 135 | unsigned long owner, blocker, blocker_type; |
| @@ -174,41 +206,21 @@ static void debug_show_blocker(struct task_struct *task) · 70 unmodified lines | |||
| 174 | t->pid, rwsem_blocked_by); | 206 | t->pid, rwsem_blocked_by); |
| 175 | break; | 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 | return; | 212 | return; |
| 179 | } | 213 | } |
| 180 | } | 214 | } |
| 181 | #else | 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 | #endif | 219 | #endif |
| 186 | 220 | ||
| 187 | static void check_hung_task(struct task_struct *t, unsigned long timeout) | 221 | static void check_hung_task(struct task_struct *t, unsigned long timeout) |
| 188 | { | 222 | { |
| 189 | unsigned long switch_count = t->nvcsw + t->nivcsw; | 223 | if (!task_is_hung(t, timeout)) |
| 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)) | ||
| 212 | return; | 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 | pr_err("\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\"" | 255 | pr_err("\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\"" |
| 244 | " disables this message.\n"); | 256 | " disables this message.\n"); |
| 245 | sched_show_task(t); | 257 | sched_show_task(t); |
| 246 | debug_show_blocker(t); | 258 | debug_show_blocker(t, timeout); |
| 247 | hung_task_show_lock = true; | 259 | hung_task_show_lock = true; |
| 248 | 260 | ||
| 249 | if (sysctl_hung_task_all_cpu_backtrace) | 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 | hung_task_show_lock = false; | 311 | hung_task_show_lock = false; |
| 300 | rcu_read_lock(); | 312 | rcu_read_lock(); |
| 301 | for_each_process_thread(g, t) { | 313 | for_each_process_thread(g, t) { |
| 302 | unsigned int state; | ||
| 303 | 314 | ||
| 304 | if (!max_count--) | 315 | if (!max_count--) |
| 305 | goto unlock; | 316 | goto unlock; |
| @@ -308,15 +319,8 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) · 2 unmodified lines | |||
| 308 | goto unlock; | 319 | goto unlock; |
| 309 | last_break = jiffies; | 320 | last_break = jiffies; |
| 310 | } | 321 | } |
| 311 | /* | 322 | |
| 312 | * skip the TASK_KILLABLE tasks -- these can be killed | 323 | check_hung_task(t, timeout); |
| 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); | ||
| 320 | } | 324 | } |
| 321 | unlock: | 325 | unlock: |
| 322 | rcu_read_unlock(); | 326 | rcu_read_unlock(); |
| @@ -264,7 +264,7 @@ static int test_kallsyms_basic_function(void) · 263 unmodified lines | |||
| 264 | char namebuf[KSYM_NAME_LEN]; | 264 | char namebuf[KSYM_NAME_LEN]; |
| 265 | struct test_stat *stat, *stat2; | 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 | if (!stat) | 268 | if (!stat) |
| 269 | return -ENOMEM; | 269 | return -ENOMEM; |
| 270 | stat2 = stat + 1; | 270 | stat2 = stat + 1; |
| @@ -978,6 +978,15 @@ static void kcov_move_area(enum kcov_mode mode, void *dst_area, · 977 unmodified lines | |||
| 978 | memcpy(dst_entries, src_entries, bytes_to_move); | 978 | memcpy(dst_entries, src_entries, bytes_to_move); |
| 979 | entries_moved = bytes_to_move >> entry_size_log; | 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 | switch (mode) { | 990 | switch (mode) { |
| 982 | case KCOV_MODE_TRACE_PC: | 991 | case KCOV_MODE_TRACE_PC: |
| 983 | WRITE_ONCE(*(unsigned long *)dst_area, dst_len + entries_moved); | 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 | if (!image) | 233 | if (!image) |
| 234 | return NULL; | 234 | return NULL; |
| 235 | 235 | ||
| 236 | image->head = 0; | ||
| 237 | image->entry = &image->head; | 236 | image->entry = &image->head; |
| 238 | image->last_entry = &image->head; | 237 | image->last_entry = &image->head; |
| 239 | image->control_page = ~0; /* By default this does not apply */ | 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 | image->no_cma = !!(flags & KEXEC_FILE_NO_CMA); | 257 | image->no_cma = !!(flags & KEXEC_FILE_NO_CMA); |
| 258 | image->force_dtb = flags & KEXEC_FILE_FORCE_DTB; | ||
| 258 | 259 | ||
| 259 | if (cmdline_len) { | 260 | if (cmdline_len) { |
| 260 | image->cmdline_buf = memdup_user(cmdline_ptr, cmdline_len); | 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 | return kho_in.fdt_phys ? phys_to_virt(kho_in.fdt_phys) : NULL; | 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 | * kho_retrieve_subtree - retrieve a preserved sub FDT by its name. | 1011 | * kho_retrieve_subtree - retrieve a preserved sub FDT by its name. |
| 992 | * @name: the name of the sub FDT passed to kho_add_subtree(). | 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 | int err = 0; | 1289 | int err = 0; |
| 1270 | struct kexec_buf scratch; | 1290 | struct kexec_buf scratch; |
| 1271 | 1291 | ||
| 1272 | if (!kho_enable) | 1292 | if (!kho_out.finalized) |
| 1273 | return 0; | 1293 | return 0; |
| 1274 | 1294 | ||
| 1275 | image->kho.fdt = page_to_phys(kho_out.ser.fdt); | 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 | #define sysctl_oops_all_cpu_backtrace 0 | 53 | #define sysctl_oops_all_cpu_backtrace 0 |
| 54 | #endif /* CONFIG_SMP */ | 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 | static unsigned long tainted_mask = | 57 | static unsigned long tainted_mask = |
| 58 | IS_ENABLED(CONFIG_RANDSTRUCT) ? (1 << TAINT_RANDSTRUCT) : 0; | 58 | IS_ENABLED(CONFIG_RANDSTRUCT) ? (1 << TAINT_RANDSTRUCT) : 0; |
| 59 | static int pause_on_oops; | 59 | static int pause_on_oops; |
| @@ -67,6 +67,7 @@ static unsigned int warn_limit __read_mostly; · 7 unmodified lines | |||
| 67 | static bool panic_console_replay; | 67 | static bool panic_console_replay; |
| 68 | 68 | ||
| 69 | bool panic_triggering_all_cpu_backtrace; | 69 | bool panic_triggering_all_cpu_backtrace; |
| 70 | static bool panic_this_cpu_backtrace_printed; | ||
| 70 | 71 | ||
| 71 | int panic_timeout = CONFIG_PANIC_TIMEOUT; | 72 | int panic_timeout = CONFIG_PANIC_TIMEOUT; |
| 72 | EXPORT_SYMBOL_GPL(panic_timeout); | 73 | EXPORT_SYMBOL_GPL(panic_timeout); |
| @@ -77,6 +78,11 @@ ATOMIC_NOTIFIER_HEAD(panic_notifier_list); · 4 unmodified lines | |||
| 77 | 78 | ||
| 78 | EXPORT_SYMBOL(panic_notifier_list); | 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 | #ifdef CONFIG_SYSCTL | 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 | static int sysctl_panic_print_handler(const struct ctl_table *table, int write, | 131 | static int sysctl_panic_print_handler(const struct ctl_table *table, int write, |
| 126 | void *buffer, size_t *lenp, loff_t *ppos) | 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 | return proc_doulongvec_minmax(table, write, buffer, lenp, ppos); | 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 | atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID); | 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 | * A variant of panic() called from NMI context. We return if we've already | 357 | * A variant of panic() called from NMI context. We return if we've already |
| 299 | * panicked on this CPU. If another CPU already panicked, loop in | 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 | void nmi_panic(struct pt_regs *regs, const char *msg) | 362 | void nmi_panic(struct pt_regs *regs, const char *msg) |
| 304 | { | 363 | { |
| 305 | int old_cpu, this_cpu; | 364 | if (panic_try_start()) |
| 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)) | ||
| 312 | panic("%s", msg); | 365 | panic("%s", msg); |
| 313 | else if (old_cpu != this_cpu) | 366 | else if (panic_on_other_cpu()) |
| 314 | nmi_panic_self_stop(regs); | 367 | nmi_panic_self_stop(regs); |
| 315 | } | 368 | } |
| 316 | EXPORT_SYMBOL(nmi_panic); | 369 | EXPORT_SYMBOL(nmi_panic); |
| @@ -328,6 +381,19 @@ void check_panic_on_warn(const char *origin) · 11 unmodified lines | |||
| 328 | origin, limit); | 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 | * Helper that triggers the NMI backtrace (if set in panic_print) | 398 | * Helper that triggers the NMI backtrace (if set in panic_print) |
| 333 | * and then performs the secondary CPUs shutdown - we cannot have | 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 | static void panic_other_cpus_shutdown(bool crash_kexec) | 402 | static void panic_other_cpus_shutdown(bool crash_kexec) |
| 337 | { | 403 | { |
| 338 | if (panic_print & SYS_INFO_ALL_CPU_BT) { | 404 | if (panic_print & SYS_INFO_ALL_CPU_BT) |
| 339 | /* Temporary allow non-panic CPUs to write their backtraces. */ | 405 | panic_trigger_all_cpu_backtrace(); |
| 340 | panic_triggering_all_cpu_backtrace = true; | ||
| 341 | trigger_all_cpu_backtrace(); | ||
| 342 | panic_triggering_all_cpu_backtrace = false; | ||
| 343 | } | ||
| 344 | 406 | ||
| 345 | /* | 407 | /* |
| 346 | * Note that smp_send_stop() is the usual SMP shutdown function, | 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 | static char buf[1024]; | 430 | static char buf[1024]; |
| 369 | long i, i_next = 0, len; | 431 | long i, i_next = 0, len; |
| 370 | int state = 0; | 432 | int state = 0; |
| 371 | int old_cpu, this_cpu; | ||
| 372 | bool _crash_kexec_post_notifiers = crash_kexec_post_notifiers; | 433 | bool _crash_kexec_post_notifiers = crash_kexec_post_notifiers; |
| 373 | 434 | ||
| 374 | if (panic_on_warn) { | 435 | if (panic_on_warn) { |
| @@ -405,13 +466,10 @@ void vpanic(const char *fmt, va_list args) · 30 unmodified lines | |||
| 405 | * `old_cpu == this_cpu' means we came from nmi_panic() which sets | 466 | * `old_cpu == this_cpu' means we came from nmi_panic() which sets |
| 406 | * panic_cpu to this CPU. In this case, this is also the 1st CPU. | 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 | /* atomic_try_cmpxchg updates old_cpu on failure */ | 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 | /* go ahead */ | 471 | /* go ahead */ |
| 414 | } else if (old_cpu != this_cpu) | 472 | } else if (panic_on_other_cpu()) |
| 415 | panic_smp_self_stop(); | 473 | panic_smp_self_stop(); |
| 416 | 474 | ||
| 417 | console_verbose(); | 475 | console_verbose(); |
| @@ -422,13 +480,15 @@ void vpanic(const char *fmt, va_list args) · 4 unmodified lines | |||
| 422 | buf[len - 1] = '\0'; | 480 | buf[len - 1] = '\0'; |
| 423 | 481 | ||
| 424 | pr_emerg("Kernel panic - not syncing: %s\n", buf); | 482 | pr_emerg("Kernel panic - not syncing: %s\n", buf); |
| 425 | #ifdef CONFIG_DEBUG_BUGVERBOSE | ||
| 426 | /* | 483 | /* |
| 427 | * Avoid nested stack-dumping if a panic occurs during oops processing | 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 | dump_stack(); | 489 | dump_stack(); |
| 431 | #endif | 490 | panic_this_cpu_backtrace_printed = true; |
| 491 | } | ||
| 432 | 492 | ||
| 433 | /* | 493 | /* |
| 434 | * If kgdb is enabled, give it a chance to run before we stop all | 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 | #endif | 997 | #endif |
| 938 | 998 | ||
| 939 | core_param(panic, panic_timeout, int, 0644); | 999 | core_param(panic, panic_timeout, int, 0644); |
| 940 | core_param(panic_print, panic_print, ulong, 0644); | ||
| 941 | core_param(pause_on_oops, pause_on_oops, int, 0644); | 1000 | core_param(pause_on_oops, pause_on_oops, int, 0644); |
| 942 | core_param(panic_on_warn, panic_on_warn, int, 0644); | 1001 | core_param(panic_on_warn, panic_on_warn, int, 0644); |
| 943 | core_param(crash_kexec_post_notifiers, crash_kexec_post_notifiers, bool, 0644); | 1002 | core_param(crash_kexec_post_notifiers, crash_kexec_post_notifiers, bool, 0644); |
| 944 | core_param(panic_console_replay, panic_console_replay, bool, 0644); | 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 | static int __init oops_setup(char *s) | 1023 | static int __init oops_setup(char *s) |
| 947 | { | 1024 | { |
| 948 | if (!s) | 1025 | if (!s) |
| @@ -332,7 +332,6 @@ struct printk_message { · 331 unmodified lines | |||
| 332 | unsigned long dropped; | 332 | unsigned long dropped; |
| 333 | }; | 333 | }; |
| 334 | 334 | ||
| 335 | bool other_cpu_in_panic(void); | ||
| 336 | bool printk_get_next_message(struct printk_message *pmsg, u64 seq, | 335 | bool printk_get_next_message(struct printk_message *pmsg, u64 seq, |
| 337 | bool is_extended, bool may_supress); | 336 | bool is_extended, bool may_supress); |
| 338 | 337 | ||
| @@ -12,6 +12,7 @@ · 11 unmodified lines | |||
| 12 | #include <linux/irqflags.h> | 12 | #include <linux/irqflags.h> |
| 13 | #include <linux/kthread.h> | 13 | #include <linux/kthread.h> |
| 14 | #include <linux/minmax.h> | 14 | #include <linux/minmax.h> |
| 15 | #include <linux/panic.h> | ||
| 15 | #include <linux/percpu.h> | 16 | #include <linux/percpu.h> |
| 16 | #include <linux/preempt.h> | 17 | #include <linux/preempt.h> |
| 17 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
| @@ -254,7 +255,7 @@ static int nbcon_context_try_acquire_direct(struct nbcon_context *ctxt, · 236 unmodified lines | |||
| 254 | * opportunity to perform any necessary cleanup if they were | 255 | * opportunity to perform any necessary cleanup if they were |
| 255 | * interrupted by the panic CPU while printing. | 256 | * interrupted by the panic CPU while printing. |
| 256 | */ | 257 | */ |
| 257 | if (other_cpu_in_panic() && | 258 | if (panic_on_other_cpu() && |
| 258 | (!is_reacquire || cur->unsafe_takeover)) { | 259 | (!is_reacquire || cur->unsafe_takeover)) { |
| 259 | return -EPERM; | 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 | * Event #2 implies the new context is PANIC. | 310 | * Event #2 implies the new context is PANIC. |
| 310 | * Event #3 occurs when panic() has flushed the console. | 311 | * Event #3 occurs when panic() has flushed the console. |
| 311 | * Event #4 occurs when a non-panic CPU reacquires. | 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 | * in nbcon_context_try_acquire_handover(). | 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 | struct nbcon_state new; | 349 | struct nbcon_state new; |
| 349 | 350 | ||
| 350 | /* Note that the caller must still remove the request! */ | 351 | /* Note that the caller must still remove the request! */ |
| 351 | if (other_cpu_in_panic()) | 352 | if (panic_on_other_cpu()) |
| 352 | return -EPERM; | 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 | * nbcon_waiter_matches(). In particular, the assumption that | 447 | * nbcon_waiter_matches(). In particular, the assumption that |
| 447 | * lower priorities are ignored during panic. | 448 | * lower priorities are ignored during panic. |
| 448 | */ | 449 | */ |
| 449 | if (other_cpu_in_panic()) | 450 | if (panic_on_other_cpu()) |
| 450 | return -EPERM; | 451 | return -EPERM; |
| 451 | 452 | ||
| 452 | /* Handover is not possible on the same CPU. */ | 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 | static bool nbcon_context_try_acquire(struct nbcon_context *ctxt, bool is_reacquire) | 591 | static bool nbcon_context_try_acquire(struct nbcon_context *ctxt, bool is_reacquire) |
| 591 | { | 592 | { |
| 592 | unsigned int cpu = smp_processor_id(); | ||
| 593 | struct console *con = ctxt->console; | 593 | struct console *con = ctxt->console; |
| 594 | struct nbcon_state cur; | 594 | struct nbcon_state cur; |
| 595 | int err; | 595 | int err; |
| @@ -614,7 +614,7 @@ out: · 18 unmodified lines | |||
| 614 | /* Acquire succeeded. */ | 614 | /* Acquire succeeded. */ |
| 615 | 615 | ||
| 616 | /* Assign the appropriate buffer for this context. */ | 616 | /* Assign the appropriate buffer for this context. */ |
| 617 | if (atomic_read(&panic_cpu) == cpu) | 617 | if (panic_on_this_cpu()) |
| 618 | ctxt->pbufs = &panic_nbcon_pbufs; | 618 | ctxt->pbufs = &panic_nbcon_pbufs; |
| 619 | else | 619 | else |
| 620 | ctxt->pbufs = con->pbufs; | 620 | ctxt->pbufs = con->pbufs; |
| @@ -1394,7 +1394,7 @@ enum nbcon_prio nbcon_get_default_prio(void) · 773 unmodified lines | |||
| 1394 | { | 1394 | { |
| 1395 | unsigned int *cpu_emergency_nesting; | 1395 | unsigned int *cpu_emergency_nesting; |
| 1396 | 1396 | ||
| 1397 | if (this_cpu_in_panic()) | 1397 | if (panic_on_this_cpu()) |
| 1398 | return NBCON_PRIO_PANIC; | 1398 | return NBCON_PRIO_PANIC; |
| 1399 | 1399 | ||
| 1400 | cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting(); | 1400 | cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting(); |
| @@ -48,6 +48,7 @@ · 47 unmodified lines | |||
| 48 | #include <linux/sched/clock.h> | 48 | #include <linux/sched/clock.h> |
| 49 | #include <linux/sched/debug.h> | 49 | #include <linux/sched/debug.h> |
| 50 | #include <linux/sched/task_stack.h> | 50 | #include <linux/sched/task_stack.h> |
| 51 | #include <linux/panic.h> | ||
| 51 | 52 | ||
| 52 | #include <linux/uaccess.h> | 53 | #include <linux/uaccess.h> |
| 53 | #include <asm/sections.h> | 54 | #include <asm/sections.h> |
| @@ -345,34 +346,6 @@ static void __up_console_sem(unsigned long ip) · 291 unmodified lines | |||
| 345 | } | 346 | } |
| 346 | #define up_console_sem() __up_console_sem(_RET_IP_) | 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 | * This is used for debugging the mess that is the VT code by | 350 | * This is used for debugging the mess that is the VT code by |
| 378 | * keeping track if we have the console semaphore held. It's | 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 | * non-panic CPUs are generating any messages, they will be | 2380 | * non-panic CPUs are generating any messages, they will be |
| 2408 | * silently dropped. | 2381 | * silently dropped. |
| 2409 | */ | 2382 | */ |
| 2410 | if (other_cpu_in_panic() && | 2383 | if (panic_on_other_cpu() && |
| 2411 | !debug_non_panic_cpus && | 2384 | !debug_non_panic_cpus && |
| 2412 | !panic_triggering_all_cpu_backtrace) | 2385 | !panic_triggering_all_cpu_backtrace) |
| 2413 | return 0; | 2386 | return 0; |
| @@ -2843,7 +2816,7 @@ void console_lock(void) · 429 unmodified lines | |||
| 2843 | might_sleep(); | 2816 | might_sleep(); |
| 2844 | 2817 | ||
| 2845 | /* On panic, the console_lock must be left to the panic cpu. */ | 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 | msleep(1000); | 2820 | msleep(1000); |
| 2848 | 2821 | ||
| 2849 | down_console_sem(); | 2822 | down_console_sem(); |
| @@ -2863,7 +2836,7 @@ EXPORT_SYMBOL(console_lock); · 13 unmodified lines | |||
| 2863 | int console_trylock(void) | 2836 | int console_trylock(void) |
| 2864 | { | 2837 | { |
| 2865 | /* On panic, the console_lock must be left to the panic cpu. */ | 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 | return 0; | 2840 | return 0; |
| 2868 | if (down_trylock_console_sem()) | 2841 | if (down_trylock_console_sem()) |
| 2869 | return 0; | 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 | any_progress = true; | 3216 | any_progress = true; |
| 3244 | 3217 | ||
| 3245 | /* Allow panic_cpu to take over the consoles safely. */ | 3218 | /* Allow panic_cpu to take over the consoles safely. */ |
| 3246 | if (other_cpu_in_panic()) | 3219 | if (panic_on_other_cpu()) |
| 3247 | goto abandon; | 3220 | goto abandon; |
| 3248 | 3221 | ||
| 3249 | if (do_cond_resched) | 3222 | if (do_cond_resched) |
| @@ -2143,7 +2143,7 @@ static bool _prb_read_valid(struct printk_ringbuffer *rb, u64 *seq, · 2142 unmodified lines | |||
| 2143 | * But it would have the sequence number returned | 2143 | * But it would have the sequence number returned |
| 2144 | * by "prb_next_reserve_seq() - 1". | 2144 | * by "prb_next_reserve_seq() - 1". |
| 2145 | */ | 2145 | */ |
| 2146 | if (this_cpu_in_panic() && | 2146 | if (panic_on_this_cpu() && |
| 2147 | (!debug_non_panic_cpus || legacy_allow_panic_sync) && | 2147 | (!debug_non_panic_cpus || legacy_allow_panic_sync) && |
| 2148 | ((*seq + 1) < prb_next_reserve_seq(rb))) { | 2148 | ((*seq + 1) < prb_next_reserve_seq(rb))) { |
| 2149 | (*seq)++; | 2149 | (*seq)++; |
| @@ -1734,6 +1734,7 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource, · 1733 unmodified lines | |||
| 1734 | struct rlimit old, new; | 1734 | struct rlimit old, new; |
| 1735 | struct task_struct *tsk; | 1735 | struct task_struct *tsk; |
| 1736 | unsigned int checkflags = 0; | 1736 | unsigned int checkflags = 0; |
| 1737 | bool need_tasklist; | ||
| 1737 | int ret; | 1738 | int ret; |
| 1738 | 1739 | ||
| 1739 | if (old_rlim) | 1740 | if (old_rlim) |
| @@ -1760,8 +1761,25 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource, · 20 unmodified lines | |||
| 1760 | get_task_struct(tsk); | 1761 | get_task_struct(tsk); |
| 1761 | rcu_read_unlock(); | 1762 | rcu_read_unlock(); |
| 1762 | 1763 | ||
| 1763 | ret = do_prlimit(tsk, resource, new_rlim ? &new : NULL, | 1764 | need_tasklist = !same_thread_group(tsk, current); |
| 1764 | old_rlim ? &old : NULL); | 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 | if (!ret && old_rlim) { | 1784 | if (!ret && old_rlim) { |
| 1767 | rlim_to_rlim64(&old, &old64); | 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 | error = -EINVAL; | 2533 | error = -EINVAL; |
| 2516 | break; | 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 | me->pdeath_signal = arg2; | 2545 | me->pdeath_signal = arg2; |
| 2546 | read_unlock(&tasklist_lock); | ||
| 2519 | break; | 2547 | break; |
| 2520 | case PR_GET_PDEATHSIG: | 2548 | case PR_GET_PDEATHSIG: |
| 2521 | error = put_user(me->pdeath_signal, (int __user *)arg2); | 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 | static u16 get_16bit_precision(u64 data_ns) | 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 | static void update_cpustat(void) | 435 | static void update_cpustat(void) |
| @@ -444,6 +448,14 @@ static void update_cpustat(void) · 12 unmodified lines | |||
| 444 | old_stat = __this_cpu_read(cpustat_old[i]); | 448 | old_stat = __this_cpu_read(cpustat_old[i]); |
| 445 | new_stat = get_16bit_precision(cpustat[tracked_stats[i]]); | 449 | new_stat = get_16bit_precision(cpustat[tracked_stats[i]]); |
| 446 | util = DIV_ROUND_UP(100 * (new_stat - old_stat), sample_period_16); | 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 | __this_cpu_write(cpustat_util[tail][i], util); | 459 | __this_cpu_write(cpustat_util[tail][i], util); |
| 448 | __this_cpu_write(cpustat_old[i], new_stat); | 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 | int i, group; | 468 | int i, group; |
| 457 | u8 tail = __this_cpu_read(cpustat_tail); | 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 | * Outputting the "watchdog" prefix on every line is redundant and not | 475 | * Outputting the "watchdog" prefix on every line is redundant and not |
| 464 | * concise, and the original alarm information is sufficient for | 476 | * concise, and the original alarm information is sufficient for |
| 465 | * positioning in logs, hence here printk() is used instead of pr_crit(). | 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", | 479 | printk(KERN_CRIT "CPU#%d Utilization every %llums during lockup:\n", |
| 468 | smp_processor_id(), sample_period_second); | 480 | smp_processor_id(), sample_period_msecond); |
| 469 | 481 | ||
| 470 | for (i = 0; i < NUM_SAMPLE_PERIODS; i++) { | 482 | for (i = 0; i < NUM_SAMPLE_PERIODS; i++) { |
| 471 | group = (tail + i) % NUM_SAMPLE_PERIODS; | 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 | if (!watchdog_enabled) | 752 | if (!watchdog_enabled) |
| 741 | return HRTIMER_NORESTART; | 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 | watchdog_hardlockup_kick(); | 761 | watchdog_hardlockup_kick(); |
| 744 | 762 | ||
| 745 | /* kick the softlockup detector */ | 763 | /* kick the softlockup detector */ |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | |||
| 12 | 12 | ||
| 13 | #define pr_fmt(fmt) "NMI watchdog: " fmt | 13 | #define pr_fmt(fmt) "NMI watchdog: " fmt |
| 14 | 14 | ||
| 15 | #include <linux/panic.h> | ||
| 15 | #include <linux/nmi.h> | 16 | #include <linux/nmi.h> |
| 16 | #include <linux/atomic.h> | 17 | #include <linux/atomic.h> |
| 17 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| @@ -108,6 +109,9 @@ static void watchdog_overflow_callback(struct perf_event *event, · 90 unmodified lines | |||
| 108 | /* Ensure the watchdog never gets throttled */ | 109 | /* Ensure the watchdog never gets throttled */ |
| 109 | event->hw.interrupts = 0; | 110 | event->hw.interrupts = 0; |
| 110 | 111 | ||
| 112 | if (panic_in_progress()) | ||
| 113 | return; | ||
| 114 | |||
| 111 | if (!watchdog_check_timestamp()) | 115 | if (!watchdog_check_timestamp()) |
| 112 | return; | 116 | return; |
| 113 | 117 | ||
| @@ -1067,12 +1067,6 @@ config PANIC_ON_OOPS · 1066 unmodified lines | |||
| 1067 | 1067 | ||
| 1068 | Say N if unsure. | 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 | config PANIC_TIMEOUT | 1070 | config PANIC_TIMEOUT |
| 1077 | int "panic timeout" | 1071 | int "panic timeout" |
| 1078 | default 0 | 1072 | default 0 |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | |||
| 9 | #include <linux/proc_fs.h> | 9 | #include <linux/proc_fs.h> |
| 10 | #include <linux/seq_buf.h> | 10 | #include <linux/seq_buf.h> |
| 11 | #include <linux/seq_file.h> | 11 | #include <linux/seq_file.h> |
| 12 | #include <linux/string_choices.h> | ||
| 12 | #include <linux/vmalloc.h> | 13 | #include <linux/vmalloc.h> |
| 13 | #include <linux/kmemleak.h> | 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 | mem_profiling_support = true; | 730 | mem_profiling_support = true; |
| 730 | pr_info("Memory allocation profiling is enabled %s compression and is turned %s!\n", | 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 | if (enable != mem_alloc_profiling_enabled()) { | 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 | * walks to remove a single object from the victim. | 653 | * walks to remove a single object from the victim. |
| 654 | */ | 654 | */ |
| 655 | for (;;) { | 655 | for (;;) { |
| 656 | if (!btree_last(victim, geo, key)) | 656 | val = btree_last(victim, geo, key); |
| 657 | if (!val) | ||
| 657 | break; | 658 | break; |
| 658 | val = btree_lookup(victim, geo, key); | ||
| 659 | err = btree_insert(target, geo, key, val, gfp); | 659 | err = btree_insert(target, geo, key, val, gfp); |
| 660 | if (err) | 660 | if (err) |
| 661 | return err; | 661 | return err; |
| @@ -49,15 +49,15 @@ struct compress_format { · 48 unmodified lines | |||
| 49 | }; | 49 | }; |
| 50 | 50 | ||
| 51 | static const struct compress_format compressed_formats[] __initconst = { | 51 | static const struct compress_format compressed_formats[] __initconst = { |
| 52 | { {0x1f, 0x8b}, "gzip", gunzip }, | 52 | { .magic = {0x1f, 0x8b}, .name = "gzip", .decompressor = gunzip }, |
| 53 | { {0x1f, 0x9e}, "gzip", gunzip }, | 53 | { .magic = {0x1f, 0x9e}, .name = "gzip", .decompressor = gunzip }, |
| 54 | { {0x42, 0x5a}, "bzip2", bunzip2 }, | 54 | { .magic = {0x42, 0x5a}, .name = "bzip2", .decompressor = bunzip2 }, |
| 55 | { {0x5d, 0x00}, "lzma", unlzma }, | 55 | { .magic = {0x5d, 0x00}, .name = "lzma", .decompressor = unlzma }, |
| 56 | { {0xfd, 0x37}, "xz", unxz }, | 56 | { .magic = {0xfd, 0x37}, .name = "xz", .decompressor = unxz }, |
| 57 | { {0x89, 0x4c}, "lzo", unlzo }, | 57 | { .magic = {0x89, 0x4c}, .name = "lzo", .decompressor = unlzo }, |
| 58 | { {0x02, 0x21}, "lz4", unlz4 }, | 58 | { .magic = {0x02, 0x21}, .name = "lz4", .decompressor = unlz4 }, |
| 59 | { {0x28, 0xb5}, "zstd", unzstd }, | 59 | { .magic = {0x28, 0xb5}, .name = "zstd", .decompressor = unzstd }, |
| 60 | { {0, 0}, NULL, NULL } | 60 | { /* sentinel */ } |
| 61 | }; | 61 | }; |
| 62 | 62 | ||
| 63 | decompress_fn __init decompress_method(const unsigned char *inbuf, long len, | 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 | pr_debug("Compressed data magic: %#.2x %#.2x\n", inbuf[0], inbuf[1]); | 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 | if (!memcmp(inbuf, cf->magic, 2)) | 77 | if (!memcmp(inbuf, cf->magic, 2)) |
| 78 | break; | 78 | break; |
| 79 | 79 | ||
| 80 | } | ||
| 81 | if (name) | 80 | if (name) |
| 82 | *name = cf->name; | 81 | *name = cf->name; |
| 83 | return cf->decompressor; | 82 | return cf->decompressor; |
| @@ -159,7 +159,6 @@ static int digsig_verify_rsa(struct key *key, · 158 unmodified lines | |||
| 159 | 159 | ||
| 160 | len = mlen; | 160 | len = mlen; |
| 161 | head = len - l; | 161 | head = len - l; |
| 162 | memset(out1, 0, head); | ||
| 163 | memcpy(out1 + head, p, l); | 162 | memcpy(out1 + head, p, l); |
| 164 | 163 | ||
| 165 | kfree(p); | 164 | kfree(p); |
| @@ -102,7 +102,7 @@ static void __dump_stack(const char *log_lvl) · 101 unmodified lines | |||
| 102 | */ | 102 | */ |
| 103 | asmlinkage __visible void dump_stack_lvl(const char *log_lvl) | 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 | unsigned long flags; | 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 | dir = fault_create_debugfs_attr("fail_usercopy", NULL, | 23 | dir = fault_create_debugfs_attr("fail_usercopy", NULL, |
| 24 | &fail_usercopy.attr); | 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 | late_initcall(fail_usercopy_debugfs); | 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 | if (!name) | 899 | if (!name) |
| 900 | name = of_node_full_name(np_pool); | 900 | name = of_node_full_name(np_pool); |
| 901 | } | 901 | } |
| 902 | if (pdev) | 902 | if (pdev) { |
| 903 | pool = gen_pool_get(&pdev->dev, name); | 903 | pool = gen_pool_get(&pdev->dev, name); |
| 904 | put_device(&pdev->dev); | ||
| 905 | } | ||
| 906 | |||
| 904 | of_node_put(np_pool); | 907 | of_node_put(np_pool); |
| 905 | 908 | ||
| 906 | return pool; | 909 | return pool; |
| @@ -75,7 +75,7 @@ ref_tracker_get_stats(struct ref_tracker_dir *dir, unsigned int limit) · 74 unmodified lines | |||
| 75 | struct ref_tracker *tracker; | 75 | struct ref_tracker *tracker; |
| 76 | 76 | ||
| 77 | stats = kmalloc(struct_size(stats, stacks, limit), | 77 | stats = kmalloc(struct_size(stats, stacks, limit), |
| 78 | GFP_NOWAIT | __GFP_NOWARN); | 78 | GFP_NOWAIT); |
| 79 | if (!stats) | 79 | if (!stats) |
| 80 | return ERR_PTR(-ENOMEM); | 80 | return ERR_PTR(-ENOMEM); |
| 81 | stats->total = 0; | 81 | stats->total = 0; |
| @@ -159,7 +159,7 @@ __ref_tracker_dir_pr_ostream(struct ref_tracker_dir *dir, · 77 unmodified lines | |||
| 159 | return; | 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 | for (i = 0, skipped = stats->total; i < stats->count; ++i) { | 164 | for (i = 0, skipped = stats->total; i < stats->count; ++i) { |
| 165 | stack = stats->stacks[i].stack_handle; | 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 | nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1); | 307 | nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1); |
| 308 | stack_handle = stack_depot_save(entries, nr_entries, | 308 | stack_handle = stack_depot_save(entries, nr_entries, |
| 309 | GFP_NOWAIT | __GFP_NOWARN); | 309 | GFP_NOWAIT); |
| 310 | 310 | ||
| 311 | spin_lock_irqsave(&dir->lock, flags); | 311 | spin_lock_irqsave(&dir->lock, flags); |
| 312 | if (tracker->dead) { | 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 | void *buffer, size_t *lenp, | 55 | void *buffer, size_t *lenp, |
| 56 | loff_t *ppos) | 56 | loff_t *ppos) |
| 57 | { | 57 | { |
| 58 | char names[sizeof(sys_info_avail) + 1]; | 58 | char names[sizeof(sys_info_avail)]; |
| 59 | struct ctl_table table; | 59 | struct ctl_table table; |
| 60 | unsigned long *si_bits_global; | 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 | char *delim = ""; | 81 | char *delim = ""; |
| 82 | int i, len = 0; | 82 | int i, len = 0; |
| 83 | 83 | ||
| 84 | names[0] = '\0'; | ||
| 84 | for (i = 0; i < ARRAY_SIZE(si_names); i++) { | 85 | for (i = 0; i < ARRAY_SIZE(si_names); i++) { |
| 85 | if (*si_bits_global & si_names[i].bit) { | 86 | if (*si_bits_global & si_names[i].bit) { |
| 86 | len += scnprintf(names + len, sizeof(names) - len, | 87 | len += scnprintf(names + len, sizeof(names) - len, |
| @@ -26,6 +26,7 @@ · 25 unmodified lines | |||
| 26 | #include <linux/kthread.h> | 26 | #include <linux/kthread.h> |
| 27 | #include <linux/vmalloc.h> | 27 | #include <linux/vmalloc.h> |
| 28 | #include <linux/efi_embedded_fw.h> | 28 | #include <linux/efi_embedded_fw.h> |
| 29 | #include <linux/string_choices.h> | ||
| 29 | 30 | ||
| 30 | MODULE_IMPORT_NS("TEST_FIRMWARE"); | 31 | MODULE_IMPORT_NS("TEST_FIRMWARE"); |
| 31 | 32 | ||
| @@ -304,17 +305,17 @@ static ssize_t config_show(struct device *dev, · 272 unmodified lines | |||
| 304 | "FW_ACTION_NOUEVENT"); | 305 | "FW_ACTION_NOUEVENT"); |
| 305 | len += scnprintf(buf + len, PAGE_SIZE - len, | 306 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 306 | "into_buf:\t\t%s\n", | 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 | len += scnprintf(buf + len, PAGE_SIZE - len, | 309 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 309 | "buf_size:\t%zu\n", test_fw_config->buf_size); | 310 | "buf_size:\t%zu\n", test_fw_config->buf_size); |
| 310 | len += scnprintf(buf + len, PAGE_SIZE - len, | 311 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 311 | "file_offset:\t%zu\n", test_fw_config->file_offset); | 312 | "file_offset:\t%zu\n", test_fw_config->file_offset); |
| 312 | len += scnprintf(buf + len, PAGE_SIZE - len, | 313 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 313 | "partial:\t\t%s\n", | 314 | "partial:\t\t%s\n", |
| 314 | test_fw_config->partial ? "true" : "false"); | 315 | str_true_false(test_fw_config->partial)); |
| 315 | len += scnprintf(buf + len, PAGE_SIZE - len, | 316 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 316 | "sync_direct:\t\t%s\n", | 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 | len += scnprintf(buf + len, PAGE_SIZE - len, | 319 | len += scnprintf(buf + len, PAGE_SIZE - len, |
| 319 | "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx); | 320 | "read_fw_idx:\t%u\n", test_fw_config->read_fw_idx); |
| 320 | if (test_fw_config->upload_name) | 321 | if (test_fw_config->upload_name) |
| @@ -2636,6 +2636,11 @@ sub exclude_global_initialisers { · 2635 unmodified lines | |||
| 2636 | $realfile =~ m@/bpf/.*\.bpf\.c$@; | 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 | sub process { | 2644 | sub process { |
| 2640 | my $filename = shift; | 2645 | my $filename = shift; |
| 2641 | 2646 | ||
| @@ -3294,7 +3299,7 @@ sub process { · 652 unmodified lines | |||
| 3294 | # file delta changes | 3299 | # file delta changes |
| 3295 | $line =~ /^\s*(?:[\w\.\-\+]*\/)++[\w\.\-\+]+:/ || | 3300 | $line =~ /^\s*(?:[\w\.\-\+]*\/)++[\w\.\-\+]+:/ || |
| 3296 | # filename then : | 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 | # A Fixes:, link or signature tag line | 3303 | # A Fixes:, link or signature tag line |
| 3299 | $commit_log_possible_stack_dump)) { | 3304 | $commit_log_possible_stack_dump)) { |
| 3300 | WARN("COMMIT_LOG_LONG_LINE", | 3305 | WARN("COMMIT_LOG_LONG_LINE", |
| @@ -7018,21 +7023,20 @@ sub process { · 3717 unmodified lines | |||
| 7018 | # } | 7023 | # } |
| 7019 | # } | 7024 | # } |
| 7020 | # } | 7025 | # } |
| 7021 | |||
| 7022 | # strcpy uses that should likely be strscpy | 7026 | # strcpy uses that should likely be strscpy |
| 7023 | if ($line =~ /\bstrcpy\s*\(/) { | 7027 | if ($line =~ /\bstrcpy\s*\(/ && !is_userspace($realfile)) { |
| 7024 | WARN("STRCPY", | 7028 | WARN("STRCPY", |
| 7025 | "Prefer strscpy over strcpy - see: https://github.com/KSPP/linux/issues/88\n" . $herecurr); | 7029 | "Prefer strscpy over strcpy - see: https://github.com/KSPP/linux/issues/88\n" . $herecurr); |
| 7026 | } | 7030 | } |
| 7027 | 7031 | ||
| 7028 | # strlcpy uses that should likely be strscpy | 7032 | # strlcpy uses that should likely be strscpy |
| 7029 | if ($line =~ /\bstrlcpy\s*\(/) { | 7033 | if ($line =~ /\bstrlcpy\s*\(/ && !is_userspace($realfile)) { |
| 7030 | WARN("STRLCPY", | 7034 | WARN("STRLCPY", |
| 7031 | "Prefer strscpy over strlcpy - see: https://github.com/KSPP/linux/issues/89\n" . $herecurr); | 7035 | "Prefer strscpy over strlcpy - see: https://github.com/KSPP/linux/issues/89\n" . $herecurr); |
| 7032 | } | 7036 | } |
| 7033 | 7037 | ||
| 7034 | # strncpy uses that should likely be strscpy or strscpy_pad | 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 | WARN("STRNCPY", | 7040 | WARN("STRNCPY", |
| 7037 | "Prefer strscpy, strscpy_pad, or __nonstring over strncpy - see: https://github.com/KSPP/linux/issues/90\n" . $herecurr); | 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 | virtual report | 10 | virtual report |
| 11 | 11 | ||
| 12 | @match1@ | 12 | @match1@ |
| 13 | declarer name builtin_i2c_driver; | ||
| 14 | declarer name builtin_platform_driver; | ||
| 15 | declarer name builtin_platform_driver_probe; | ||
| 13 | declarer name module_i2c_driver; | 16 | declarer name module_i2c_driver; |
| 14 | declarer name module_platform_driver; | 17 | declarer name module_platform_driver; |
| 15 | declarer name module_platform_driver_probe; | 18 | declarer name module_platform_driver_probe; |
| 16 | identifier __driver; | 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 | module_i2c_driver(__driver); | 28 | module_i2c_driver(__driver); |
| 20 | | | 29 | | |
| 21 | module_platform_driver(__driver); | 30 | module_platform_driver(__driver); |
| @@ -1,5 +1,5 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 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 | // Keywords: of_table i2c_table platform_table | 4 | // Keywords: of_table i2c_table platform_table |
| 5 | // Confidence: Medium | 5 | // Confidence: Medium |
| @@ -15,14 +15,14 @@ identifier var, arr; · 9 unmodified lines | |||
| 15 | expression E; | 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 | .var = E, | 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 | * { ..., E, ... }, | 27 | * { ..., E, ... }, |
| 28 | }; | 28 | }; |
| @@ -33,7 +33,7 @@ identifier var, arr; · 4 unmodified lines | |||
| 33 | expression E; | 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 | .var = E, | 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 | { ..., E, ... }, | 47 | { ..., E, ... }, |
| 48 | + { }, | 48 | + { }, |
| @@ -55,7 +55,7 @@ identifier var, arr; · 6 unmodified lines | |||
| 55 | expression E; | 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 | .var = E, | 61 | .var = E, |
| @@ -63,7 +63,7 @@ struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { · 1 unmodified line | |||
| 63 | @p1 | 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 | { ..., E, ... } | 68 | { ..., E, ... } |
| 69 | @p1 | 69 | @p1 |
| @@ -42,14 +42,13 @@ · 41 unmodified lines | |||
| 42 | #include <linux/genetlink.h> | 42 | #include <linux/genetlink.h> |
| 43 | #include <linux/taskstats.h> | 43 | #include <linux/taskstats.h> |
| 44 | #include <linux/cgroupstats.h> | 44 | #include <linux/cgroupstats.h> |
| 45 | #include <stddef.h> | ||
| 45 | 46 | ||
| 46 | #define PSI_CPU_SOME "/proc/pressure/cpu" | 47 | #define PSI_PATH "/proc/pressure" |
| 47 | #define PSI_CPU_FULL "/proc/pressure/cpu" | 48 | #define PSI_CPU_PATH "/proc/pressure/cpu" |
| 48 | #define PSI_MEMORY_SOME "/proc/pressure/memory" | 49 | #define PSI_MEMORY_PATH "/proc/pressure/memory" |
| 49 | #define PSI_MEMORY_FULL "/proc/pressure/memory" | 50 | #define PSI_IO_PATH "/proc/pressure/io" |
| 50 | #define PSI_IO_SOME "/proc/pressure/io" | 51 | #define PSI_IRQ_PATH "/proc/pressure/irq" |
| 51 | #define PSI_IO_FULL "/proc/pressure/io" | ||
| 52 | #define PSI_IRQ_FULL "/proc/pressure/irq" | ||
| 53 | 52 | ||
| 54 | #define NLA_NEXT(na) ((struct nlattr *)((char *)(na) + NLA_ALIGN((na)->nla_len))) | 53 | #define NLA_NEXT(na) ((struct nlattr *)((char *)(na) + NLA_ALIGN((na)->nla_len))) |
| 55 | #define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN)) | 54 | #define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN)) |
| @@ -61,24 +60,28 @@ · 5 unmodified lines | |||
| 61 | #define TASK_COMM_LEN 16 | 60 | #define TASK_COMM_LEN 16 |
| 62 | #define MAX_MSG_SIZE 1024 | 61 | #define MAX_MSG_SIZE 1024 |
| 63 | #define MAX_TASKS 1000 | 62 | #define MAX_TASKS 1000 |
| 63 | #define MAX_BUF_LEN 256 | ||
| 64 | #define SET_TASK_STAT(task_count, field) tasks[task_count].field = stats.field | 64 | #define SET_TASK_STAT(task_count, field) tasks[task_count].field = stats.field |
| 65 | #define BOOL_FPRINT(stream, fmt, ...) \ | 65 | #define BOOL_FPRINT(stream, fmt, ...) \ |
| 66 | ({ \ | 66 | ({ \ |
| 67 | int ret = fprintf(stream, fmt, ##__VA_ARGS__); \ | 67 | int ret = fprintf(stream, fmt, ##__VA_ARGS__); \ |
| 68 | ret >= 0; \ | 68 | ret >= 0; \ |
| 69 | }) | 69 | }) |
| 70 | #define TASK_AVG(task, field) average_ms((task).field##_delay_total, (task).field##_count) | ||
| 70 | #define PSI_LINE_FORMAT "%-12s %6.1f%%/%6.1f%%/%6.1f%%/%8llu(ms)\n" | 71 | #define PSI_LINE_FORMAT "%-12s %6.1f%%/%6.1f%%/%6.1f%%/%8llu(ms)\n" |
| 71 | 72 | #define DELAY_FMT_DEFAULT "%8.2f %8.2f %8.2f %8.2f\n" | |
| 72 | /* Program settings structure */ | 73 | #define DELAY_FMT_MEMVERBOSE "%8.2f %8.2f %8.2f %8.2f %8.2f %8.2f\n" |
| 73 | struct config { | 74 | #define SORT_FIELD(name, cmd, modes) \ |
| 74 | int delay; /* Update interval in seconds */ | 75 | {#name, #cmd, \ |
| 75 | int iterations; /* Number of iterations, 0 == infinite */ | 76 | offsetof(struct task_info, name##_delay_total), \ |
| 76 | int max_processes; /* Maximum number of processes to show */ | 77 | offsetof(struct task_info, name##_count), \ |
| 77 | char sort_field; /* Field to sort by */ | 78 | modes} |
| 78 | int output_one_time; /* Output once and exit */ | 79 | #define END_FIELD {NULL, 0, 0} |
| 79 | int monitor_pid; /* Monitor specific PID */ | 80 | |
| 80 | char *container_path; /* Path to container cgroup */ | 81 | /* Display mode types */ |
| 81 | }; | 82 | #define MODE_TYPE_ALL (0xFFFFFFFF) |
| 83 | #define MODE_DEFAULT (1 << 0) | ||
| 84 | #define MODE_MEMVERBOSE (1 << 1) | ||
| 82 | 85 | ||
| 83 | /* PSI statistics structure */ | 86 | /* PSI statistics structure */ |
| 84 | struct psi_stats { | 87 | struct psi_stats { |
| @@ -119,6 +122,8 @@ struct task_info { · 34 unmodified lines | |||
| 119 | unsigned long long wpcopy_delay_total; | 122 | unsigned long long wpcopy_delay_total; |
| 120 | unsigned long long irq_count; | 123 | unsigned long long irq_count; |
| 121 | unsigned long long irq_delay_total; | 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 | /* Container statistics structure */ | 129 | /* Container statistics structure */ |
| @@ -130,6 +135,27 @@ struct container_stats { · 5 unmodified lines | |||
| 130 | int nr_io_wait; /* Number of processes in IO wait */ | 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 | /* Global variables */ | 159 | /* Global variables */ |
| 134 | static struct config cfg; | 160 | static struct config cfg; |
| 135 | static struct psi_stats psi; | 161 | static struct psi_stats psi; |
| @@ -137,6 +163,19 @@ static struct task_info tasks[MAX_TASKS]; · 1 unmodified line | |||
| 137 | static int task_count; | 163 | static int task_count; |
| 138 | static int running = 1; | 164 | static int running = 1; |
| 139 | static struct container_stats container_stats; | 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 | /* Netlink socket variables */ | 180 | /* Netlink socket variables */ |
| 142 | static int nl_sd = -1; | 181 | static int nl_sd = -1; |
| @@ -158,18 +197,75 @@ static void disable_raw_mode(void) · 15 unmodified lines | |||
| 158 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); | 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 | /* Display usage information and command line options */ | 255 | /* Display usage information and command line options */ |
| 162 | static void usage(void) | 256 | static void usage(void) |
| 163 | { | 257 | { |
| 164 | printf("Usage: delaytop [Options]\n" | 258 | printf("Usage: delaytop [Options]\n" |
| 165 | "Options:\n" | 259 | "Options:\n" |
| 166 | " -h, --help Show this help message and exit\n" | 260 | " -h, --help Show this help message and exit\n" |
| 167 | " -d, --delay=SECONDS Set refresh interval (default: 2 seconds, min: 1)\n" | 261 | " -d, --delay=SECONDS Set refresh interval (default: 2 seconds, min: 1)\n" |
| 168 | " -n, --iterations=COUNT Set number of updates (default: 0 = infinite)\n" | 262 | " -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" | 263 | " -P, --processes=NUMBER Set maximum number of processes to show (default: 20, max: 1000)\n" |
| 170 | " -o, --once Display once and exit\n" | 264 | " -o, --once Display once and exit\n" |
| 171 | " -p, --pid=PID Monitor only the specified PID\n" | 265 | " -p, --pid=PID Monitor only the specified PID\n" |
| 172 | " -C, --container=PATH Monitor the container at specified cgroup path\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 | exit(0); | 269 | exit(0); |
| 174 | } | 270 | } |
| 175 | 271 | ||
| @@ -177,6 +273,7 @@ static void usage(void) · 1 unmodified line | |||
| 177 | static void parse_args(int argc, char **argv) | 273 | static void parse_args(int argc, char **argv) |
| 178 | { | 274 | { |
| 179 | int c; | 275 | int c; |
| 276 | const struct field_desc *field; | ||
| 180 | struct option long_options[] = { | 277 | struct option long_options[] = { |
| 181 | {"help", no_argument, 0, 'h'}, | 278 | {"help", no_argument, 0, 'h'}, |
| 182 | {"delay", required_argument, 0, 'd'}, | 279 | {"delay", required_argument, 0, 'd'}, |
| @@ -184,7 +281,9 @@ static void parse_args(int argc, char **argv) · 1 unmodified line | |||
| 184 | {"pid", required_argument, 0, 'p'}, | 281 | {"pid", required_argument, 0, 'p'}, |
| 185 | {"once", no_argument, 0, 'o'}, | 282 | {"once", no_argument, 0, 'o'}, |
| 186 | {"processes", required_argument, 0, 'P'}, | 283 | {"processes", required_argument, 0, 'P'}, |
| 284 | {"sort", required_argument, 0, 's'}, | ||
| 187 | {"container", required_argument, 0, 'C'}, | 285 | {"container", required_argument, 0, 'C'}, |
| 286 | {"memverbose", no_argument, 0, 'M'}, | ||
| 188 | {0, 0, 0, 0} | 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 | cfg.delay = 2; | 291 | cfg.delay = 2; |
| 193 | cfg.iterations = 0; | 292 | cfg.iterations = 0; |
| 194 | cfg.max_processes = 20; | 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 | cfg.output_one_time = 0; | 295 | cfg.output_one_time = 0; |
| 197 | cfg.monitor_pid = 0; /* 0 means monitor all PIDs */ | 296 | cfg.monitor_pid = 0; /* 0 means monitor all PIDs */ |
| 198 | cfg.container_path = NULL; | 297 | cfg.container_path = NULL; |
| 298 | cfg.display_mode = MODE_DEFAULT; | ||
| 199 | 299 | ||
| 200 | while (1) { | 300 | while (1) { |
| 201 | int option_index = 0; | 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 | if (c == -1) | 304 | if (c == -1) |
| 205 | break; | 305 | break; |
| 206 | 306 | ||
| @@ -247,6 +347,26 @@ static void parse_args(int argc, char **argv) · 40 unmodified lines | |||
| 247 | case 'C': | 347 | case 'C': |
| 248 | cfg.container_path = strdup(optarg); | 348 | cfg.container_path = strdup(optarg); |
| 249 | break; | 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 | default: | 370 | default: |
| 251 | fprintf(stderr, "Try 'delaytop --help' for more information.\n"); | 371 | fprintf(stderr, "Try 'delaytop --help' for more information.\n"); |
| 252 | exit(1); | 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 | /* Create a raw netlink socket and bind */ | 396 | /* Create a raw netlink socket and bind */ |
| 258 | static int create_nl_socket(void) | 397 | static int create_nl_socket(void) |
| 259 | { | 398 | { |
| @@ -358,87 +497,134 @@ static int get_family_id(int sd) · 98 unmodified lines | |||
| 358 | return id; | 497 | return id; |
| 359 | } | 498 | } |
| 360 | 499 | ||
| 361 | static void read_psi_stats(void) | 500 | static int read_psi_stats(void) |
| 362 | { | 501 | { |
| 363 | FILE *fp; | 502 | FILE *fp; |
| 364 | char line[256]; | 503 | char line[256]; |
| 365 | int ret = 0; | 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 | /* Zero all fields */ | 514 | /* Zero all fields */ |
| 367 | memset(&psi, 0, sizeof(psi)); | 515 | memset(&psi, 0, sizeof(psi)); |
| 516 | |||
| 368 | /* CPU pressure */ | 517 | /* CPU pressure */ |
| 369 | fp = fopen(PSI_CPU_SOME, "r"); | 518 | fp = fopen(PSI_CPU_PATH, "r"); |
| 370 | if (fp) { | 519 | if (fp) { |
| 371 | while (fgets(line, sizeof(line), fp)) { | 520 | while (fgets(line, sizeof(line), fp)) { |
| 372 | if (strncmp(line, "some", 4) == 0) { | 521 | if (strncmp(line, "some", 4) == 0) { |
| 373 | ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", | 522 | ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 374 | &psi.cpu_some_avg10, &psi.cpu_some_avg60, | 523 | &psi.cpu_some_avg10, &psi.cpu_some_avg60, |
| 375 | &psi.cpu_some_avg300, &psi.cpu_some_total); | 524 | &psi.cpu_some_avg300, &psi.cpu_some_total); |
| 376 | if (ret != 4) | 525 | if (ret != 4) { |
| 377 | fprintf(stderr, "Failed to parse CPU some PSI data\n"); | 526 | fprintf(stderr, "Failed to parse CPU some PSI data\n"); |
| 527 | error_count++; | ||
| 528 | } | ||
| 378 | } else if (strncmp(line, "full", 4) == 0) { | 529 | } else if (strncmp(line, "full", 4) == 0) { |
| 379 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", | 530 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 380 | &psi.cpu_full_avg10, &psi.cpu_full_avg60, | 531 | &psi.cpu_full_avg10, &psi.cpu_full_avg60, |
| 381 | &psi.cpu_full_avg300, &psi.cpu_full_total); | 532 | &psi.cpu_full_avg300, &psi.cpu_full_total); |
| 382 | if (ret != 4) | 533 | if (ret != 4) { |
| 383 | fprintf(stderr, "Failed to parse CPU full PSI data\n"); | 534 | fprintf(stderr, "Failed to parse CPU full PSI data\n"); |
| 535 | error_count++; | ||
| 536 | } | ||
| 384 | } | 537 | } |
| 385 | } | 538 | } |
| 386 | fclose(fp); | 539 | fclose(fp); |
| 540 | } else { | ||
| 541 | fprintf(stderr, "Warning: Failed to open %s\n", PSI_CPU_PATH); | ||
| 542 | error_count++; | ||
| 387 | } | 543 | } |
| 544 | |||
| 388 | /* Memory pressure */ | 545 | /* Memory pressure */ |
| 389 | fp = fopen(PSI_MEMORY_SOME, "r"); | 546 | fp = fopen(PSI_MEMORY_PATH, "r"); |
| 390 | if (fp) { | 547 | if (fp) { |
| 391 | while (fgets(line, sizeof(line), fp)) { | 548 | while (fgets(line, sizeof(line), fp)) { |
| 392 | if (strncmp(line, "some", 4) == 0) { | 549 | if (strncmp(line, "some", 4) == 0) { |
| 393 | ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", | 550 | ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 394 | &psi.memory_some_avg10, &psi.memory_some_avg60, | 551 | &psi.memory_some_avg10, &psi.memory_some_avg60, |
| 395 | &psi.memory_some_avg300, &psi.memory_some_total); | 552 | &psi.memory_some_avg300, &psi.memory_some_total); |
| 396 | if (ret != 4) | 553 | if (ret != 4) { |
| 397 | fprintf(stderr, "Failed to parse Memory some PSI data\n"); | 554 | fprintf(stderr, "Failed to parse Memory some PSI data\n"); |
| 555 | error_count++; | ||
| 556 | } | ||
| 398 | } else if (strncmp(line, "full", 4) == 0) { | 557 | } else if (strncmp(line, "full", 4) == 0) { |
| 399 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", | 558 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 400 | &psi.memory_full_avg10, &psi.memory_full_avg60, | 559 | &psi.memory_full_avg10, &psi.memory_full_avg60, |
| 401 | &psi.memory_full_avg300, &psi.memory_full_total); | 560 | &psi.memory_full_avg300, &psi.memory_full_total); |
| 402 | } | 561 | if (ret != 4) { |
| 403 | if (ret != 4) | ||
| 404 | fprintf(stderr, "Failed to parse Memory full PSI data\n"); | 562 | fprintf(stderr, "Failed to parse Memory full PSI data\n"); |
| 563 | error_count++; | ||
| 564 | } | ||
| 565 | } | ||
| 405 | } | 566 | } |
| 406 | fclose(fp); | 567 | fclose(fp); |
| 568 | } else { | ||
| 569 | fprintf(stderr, "Warning: Failed to open %s\n", PSI_MEMORY_PATH); | ||
| 570 | error_count++; | ||
| 407 | } | 571 | } |
| 572 | |||
| 408 | /* IO pressure */ | 573 | /* IO pressure */ |
| 409 | fp = fopen(PSI_IO_SOME, "r"); | 574 | fp = fopen(PSI_IO_PATH, "r"); |
| 410 | if (fp) { | 575 | if (fp) { |
| 411 | while (fgets(line, sizeof(line), fp)) { | 576 | while (fgets(line, sizeof(line), fp)) { |
| 412 | if (strncmp(line, "some", 4) == 0) { | 577 | if (strncmp(line, "some", 4) == 0) { |
| 413 | ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", | 578 | ret = sscanf(line, "some avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 414 | &psi.io_some_avg10, &psi.io_some_avg60, | 579 | &psi.io_some_avg10, &psi.io_some_avg60, |
| 415 | &psi.io_some_avg300, &psi.io_some_total); | 580 | &psi.io_some_avg300, &psi.io_some_total); |
| 416 | if (ret != 4) | 581 | if (ret != 4) { |
| 417 | fprintf(stderr, "Failed to parse IO some PSI data\n"); | 582 | fprintf(stderr, "Failed to parse IO some PSI data\n"); |
| 583 | error_count++; | ||
| 584 | } | ||
| 418 | } else if (strncmp(line, "full", 4) == 0) { | 585 | } else if (strncmp(line, "full", 4) == 0) { |
| 419 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", | 586 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 420 | &psi.io_full_avg10, &psi.io_full_avg60, | 587 | &psi.io_full_avg10, &psi.io_full_avg60, |
| 421 | &psi.io_full_avg300, &psi.io_full_total); | 588 | &psi.io_full_avg300, &psi.io_full_total); |
| 422 | if (ret != 4) | 589 | if (ret != 4) { |
| 423 | fprintf(stderr, "Failed to parse IO full PSI data\n"); | 590 | fprintf(stderr, "Failed to parse IO full PSI data\n"); |
| 591 | error_count++; | ||
| 592 | } | ||
| 424 | } | 593 | } |
| 425 | } | 594 | } |
| 426 | fclose(fp); | 595 | fclose(fp); |
| 596 | } else { | ||
| 597 | fprintf(stderr, "Warning: Failed to open %s\n", PSI_IO_PATH); | ||
| 598 | error_count++; | ||
| 427 | } | 599 | } |
| 600 | |||
| 428 | /* IRQ pressure (only full) */ | 601 | /* IRQ pressure (only full) */ |
| 429 | fp = fopen(PSI_IRQ_FULL, "r"); | 602 | fp = fopen(PSI_IRQ_PATH, "r"); |
| 430 | if (fp) { | 603 | if (fp) { |
| 431 | while (fgets(line, sizeof(line), fp)) { | 604 | while (fgets(line, sizeof(line), fp)) { |
| 432 | if (strncmp(line, "full", 4) == 0) { | 605 | if (strncmp(line, "full", 4) == 0) { |
| 433 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", | 606 | ret = sscanf(line, "full avg10=%lf avg60=%lf avg300=%lf total=%llu", |
| 434 | &psi.irq_full_avg10, &psi.irq_full_avg60, | 607 | &psi.irq_full_avg10, &psi.irq_full_avg60, |
| 435 | &psi.irq_full_avg300, &psi.irq_full_total); | 608 | &psi.irq_full_avg300, &psi.irq_full_total); |
| 436 | if (ret != 4) | 609 | if (ret != 4) { |
| 437 | fprintf(stderr, "Failed to parse IRQ full PSI data\n"); | 610 | fprintf(stderr, "Failed to parse IRQ full PSI data\n"); |
| 611 | error_count++; | ||
| 612 | } | ||
| 438 | } | 613 | } |
| 439 | } | 614 | } |
| 440 | fclose(fp); | 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 | static int read_comm(int pid, char *comm_buf, size_t buf_size) | 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 | SET_TASK_STAT(task_count, wpcopy_delay_total); | 713 | SET_TASK_STAT(task_count, wpcopy_delay_total); |
| 528 | SET_TASK_STAT(task_count, irq_count); | 714 | SET_TASK_STAT(task_count, irq_count); |
| 529 | SET_TASK_STAT(task_count, irq_delay_total); | 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 | task_count++; | 718 | task_count++; |
| 531 | } | 719 | } |
| 532 | break; | 720 | break; |
| @@ -587,19 +775,23 @@ static int compare_tasks(const void *a, const void *b) · 54 unmodified lines | |||
| 587 | { | 775 | { |
| 588 | const struct task_info *t1 = (const struct task_info *)a; | 776 | const struct task_info *t1 = (const struct task_info *)a; |
| 589 | const struct task_info *t2 = (const struct task_info *)b; | 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 | double avg1, avg2; | 782 | double avg1, avg2; |
| 591 | 783 | ||
| 592 | switch (cfg.sort_field) { | 784 | total1 = *(unsigned long long *)((char *)t1 + cfg.sort_field->total_offset); |
| 593 | case 'c': /* CPU */ | 785 | total2 = *(unsigned long long *)((char *)t2 + cfg.sort_field->total_offset); |
| 594 | avg1 = average_ms(t1->cpu_delay_total, t1->cpu_count); | 786 | count1 = *(unsigned long *)((char *)t1 + cfg.sort_field->count_offset); |
| 595 | avg2 = average_ms(t2->cpu_delay_total, t2->cpu_count); | 787 | count2 = *(unsigned long *)((char *)t2 + cfg.sort_field->count_offset); |
| 596 | if (avg1 != avg2) | ||
| 597 | return avg2 > avg1 ? 1 : -1; | ||
| 598 | return t2->cpu_delay_total > t1->cpu_delay_total ? 1 : -1; | ||
| 599 | 788 | ||
| 600 | default: | 789 | avg1 = average_ms(total1, count1); |
| 601 | return t2->cpu_delay_total > t1->cpu_delay_total ? 1 : -1; | 790 | avg2 = average_ms(total2, count2); |
| 602 | } | 791 | if (avg1 != avg2) |
| 792 | return avg2 > avg1 ? 1 : -1; | ||
| 793 | |||
| 794 | return 0; | ||
| 603 | } | 795 | } |
| 604 | 796 | ||
| 605 | /* Sort tasks by selected field */ | 797 | /* Sort tasks by selected field */ |
| @@ -673,7 +865,7 @@ static void get_container_stats(void) · 67 unmodified lines | |||
| 673 | } | 865 | } |
| 674 | 866 | ||
| 675 | /* Display results to stdout or log file */ | 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 | time_t now = time(NULL); | 870 | time_t now = time(NULL); |
| 679 | struct tm *tm_now = localtime(&now); | 871 | struct tm *tm_now = localtime(&now); |
| @@ -686,49 +878,53 @@ static void display_results(void) · 6 unmodified lines | |||
| 686 | suc &= BOOL_FPRINT(out, "\033[H\033[J"); | 878 | suc &= BOOL_FPRINT(out, "\033[H\033[J"); |
| 687 | 879 | ||
| 688 | /* PSI output (one-line, no cat style) */ | 880 | /* PSI output (one-line, no cat style) */ |
| 689 | suc &= BOOL_FPRINT(out, "System Pressure Information: (avg10/avg60/avg300/total)\n"); | 881 | suc &= BOOL_FPRINT(out, "System Pressure Information: (avg10/avg60vg300/total)\n"); |
| 690 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | 882 | if (psi_ret) { |
| 691 | "CPU some:", | 883 | suc &= BOOL_FPRINT(out, " PSI not found: check if psi=1 enabled in cmdline\n"); |
| 692 | psi.cpu_some_avg10, | 884 | } else { |
| 693 | psi.cpu_some_avg60, | 885 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, |
| 694 | psi.cpu_some_avg300, | 886 | "CPU some:", |
| 695 | psi.cpu_some_total / 1000); | 887 | psi.cpu_some_avg10, |
| 696 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | 888 | psi.cpu_some_avg60, |
| 697 | "CPU full:", | 889 | psi.cpu_some_avg300, |
| 698 | psi.cpu_full_avg10, | 890 | psi.cpu_some_total / 1000); |
| 699 | psi.cpu_full_avg60, | 891 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, |
| 700 | psi.cpu_full_avg300, | 892 | "CPU full:", |
| 701 | psi.cpu_full_total / 1000); | 893 | psi.cpu_full_avg10, |
| 702 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | 894 | psi.cpu_full_avg60, |
| 703 | "Memory full:", | 895 | psi.cpu_full_avg300, |
| 704 | psi.memory_full_avg10, | 896 | psi.cpu_full_total / 1000); |
| 705 | psi.memory_full_avg60, | 897 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, |
| 706 | psi.memory_full_avg300, | 898 | "Memory full:", |
| 707 | psi.memory_full_total / 1000); | 899 | psi.memory_full_avg10, |
| 708 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | 900 | psi.memory_full_avg60, |
| 709 | "Memory some:", | 901 | psi.memory_full_avg300, |
| 710 | psi.memory_some_avg10, | 902 | psi.memory_full_total / 1000); |
| 711 | psi.memory_some_avg60, | 903 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, |
| 712 | psi.memory_some_avg300, | 904 | "Memory some:", |
| 713 | psi.memory_some_total / 1000); | 905 | psi.memory_some_avg10, |
| 714 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | 906 | psi.memory_some_avg60, |
| 715 | "IO full:", | 907 | psi.memory_some_avg300, |
| 716 | psi.io_full_avg10, | 908 | psi.memory_some_total / 1000); |
| 717 | psi.io_full_avg60, | 909 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, |
| 718 | psi.io_full_avg300, | 910 | "IO full:", |
| 719 | psi.io_full_total / 1000); | 911 | psi.io_full_avg10, |
| 720 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | 912 | psi.io_full_avg60, |
| 721 | "IO some:", | 913 | psi.io_full_avg300, |
| 722 | psi.io_some_avg10, | 914 | psi.io_full_total / 1000); |
| 723 | psi.io_some_avg60, | 915 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, |
| 724 | psi.io_some_avg300, | 916 | "IO some:", |
| 725 | psi.io_some_total / 1000); | 917 | psi.io_some_avg10, |
| 726 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, | 918 | psi.io_some_avg60, |
| 727 | "IRQ full:", | 919 | psi.io_some_avg300, |
| 728 | psi.irq_full_avg10, | 920 | psi.io_some_total / 1000); |
| 729 | psi.irq_full_avg60, | 921 | suc &= BOOL_FPRINT(out, PSI_LINE_FORMAT, |
| 730 | psi.irq_full_avg300, | 922 | "IRQ full:", |
| 731 | psi.irq_full_total / 1000); | 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 | if (cfg.container_path) { | 929 | if (cfg.container_path) { |
| 734 | suc &= BOOL_FPRINT(out, "Container Information (%s):\n", cfg.container_path); | 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 | container_stats.nr_stopped, container_stats.nr_uninterruptible, | 934 | container_stats.nr_stopped, container_stats.nr_uninterruptible, |
| 739 | container_stats.nr_io_wait); | 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, "-----------------------------------------------"); | 938 | /* Interacive command */ |
| 749 | suc &= BOOL_FPRINT(out, "----------------------------------------------\n"); | 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 | count = task_count < cfg.max_processes ? task_count : cfg.max_processes; | 970 | count = task_count < cfg.max_processes ? task_count : cfg.max_processes; |
| 751 | 971 | ||
| 752 | for (i = 0; i < count; i++) { | 972 | for (i = 0; i < count; i++) { |
| 753 | suc &= BOOL_FPRINT(out, "%5d %5d %-15s", | 973 | suc &= BOOL_FPRINT(out, "%8d %8d %-15s", |
| 754 | tasks[i].pid, tasks[i].tgid, tasks[i].command); | 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", | 975 | if (cfg.display_mode == MODE_MEMVERBOSE) { |
| 756 | average_ms(tasks[i].cpu_delay_total, tasks[i].cpu_count), | 976 | suc &= BOOL_FPRINT(out, DELAY_FMT_MEMVERBOSE, |
| 757 | average_ms(tasks[i].blkio_delay_total, tasks[i].blkio_count), | 977 | TASK_AVG(tasks[i], mem), |
| 758 | average_ms(tasks[i].swapin_delay_total, tasks[i].swapin_count), | 978 | TASK_AVG(tasks[i], swapin), |
| 759 | average_ms(tasks[i].freepages_delay_total, tasks[i].freepages_count), | 979 | TASK_AVG(tasks[i], freepages), |
| 760 | average_ms(tasks[i].thrashing_delay_total, tasks[i].thrashing_count), | 980 | TASK_AVG(tasks[i], thrashing), |
| 761 | average_ms(tasks[i].compact_delay_total, tasks[i].compact_count), | 981 | TASK_AVG(tasks[i], compact), |
| 762 | average_ms(tasks[i].wpcopy_delay_total, tasks[i].wpcopy_count), | 982 | TASK_AVG(tasks[i], wpcopy)); |
| 763 | average_ms(tasks[i].irq_delay_total, tasks[i].irq_count)); | 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 | suc &= BOOL_FPRINT(out, "\n"); | 992 | suc &= BOOL_FPRINT(out, "\n"); |
| @@ -769,11 +995,79 @@ static void display_results(void) · 2 unmodified lines | |||
| 769 | perror("Error writing to output"); | 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 | /* Main function */ | 1064 | /* Main function */ |
| 773 | int main(int argc, char **argv) | 1065 | int main(int argc, char **argv) |
| 774 | { | 1066 | { |
| 1067 | const struct field_desc *field; | ||
| 775 | int iterations = 0; | 1068 | int iterations = 0; |
| 776 | int use_q_quit = 0; | 1069 | int psi_ret = 0; |
| 1070 | char keypress; | ||
| 777 | 1071 | ||
| 778 | /* Parse command line arguments */ | 1072 | /* Parse command line arguments */ |
| 779 | parse_args(argc, argv); | 1073 | parse_args(argc, argv); |
| @@ -793,17 +1087,24 @@ int main(int argc, char **argv) · 13 unmodified lines | |||
| 793 | exit(1); | 1087 | exit(1); |
| 794 | } | 1088 | } |
| 795 | 1089 | ||
| 796 | if (!cfg.output_one_time) { | 1090 | /* Set terminal to non-canonical mode for interaction */ |
| 797 | use_q_quit = 1; | 1091 | enable_raw_mode(); |
| 798 | enable_raw_mode(); | ||
| 799 | printf("Press 'q' to quit.\n"); | ||
| 800 | fflush(stdout); | ||
| 801 | } | ||
| 802 | 1092 | ||
| 803 | /* Main loop */ | 1093 | /* Main loop */ |
| 804 | while (running) { | 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 | /* Read PSI statistics */ | 1106 | /* Read PSI statistics */ |
| 806 | read_psi_stats(); | 1107 | psi_ret = read_psi_stats(); |
| 807 | 1108 | ||
| 808 | /* Get container stats if container path provided */ | 1109 | /* Get container stats if container path provided */ |
| 809 | if (cfg.container_path) | 1110 | if (cfg.container_path) |
| @@ -816,7 +1117,7 @@ int main(int argc, char **argv) · 6 unmodified lines | |||
| 816 | sort_tasks(); | 1117 | sort_tasks(); |
| 817 | 1118 | ||
| 818 | /* Display results to stdout or log file */ | 1119 | /* Display results to stdout or log file */ |
| 819 | display_results(); | 1120 | display_results(psi_ret); |
| 820 | 1121 | ||
| 821 | /* Check for iterations */ | 1122 | /* Check for iterations */ |
| 822 | if (cfg.iterations > 0 && ++iterations >= cfg.iterations) | 1123 | if (cfg.iterations > 0 && ++iterations >= cfg.iterations) |
| @@ -826,32 +1127,14 @@ int main(int argc, char **argv) · 3 unmodified lines | |||
| 826 | if (cfg.output_one_time) | 1127 | if (cfg.output_one_time) |
| 827 | break; | 1128 | break; |
| 828 | 1129 | ||
| 829 | /* Check for 'q' key to quit */ | 1130 | /* Keypress for interactive usage */ |
| 830 | if (use_q_quit) { | 1131 | keypress = check_for_keypress(); |
| 831 | struct timeval tv = {cfg.delay, 0}; | 1132 | if (keypress) |
| 832 | fd_set readfds; | 1133 | handle_keypress(keypress, &running); |
| 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 | } | ||
| 850 | } | 1134 | } |
| 851 | 1135 | ||
| 852 | /* Restore terminal mode */ | 1136 | /* Restore terminal mode */ |
| 853 | if (use_q_quit) | 1137 | disable_raw_mode(); |
| 854 | disable_raw_mode(); | ||
| 855 | 1138 | ||
| 856 | /* Cleanup */ | 1139 | /* Cleanup */ |
| 857 | close(nl_sd); | 1140 | close(nl_sd); |
| @@ -499,19 +499,17 @@ void ida_check_random(void) · 498 unmodified lines | |||
| 499 | goto repeat; | 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 | DEFINE_IDA(ida); | 504 | DEFINE_IDA(ida); |
| 505 | unsigned long i; | 505 | unsigned long i; |
| 506 | 506 | ||
| 507 | for (i = 0; i < 10000; i++) { | 507 | for (i = 0; i < 10000; i++) |
| 508 | assert(ida_simple_get(&ida, 0, 20000, GFP_KERNEL) == i); | 508 | assert(ida_alloc_max(&ida, 20000, GFP_KERNEL) == i); |
| 509 | } | 509 | assert(ida_alloc_range(&ida, 5, 30, GFP_KERNEL) < 0); |
| 510 | assert(ida_simple_get(&ida, 5, 30, GFP_KERNEL) < 0); | ||
| 511 | 510 | ||
| 512 | for (i = 0; i < 10000; i++) { | 511 | for (i = 0; i < 10000; i++) |
| 513 | ida_simple_remove(&ida, i); | 512 | ida_free(&ida, i); |
| 514 | } | ||
| 515 | assert(ida_is_empty(&ida)); | 513 | assert(ida_is_empty(&ida)); |
| 516 | 514 | ||
| 517 | ida_destroy(&ida); | 515 | ida_destroy(&ida); |
| @@ -524,7 +522,7 @@ void user_ida_checks(void) · 6 unmodified lines | |||
| 524 | ida_check_nomem(); | 522 | ida_check_nomem(); |
| 525 | ida_check_conv_user(); | 523 | ida_check_conv_user(); |
| 526 | ida_check_random(); | 524 | ida_check_random(); |
| 527 | ida_simple_get_remove_test(); | 525 | ida_alloc_free_test(); |
| 528 | 526 | ||
| 529 | radix_tree_cpu_dead(1); | 527 | radix_tree_cpu_dead(1); |
| 530 | } | 528 | } |
| @@ -7,6 +7,7 @@ · 6 unmodified lines | |||
| 7 | /proc-loadavg-001 | 7 | /proc-loadavg-001 |
| 8 | /proc-maps-race | 8 | /proc-maps-race |
| 9 | /proc-multiple-procfs | 9 | /proc-multiple-procfs |
| 10 | /proc-net-dev-lseek | ||
| 10 | /proc-empty-vm | 11 | /proc-empty-vm |
| 11 | /proc-pid-vm | 12 | /proc-pid-vm |
| 12 | /proc-self-map-files-001 | 13 | /proc-self-map-files-001 |
| @@ -10,6 +10,7 @@ TEST_GEN_PROGS += fd-003-kthread · 9 unmodified lines | |||
| 10 | TEST_GEN_PROGS += proc-2-is-kthread | 10 | TEST_GEN_PROGS += proc-2-is-kthread |
| 11 | TEST_GEN_PROGS += proc-loadavg-001 | 11 | TEST_GEN_PROGS += proc-loadavg-001 |
| 12 | TEST_GEN_PROGS += proc-maps-race | 12 | TEST_GEN_PROGS += proc-maps-race |
| 13 | TEST_GEN_PROGS += proc-net-dev-lseek | ||
| 13 | TEST_GEN_PROGS += proc-empty-vm | 14 | TEST_GEN_PROGS += proc-empty-vm |
| 14 | TEST_GEN_PROGS += proc-pid-vm | 15 | TEST_GEN_PROGS += proc-pid-vm |
| 15 | TEST_GEN_PROGS += proc-self-map-files-001 | 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 | #include <sys/resource.h> | 47 | #include <sys/resource.h> |
| 48 | #include <linux/fs.h> | 48 | #include <linux/fs.h> |
| 49 | 49 | ||
| 50 | #ifndef __maybe_unused | ||
| 51 | #define __maybe_unused __attribute__((__unused__)) | ||
| 52 | #endif | ||
| 53 | |||
| 50 | #include "../kselftest.h" | 54 | #include "../kselftest.h" |
| 51 | 55 | ||
| 52 | static inline long sys_execveat(int dirfd, const char *pathname, char **argv, char **envp, int flags) | 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 | * 2: vsyscall VMA is r-xp vsyscall=emulate | 222 | * 2: vsyscall VMA is r-xp vsyscall=emulate |
| 219 | */ | 223 | */ |
| 220 | static volatile int g_vsyscall; | 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[] = ""; | 227 | static const char str_vsyscall_0[] __maybe_unused = ""; |
| 224 | static const char str_vsyscall_1[] = | 228 | static const char str_vsyscall_1[] __maybe_unused = |
| 225 | "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n"; | 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 | "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n"; | 231 | "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n"; |
| 228 | 232 | ||
| 229 | #ifdef __x86_64__ | 233 | #ifdef __x86_64__ |