Pull ring-buffer fixes from Steven Rostedt: - Fix reporting MISSED EVENTS in trace iterator When the "trace" file is read with tracing enabled, if the writer were to pass the iterator reader, it resets, sets a "missed_events" flag and continues. The tracing output checks for missed events and if there are some, it prints out "[LOST EVENTS]" to let the user know events were dropped. But the clearing of the missed_events happened when the tracing system queried the ring buffer iterator about missed events. This was premature as the ring buffer is per CPU, and the tracing code reads all the CPU buffers and checks for missed events when it is read. If the CPU iterator that had missed events isn't printed next, the output for the LOST EVENTS is lost. Clear the missed_events flag when the iterator moves to the next event and not when the missed_events flag is queried. Also clear it on reset. - Flush and stop the persistent ring buffer on panic On panic the persistent ring buffer is used to debug what caused the panic. But on some architectures, it requires flushing the memory from cache, otherwise, the ring buffer persistent memory may not have the last events and this could also cause the ring buffer to be corrupted on the next boot. - Fix nr_subbufs initialization in simple_ring_buffer_init_mm The remote simple ring buffer meta data nr_subbufs is initialized too early and gets cleared later on, making it zero and not reflect the actual number of sub-buffers. - Fix unload_page for simple_ring_buffer init rollback On error, the pages loaded need to be unloaded. To unload a page it is expected that: page = load_page(va); -> unload_page(page). But the code was doing: unload_page(va) and not unload_page(page). - Create output file from cmd_check_undefined The check for undefined symbols checks if the file *.o.checked exists and if so it skips doing the work. But the *.o.checked file never was created making every build do the work even when it was already done previously. * tag 'trace-ringbuffer-v7.1-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: tracing: Create output file from cmd_check_undefined tracing: Fix unload_page for simple_ring_buffer init rollback tracing: Fix nr_subbufs initialization in simple_ring_buffer_init_mm() ring-buffer: Flush and stop persistent ring buffer on panic ring-buffer: Fix reporting of missed events in iterator
Unified · Split
| @@ -5,4 +5,5 @@ generic-y += agp.h · 4 unmodified lines | |||
| 5 | generic-y += asm-offsets.h | 5 | generic-y += asm-offsets.h |
| 6 | generic-y += kvm_para.h | 6 | generic-y += kvm_para.h |
| 7 | generic-y += mcs_spinlock.h | 7 | generic-y += mcs_spinlock.h |
| 8 | generic-y += ring_buffer.h | ||
| 8 | generic-y += text-patching.h | 9 | generic-y += text-patching.h |
| @@ -5,5 +5,6 @@ generic-y += extable.h · 4 unmodified lines | |||
| 5 | generic-y += kvm_para.h | 5 | generic-y += kvm_para.h |
| 6 | generic-y += mcs_spinlock.h | 6 | generic-y += mcs_spinlock.h |
| 7 | generic-y += parport.h | 7 | generic-y += parport.h |
| 8 | generic-y += ring_buffer.h | ||
| 8 | generic-y += user.h | 9 | generic-y += user.h |
| 9 | generic-y += text-patching.h | 10 | generic-y += text-patching.h |
| @@ -3,6 +3,7 @@ generic-y += early_ioremap.h · 2 unmodified lines | |||
| 3 | generic-y += extable.h | 3 | generic-y += extable.h |
| 4 | generic-y += flat.h | 4 | generic-y += flat.h |
| 5 | generic-y += parport.h | 5 | generic-y += parport.h |
| 6 | generic-y += ring_buffer.h | ||
| 6 | 7 | ||
| 7 | generated-y += mach-types.h | 8 | generated-y += mach-types.h |
| 8 | generated-y += unistd-nr.h | 9 | generated-y += unistd-nr.h |
| @@ -0,0 +1,10 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0-only */ | ||
| 2 | #ifndef _ASM_ARM64_RING_BUFFER_H | ||
| 3 | #define _ASM_ARM64_RING_BUFFER_H | ||
| 4 | |||
| 5 | #include <asm/cacheflush.h> | ||
| 6 | |||
| 7 | /* Flush D-cache on persistent ring buffer */ | ||
| 8 | #define arch_ring_buffer_flush_range(start, end) dcache_clean_pop(start, end) | ||
| 9 | |||
| 10 | #endif /* _ASM_ARM64_RING_BUFFER_H */ | ||
| @@ -9,6 +9,7 @@ generic-y += qrwlock.h · 8 unmodified lines | |||
| 9 | generic-y += qrwlock_types.h | 9 | generic-y += qrwlock_types.h |
| 10 | generic-y += qspinlock.h | 10 | generic-y += qspinlock.h |
| 11 | generic-y += parport.h | 11 | generic-y += parport.h |
| 12 | generic-y += ring_buffer.h | ||
| 12 | generic-y += user.h | 13 | generic-y += user.h |
| 13 | generic-y += vmlinux.lds.h | 14 | generic-y += vmlinux.lds.h |
| 14 | generic-y += text-patching.h | 15 | generic-y += text-patching.h |
| @@ -5,4 +5,5 @@ generic-y += extable.h · 4 unmodified lines | |||
| 5 | generic-y += iomap.h | 5 | generic-y += iomap.h |
| 6 | generic-y += kvm_para.h | 6 | generic-y += kvm_para.h |
| 7 | generic-y += mcs_spinlock.h | 7 | generic-y += mcs_spinlock.h |
| 8 | generic-y += ring_buffer.h | ||
| 8 | generic-y += text-patching.h | 9 | generic-y += text-patching.h |
| @@ -10,5 +10,6 @@ generic-y += qrwlock.h · 9 unmodified lines | |||
| 10 | generic-y += user.h | 10 | generic-y += user.h |
| 11 | generic-y += ioctl.h | 11 | generic-y += ioctl.h |
| 12 | generic-y += mmzone.h | 12 | generic-y += mmzone.h |
| 13 | generic-y += ring_buffer.h | ||
| 13 | generic-y += statfs.h | 14 | generic-y += statfs.h |
| 14 | generic-y += text-patching.h | 15 | generic-y += text-patching.h |
| @@ -3,5 +3,6 @@ generated-y += syscall_table.h · 2 unmodified lines | |||
| 3 | generic-y += extable.h | 3 | generic-y += extable.h |
| 4 | generic-y += kvm_para.h | 4 | generic-y += kvm_para.h |
| 5 | generic-y += mcs_spinlock.h | 5 | generic-y += mcs_spinlock.h |
| 6 | generic-y += ring_buffer.h | ||
| 6 | generic-y += spinlock.h | 7 | generic-y += spinlock.h |
| 7 | generic-y += text-patching.h | 8 | generic-y += text-patching.h |
| @@ -5,6 +5,7 @@ generic-y += extable.h · 4 unmodified lines | |||
| 5 | generic-y += kvm_para.h | 5 | generic-y += kvm_para.h |
| 6 | generic-y += mcs_spinlock.h | 6 | generic-y += mcs_spinlock.h |
| 7 | generic-y += parport.h | 7 | generic-y += parport.h |
| 8 | generic-y += ring_buffer.h | ||
| 8 | generic-y += syscalls.h | 9 | generic-y += syscalls.h |
| 9 | generic-y += tlb.h | 10 | generic-y += tlb.h |
| 10 | generic-y += user.h | 11 | generic-y += user.h |
| @@ -12,5 +12,6 @@ generic-y += mcs_spinlock.h · 11 unmodified lines | |||
| 12 | generic-y += parport.h | 12 | generic-y += parport.h |
| 13 | generic-y += qrwlock.h | 13 | generic-y += qrwlock.h |
| 14 | generic-y += qspinlock.h | 14 | generic-y += qspinlock.h |
| 15 | generic-y += ring_buffer.h | ||
| 15 | generic-y += user.h | 16 | generic-y += user.h |
| 16 | generic-y += text-patching.h | 17 | generic-y += text-patching.h |
| @@ -5,6 +5,7 @@ generic-y += cmpxchg.h · 4 unmodified lines | |||
| 5 | generic-y += extable.h | 5 | generic-y += extable.h |
| 6 | generic-y += kvm_para.h | 6 | generic-y += kvm_para.h |
| 7 | generic-y += mcs_spinlock.h | 7 | generic-y += mcs_spinlock.h |
| 8 | generic-y += ring_buffer.h | ||
| 8 | generic-y += spinlock.h | 9 | generic-y += spinlock.h |
| 9 | generic-y += user.h | 10 | generic-y += user.h |
| 10 | generic-y += text-patching.h | 11 | generic-y += text-patching.h |
| @@ -8,4 +8,5 @@ generic-y += spinlock_types.h · 7 unmodified lines | |||
| 8 | generic-y += spinlock.h | 8 | generic-y += spinlock.h |
| 9 | generic-y += qrwlock_types.h | 9 | generic-y += qrwlock_types.h |
| 10 | generic-y += qrwlock.h | 10 | generic-y += qrwlock.h |
| 11 | generic-y += ring_buffer.h | ||
| 11 | generic-y += user.h | 12 | generic-y += user.h |
| @@ -4,4 +4,5 @@ generated-y += syscall_table_64.h · 3 unmodified lines | |||
| 4 | generic-y += agp.h | 4 | generic-y += agp.h |
| 5 | generic-y += kvm_para.h | 5 | generic-y += kvm_para.h |
| 6 | generic-y += mcs_spinlock.h | 6 | generic-y += mcs_spinlock.h |
| 7 | generic-y += ring_buffer.h | ||
| 7 | generic-y += user.h | 8 | generic-y += user.h |
| @@ -5,4 +5,5 @@ generated-y += syscall_table_spu.h · 4 unmodified lines | |||
| 5 | generic-y += agp.h | 5 | generic-y += agp.h |
| 6 | generic-y += mcs_spinlock.h | 6 | generic-y += mcs_spinlock.h |
| 7 | generic-y += qrwlock.h | 7 | generic-y += qrwlock.h |
| 8 | generic-y += ring_buffer.h | ||
| 8 | generic-y += early_ioremap.h | 9 | generic-y += early_ioremap.h |
| @@ -14,5 +14,6 @@ generic-y += ticket_spinlock.h · 13 unmodified lines | |||
| 14 | generic-y += qrwlock.h | 14 | generic-y += qrwlock.h |
| 15 | generic-y += qrwlock_types.h | 15 | generic-y += qrwlock_types.h |
| 16 | generic-y += qspinlock.h | 16 | generic-y += qspinlock.h |
| 17 | generic-y += ring_buffer.h | ||
| 17 | generic-y += user.h | 18 | generic-y += user.h |
| 18 | generic-y += vmlinux.lds.h | 19 | generic-y += vmlinux.lds.h |
| @@ -7,3 +7,4 @@ generated-y += unistd_nr.h · 6 unmodified lines | |||
| 7 | generic-y += asm-offsets.h | 7 | generic-y += asm-offsets.h |
| 8 | generic-y += mcs_spinlock.h | 8 | generic-y += mcs_spinlock.h |
| 9 | generic-y += mmzone.h | 9 | generic-y += mmzone.h |
| 10 | generic-y += ring_buffer.h | ||
| @@ -3,4 +3,5 @@ generated-y += syscall_table.h · 2 unmodified lines | |||
| 3 | generic-y += kvm_para.h | 3 | generic-y += kvm_para.h |
| 4 | generic-y += mcs_spinlock.h | 4 | generic-y += mcs_spinlock.h |
| 5 | generic-y += parport.h | 5 | generic-y += parport.h |
| 6 | generic-y += ring_buffer.h | ||
| 6 | generic-y += text-patching.h | 7 | generic-y += text-patching.h |
| @@ -4,4 +4,5 @@ generated-y += syscall_table_64.h · 3 unmodified lines | |||
| 4 | generic-y += agp.h | 4 | generic-y += agp.h |
| 5 | generic-y += kvm_para.h | 5 | generic-y += kvm_para.h |
| 6 | generic-y += mcs_spinlock.h | 6 | generic-y += mcs_spinlock.h |
| 7 | generic-y += ring_buffer.h | ||
| 7 | generic-y += text-patching.h | 8 | generic-y += text-patching.h |
| @@ -17,6 +17,7 @@ generic-y += module.lds.h · 16 unmodified lines | |||
| 17 | generic-y += parport.h | 17 | generic-y += parport.h |
| 18 | generic-y += percpu.h | 18 | generic-y += percpu.h |
| 19 | generic-y += preempt.h | 19 | generic-y += preempt.h |
| 20 | generic-y += ring_buffer.h | ||
| 20 | generic-y += runtime-const.h | 21 | generic-y += runtime-const.h |
| 21 | generic-y += softirq_stack.h | 22 | generic-y += softirq_stack.h |
| 22 | generic-y += switch_to.h | 23 | generic-y += switch_to.h |
| @@ -14,3 +14,4 @@ generic-y += early_ioremap.h · 13 unmodified lines | |||
| 14 | generic-y += fprobe.h | 14 | generic-y += fprobe.h |
| 15 | generic-y += mcs_spinlock.h | 15 | generic-y += mcs_spinlock.h |
| 16 | generic-y += mmzone.h | 16 | generic-y += mmzone.h |
| 17 | generic-y += ring_buffer.h | ||
| @@ -6,5 +6,6 @@ generic-y += mcs_spinlock.h · 5 unmodified lines | |||
| 6 | generic-y += parport.h | 6 | generic-y += parport.h |
| 7 | generic-y += qrwlock.h | 7 | generic-y += qrwlock.h |
| 8 | generic-y += qspinlock.h | 8 | generic-y += qspinlock.h |
| 9 | generic-y += ring_buffer.h | ||
| 9 | generic-y += user.h | 10 | generic-y += user.h |
| 10 | generic-y += text-patching.h | 11 | generic-y += text-patching.h |
| @@ -0,0 +1,13 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0-only */ | ||
| 2 | /* | ||
| 3 | * Generic arch dependent ring_buffer macros. | ||
| 4 | */ | ||
| 5 | #ifndef __ASM_GENERIC_RING_BUFFER_H__ | ||
| 6 | #define __ASM_GENERIC_RING_BUFFER_H__ | ||
| 7 | |||
| 8 | #include <linux/cacheflush.h> | ||
| 9 | |||
| 10 | /* Flush cache on ring buffer range if needed. Do nothing by default. */ | ||
| 11 | #define arch_ring_buffer_flush_range(start, end) do { } while (0) | ||
| 12 | |||
| 13 | #endif /* __ASM_GENERIC_RING_BUFFER_H__ */ | ||
| @@ -154,7 +154,8 @@ quiet_cmd_check_undefined = NM $< · 153 unmodified lines | |||
| 154 | echo "Unexpected symbols in $<:" >&2; \ | 154 | echo "Unexpected symbols in $<:" >&2; \ |
| 155 | echo "$$undefsyms" >&2; \ | 155 | echo "$$undefsyms" >&2; \ |
| 156 | false; \ | 156 | false; \ |
| 157 | fi | 157 | fi; \ |
| 158 | touch $@ | ||
| 158 | 159 | ||
| 159 | $(obj)/%.o.checked: $(obj)/%.o $(obj)/undefsyms_base.o FORCE | 160 | $(obj)/%.o.checked: $(obj)/%.o $(obj)/undefsyms_base.o FORCE |
| 160 | $(call if_changed,check_undefined) | 161 | $(call if_changed,check_undefined) |
| @@ -7,6 +7,7 @@ · 6 unmodified lines | |||
| 7 | #include <linux/ring_buffer_types.h> | 7 | #include <linux/ring_buffer_types.h> |
| 8 | #include <linux/sched/isolation.h> | 8 | #include <linux/sched/isolation.h> |
| 9 | #include <linux/trace_recursion.h> | 9 | #include <linux/trace_recursion.h> |
| 10 | #include <linux/panic_notifier.h> | ||
| 10 | #include <linux/trace_events.h> | 11 | #include <linux/trace_events.h> |
| 11 | #include <linux/ring_buffer.h> | 12 | #include <linux/ring_buffer.h> |
| 12 | #include <linux/trace_clock.h> | 13 | #include <linux/trace_clock.h> |
| @@ -31,6 +32,7 @@ · 18 unmodified lines | |||
| 31 | #include <linux/oom.h> | 32 | #include <linux/oom.h> |
| 32 | #include <linux/mm.h> | 33 | #include <linux/mm.h> |
| 33 | 34 | ||
| 35 | #include <asm/ring_buffer.h> | ||
| 34 | #include <asm/local64.h> | 36 | #include <asm/local64.h> |
| 35 | #include <asm/local.h> | 37 | #include <asm/local.h> |
| 36 | #include <asm/setup.h> | 38 | #include <asm/setup.h> |
| @@ -559,6 +561,7 @@ struct trace_buffer { · 522 unmodified lines | |||
| 559 | 561 | ||
| 560 | unsigned long range_addr_start; | 562 | unsigned long range_addr_start; |
| 561 | unsigned long range_addr_end; | 563 | unsigned long range_addr_end; |
| 564 | struct notifier_block flush_nb; | ||
| 562 | 565 | ||
| 563 | struct ring_buffer_meta *meta; | 566 | struct ring_buffer_meta *meta; |
| 564 | 567 | ||
| @@ -2521,6 +2524,16 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer) · 1956 unmodified lines | |||
| 2521 | kfree(cpu_buffer); | 2524 | kfree(cpu_buffer); |
| 2522 | } | 2525 | } |
| 2523 | 2526 | ||
| 2527 | /* Stop recording on a persistent buffer and flush cache if needed. */ | ||
| 2528 | static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data) | ||
| 2529 | { | ||
| 2530 | struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb); | ||
| 2531 | |||
| 2532 | ring_buffer_record_off(buffer); | ||
| 2533 | arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end); | ||
| 2534 | return NOTIFY_DONE; | ||
| 2535 | } | ||
| 2536 | |||
| 2524 | static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, | 2537 | static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, |
| 2525 | int order, unsigned long start, | 2538 | int order, unsigned long start, |
| 2526 | unsigned long end, | 2539 | unsigned long end, |
| @@ -2651,6 +2664,12 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, · 124 unmodified lines | |||
| 2651 | 2664 | ||
| 2652 | mutex_init(&buffer->mutex); | 2665 | mutex_init(&buffer->mutex); |
| 2653 | 2666 | ||
| 2667 | /* Persistent ring buffer needs to flush cache before reboot. */ | ||
| 2668 | if (start && end) { | ||
| 2669 | buffer->flush_nb.notifier_call = rb_flush_buffer_cb; | ||
| 2670 | atomic_notifier_chain_register(&panic_notifier_list, &buffer->flush_nb); | ||
| 2671 | } | ||
| 2672 | |||
| 2654 | return_ptr(buffer); | 2673 | return_ptr(buffer); |
| 2655 | 2674 | ||
| 2656 | fail_free_buffers: | 2675 | fail_free_buffers: |
| @@ -2749,6 +2768,9 @@ ring_buffer_free(struct trace_buffer *buffer) · 92 unmodified lines | |||
| 2749 | { | 2768 | { |
| 2750 | int cpu; | 2769 | int cpu; |
| 2751 | 2770 | ||
| 2771 | if (buffer->range_addr_start && buffer->range_addr_end) | ||
| 2772 | atomic_notifier_chain_unregister(&panic_notifier_list, &buffer->flush_nb); | ||
| 2773 | |||
| 2752 | cpuhp_state_remove_instance(CPUHP_TRACE_RB_PREPARE, &buffer->node); | 2774 | cpuhp_state_remove_instance(CPUHP_TRACE_RB_PREPARE, &buffer->node); |
| 2753 | 2775 | ||
| 2754 | irq_work_sync(&buffer->irq_work.work); | 2776 | irq_work_sync(&buffer->irq_work.work); |
| @@ -5407,6 +5429,7 @@ static void rb_iter_reset(struct ring_buffer_iter *iter) · 2652 unmodified lines | |||
| 5407 | iter->head_page = cpu_buffer->reader_page; | 5429 | iter->head_page = cpu_buffer->reader_page; |
| 5408 | iter->head = cpu_buffer->reader_page->read; | 5430 | iter->head = cpu_buffer->reader_page->read; |
| 5409 | iter->next_event = iter->head; | 5431 | iter->next_event = iter->head; |
| 5432 | iter->missed_events = 0; | ||
| 5410 | 5433 | ||
| 5411 | iter->cache_reader_page = iter->head_page; | 5434 | iter->cache_reader_page = iter->head_page; |
| 5412 | iter->cache_read = cpu_buffer->read; | 5435 | iter->cache_read = cpu_buffer->read; |
| @@ -6086,10 +6109,7 @@ ring_buffer_peek(struct trace_buffer *buffer, int cpu, u64 *ts, · 673 unmodified lines | |||
| 6086 | */ | 6109 | */ |
| 6087 | bool ring_buffer_iter_dropped(struct ring_buffer_iter *iter) | 6110 | bool ring_buffer_iter_dropped(struct ring_buffer_iter *iter) |
| 6088 | { | 6111 | { |
| 6089 | bool ret = iter->missed_events != 0; | 6112 | return iter->missed_events != 0; |
| 6090 | |||
| 6091 | iter->missed_events = 0; | ||
| 6092 | return ret; | ||
| 6093 | } | 6113 | } |
| 6094 | EXPORT_SYMBOL_GPL(ring_buffer_iter_dropped); | 6114 | EXPORT_SYMBOL_GPL(ring_buffer_iter_dropped); |
| 6095 | 6115 | ||
| @@ -6251,7 +6271,7 @@ void ring_buffer_iter_advance(struct ring_buffer_iter *iter) · 155 unmodified lines | |||
| 6251 | unsigned long flags; | 6271 | unsigned long flags; |
| 6252 | 6272 | ||
| 6253 | raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); | 6273 | raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); |
| 6254 | 6274 | iter->missed_events = 0; | |
| 6255 | rb_advance_iter(iter); | 6275 | rb_advance_iter(iter); |
| 6256 | 6276 | ||
| 6257 | raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); | 6277 | raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); |
| @@ -395,7 +395,6 @@ int simple_ring_buffer_init_mm(struct simple_rb_per_cpu *cpu_buffer, · 394 unmodified lines | |||
| 395 | 395 | ||
| 396 | memset(cpu_buffer->meta, 0, sizeof(*cpu_buffer->meta)); | 396 | memset(cpu_buffer->meta, 0, sizeof(*cpu_buffer->meta)); |
| 397 | cpu_buffer->meta->meta_page_size = PAGE_SIZE; | 397 | cpu_buffer->meta->meta_page_size = PAGE_SIZE; |
| 398 | cpu_buffer->meta->nr_subbufs = cpu_buffer->nr_pages; | ||
| 399 | 398 | ||
| 400 | /* The reader page is not part of the ring initially */ | 399 | /* The reader page is not part of the ring initially */ |
| 401 | page = load_page(desc->page_va[0]); | 400 | page = load_page(desc->page_va[0]); |
| @@ -431,12 +430,13 @@ int simple_ring_buffer_init_mm(struct simple_rb_per_cpu *cpu_buffer, · 29 unmodified lines | |||
| 431 | 430 | ||
| 432 | if (ret) { | 431 | if (ret) { |
| 433 | for (i--; i >= 0; i--) | 432 | for (i--; i >= 0; i--) |
| 434 | unload_page((void *)desc->page_va[i]); | 433 | unload_page(bpages[i].page); |
| 435 | unload_page(cpu_buffer->meta); | 434 | unload_page(cpu_buffer->meta); |
| 436 | 435 | ||
| 437 | return ret; | 436 | return ret; |
| 438 | } | 437 | } |
| 439 | 438 | ||
| 439 | cpu_buffer->meta->nr_subbufs = cpu_buffer->nr_pages; | ||
| 440 | /* Close the ring */ | 440 | /* Close the ring */ |
| 441 | bpage->link.next = &cpu_buffer->tail_page->link; | 441 | bpage->link.next = &cpu_buffer->tail_page->link; |
| 442 | cpu_buffer->tail_page->link.prev = &bpage->link; | 442 | cpu_buffer->tail_page->link.prev = &bpage->link; |