tools/testing/selftests/mm/uffd-common.c

Source file repositories/reference/linux-study-clean/tools/testing/selftests/mm/uffd-common.c

File Facts

System
Linux kernel
Corpus path
tools/testing/selftests/mm/uffd-common.c
Extension
.c
Size
20509 bytes
Lines
746
Domain
Support Tooling And Documentation
Bucket
tools
Inferred role
Support Tooling And Documentation: implementation source
Status
source implementation candidate

Why This File Exists

Repository support layer: documentation, build tooling, samples, user-space helper tools, generated initramfs support, licenses, and validation utilities.

Dependency Surface

Detected Declarations

Annotated Snippet

if (ret < 0) {
			if (errno == EAGAIN || errno == EINTR)
				return 1;
			err("blocking read error");
		} else {
			err("short read");
		}
	}

	return 0;
}

void uffd_handle_page_fault(uffd_global_test_opts_t *gopts, struct uffd_msg *msg,
			    struct uffd_args *args)
{
	unsigned long offset;

	if (msg->event != UFFD_EVENT_PAGEFAULT)
		err("unexpected msg event %u", msg->event);

	if (msg->arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP) {
		/* Write protect page faults */
		wp_range(gopts->uffd, msg->arg.pagefault.address, gopts->page_size, false);
		args->wp_faults++;
	} else if (msg->arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_MINOR) {
		uint8_t *area;
		int b;

		/*
		 * Minor page faults
		 *
		 * To prove we can modify the original range for testing
		 * purposes, we're going to bit flip this range before
		 * continuing.
		 *
		 * Note that this requires all minor page fault tests operate on
		 * area_dst (non-UFFD-registered) and area_dst_alias
		 * (UFFD-registered).
		 */

		area = (uint8_t *)(gopts->area_dst +
		       ((char *)msg->arg.pagefault.address -
		       gopts->area_dst_alias));
		for (b = 0; b < gopts->page_size; ++b)
			area[b] = ~area[b];
		continue_range(gopts->uffd, msg->arg.pagefault.address, gopts->page_size,
			       args->apply_wp);
		args->minor_faults++;
	} else {
		/*
		 * Missing page faults.
		 *
		 * Here we force a write check for each of the missing mode
		 * faults.  It's guaranteed because the only threads that
		 * will trigger uffd faults are the locking threads, and
		 * their first instruction to touch the missing page will
		 * always be pthread_mutex_lock().
		 *
		 * Note that here we relied on an NPTL glibc impl detail to
		 * always read the lock type at the entry of the lock op
		 * (pthread_mutex_t.__data.__type, offset 0x10) before
		 * doing any locking operations to guarantee that.  It's
		 * actually not good to rely on this impl detail because
		 * logically a pthread-compatible lib can implement the
		 * locks without types and we can fail when linking with
		 * them.  However since we used to find bugs with this
		 * strict check we still keep it around.  Hopefully this
		 * could be a good hint when it fails again.  If one day
		 * it'll break on some other impl of glibc we'll revisit.
		 */
		if (msg->arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE)
			err("unexpected write fault");

		offset = (char *)(unsigned long)msg->arg.pagefault.address - gopts->area_dst;
		offset &= ~(gopts->page_size-1);

		if (copy_page(gopts, offset, args->apply_wp))
			args->missing_faults++;
	}
}

void *uffd_poll_thread(void *arg)
{
	struct uffd_args *args = (struct uffd_args *)arg;
	uffd_global_test_opts_t *gopts = args->gopts;
	unsigned long cpu = args->cpu;
	struct pollfd pollfd[2];
	struct uffd_msg msg;
	struct uffdio_register uffd_reg;
	int ret;

Annotation

Implementation Notes