arch/mips/kernel/smp.c

Source file repositories/reference/linux-study-clean/arch/mips/kernel/smp.c

File Facts

System
Linux kernel
Corpus path
arch/mips/kernel/smp.c
Extension
.c
Size
17563 bytes
Lines
762
Domain
Architecture Layer
Bucket
arch/mips
Inferred role
Architecture Layer: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

struct flush_tlb_data {
	struct vm_area_struct *vma;
	unsigned long addr1;
	unsigned long addr2;
};

static void flush_tlb_range_ipi(void *info)
{
	struct flush_tlb_data *fd = info;

	local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2);
}

void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
{
	struct mm_struct *mm = vma->vm_mm;
	unsigned long addr;
	u32 old_mmid;

	preempt_disable();
	if (cpu_has_mmid) {
		htw_stop();
		old_mmid = read_c0_memorymapid();
		write_c0_memorymapid(cpu_asid(0, mm));
		mtc0_tlbw_hazard();
		addr = round_down(start, PAGE_SIZE * 2);
		end = round_up(end, PAGE_SIZE * 2);
		do {
			ginvt_va_mmid(addr);
			sync_ginv();
			addr += PAGE_SIZE * 2;
		} while (addr < end);
		write_c0_memorymapid(old_mmid);
		instruction_hazard();
		htw_start();
	} else if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) {
		struct flush_tlb_data fd = {
			.vma = vma,
			.addr1 = start,
			.addr2 = end,
		};

		smp_on_other_tlbs(flush_tlb_range_ipi, &fd);
		local_flush_tlb_range(vma, start, end);
	} else {
		unsigned int cpu;
		int exec = vma->vm_flags & VM_EXEC;

		for_each_online_cpu(cpu) {
			/*
			 * flush_cache_range() will only fully flush icache if
			 * the VMA is executable, otherwise we must invalidate
			 * ASID without it appearing to has_valid_asid() as if
			 * mm has been completely unused by that CPU.
			 */
			if (cpu != smp_processor_id() && cpu_context(cpu, mm))
				set_cpu_context(cpu, mm, !exec);
		}
		local_flush_tlb_range(vma, start, end);
	}
	preempt_enable();
}

static void flush_tlb_kernel_range_ipi(void *info)
{
	struct flush_tlb_data *fd = info;

	local_flush_tlb_kernel_range(fd->addr1, fd->addr2);
}

void flush_tlb_kernel_range(unsigned long start, unsigned long end)
{
	struct flush_tlb_data fd = {
		.addr1 = start,
		.addr2 = end,
	};

	on_each_cpu(flush_tlb_kernel_range_ipi, &fd, 1);
}

static void flush_tlb_page_ipi(void *info)
{
	struct flush_tlb_data *fd = info;

	local_flush_tlb_page(fd->vma, fd->addr1);
}

void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
{
	u32 old_mmid;

Annotation

Implementation Notes