Notes
main
main
  • Introduction
  • linuxKernel
    • tips
    • make_help
    • old linux
      • compile_linux0.11
      • TestEnvironment
      • load_setup
      • get_hard_data
    • list
    • plist
    • fifo
    • idr
    • xarray
    • rbtree
    • maple_tree
    • syscall
    • bitmap
    • page
    • page_flags
    • page_size
    • page mapcount
    • page refcount
    • folio
    • slub
      • proc_slabinfo
      • slub_theory
      • kmalloc_kfree
      • kmem_cache
      • slab_alloc
      • slab_free
      • proc_meminfo_SReclaimable_SReclaimable
    • vmalloc
    • brk
    • mmap
    • mremap
    • mprotect
    • madvise
    • read
    • write
    • shmem
    • huge_page
    • page_fault
    • rmap
    • lru
    • multi-gen-LRU
    • page_reclaim
    • page_cache
    • page_table
    • rcu
    • kvm
    • aarch64_boot
    • tracing_system
    • cache_coherence_and_memory_consistency
    • cpu_speculates
    • mmap_lock
    • per-vma_lock
    • cgroup
    • symbol
    • campact
    • page_ext
    • mempool
    • kernelstack
    • filesystem
    • io_stack
    • workingset
    • ioremap
    • sched_period
  • linuxDebug
    • openocd_openjtag
    • i2c_tools
    • objdump
    • addr2line
    • gdb_useage
    • debug_linux_kernel_via_gdb
    • debug_linux_module_via_gdb
    • early_boot
    • sequentially_execute
    • dynamic_debug
    • research_linuxKernel_by_patch
    • tracefs
    • ebpf
    • bpftrace
    • perf
    • flame_graph
    • crash
    • ASAN_HWASAN_MTE_check_mem_bug
    • page_owner
    • vmtouch
    • fio
    • benchmark
  • linuxSystem
    • common
      • system_version
      • procfs
      • proc_sys_vm
      • cmd_ps
      • makefile
      • file_descriptor
      • psi
      • ulimit
      • top
      • delay_accounting
    • ubuntu
      • custom_kernel
      • get_cmd_src
      • record_ssh_info
      • log
      • run_custom_script
      • repo
      • cockpit
      • nfs
      • tftp
      • misc
    • fedora
      • system_upgrade
      • custom_kernel
      • lvextend
      • yt-dlp
      • jellyfin
  • linuxDriver
    • i2c_peripherals_driver
    • spi_peripherals_driver
    • gpio_subsystem
    • IRQ_driver
    • blockIO_unblockIO_async
    • linux_own_driver
    • misc_device
    • input_device
    • timer
    • atomic_spinlock_semaphore_mutex
    • lcd
    • touch_screen
    • debugfs
    • v4l2
    • mmap
  • hardware
    • paging_mmu_pt
    • iommu
  • process_thread_scheduler
    • scheduler01
    • scheduler02
    • scheduler03
    • scheduler04
    • scheduler05
    • scheduler06
  • memory_management
    • mm1
    • mm2
    • mm3
    • mm4
    • mm5
  • input_output_filesystem
    • io_fs_01
    • io_fs_02
    • io_fs_03
    • io_fs_04
  • lock_and_lockup_detector
    • general_lock
    • hung_task
    • softLockup_hardLockup
    • crash_experiment
  • MIT_6.S081
    • 6.S081_Operating_System_Engineering
    • Schedule.md
    • Class
      • Overview
      • Administrivia
    • Labs
      • Tools
      • Guidance
      • startup
      • syscall
      • page_table
      • Calling_Convention
      • traps
    • xv6
      • xv6
    • References.md
  • qemu
    • qemu_buildroot
    • qemu_busybox.md
    • Serial.md
    • demo_mini2440
      • 0_compilation_error_summary
      • 1_compilation_steps
      • 2_operation_mode
      • 3_transplant_tools_libraries
      • 4_tools_use
      • reference_website
  • tools
    • getKernelSourceCodeList
    • nat
    • shell
    • translating
    • YouCompleteMe
    • cscope
    • global
    • vscode
    • vim
    • binary
    • markdown
    • draw
    • git
    • tig
    • tmux
    • mail_client
    • download_patchset_from_LKML
    • minicom
    • clash
  • other
    • interview
    • interview_c_base
    • know_dontknow
    • Stop-Ask-Questions-The-Stupid-Ways
    • How-To-Ask-Questions-The-Smart-Way
    • docker
    • buildroot
    • rv32_to_rv64
Powered by GitBook
On this page
  • 简介
  • 普通 API
  • 高级 API
  • 参考

Was this helpful?

  1. linuxKernel

maple_tree

简介

Maple Tree 是一种基于 B-Tree 的数据类型,优化了存储非重叠范围,包括范围大小为1, 并且使用简单,不需要用户编写查找函数,以 cache-efficient 方式,迭代 entry range 和 获得上一个/下一个 entry。也支持 RCU-safe 模式,允许同时 read and write,但是 writer 必须通过 lock 来同步操作,默认是 spinlock,或者使用外部锁

Maple Tree 维护一个占用内存很小的结构体,可以高效地利用处理器 cache。大多数用户 使用普通 API 即可,高级 API 的存在是为了更复杂的场景,它最重要的用途,就是跟踪 虚拟内存区域 VMA。

Maple Tree 存储范围为 [0, ULONG_MAX],其预留值的最低两位为 10 并且低于 4096 (即2、6、10 .. 4094),只供内部使用。 如果 entry 使用预留值,用户能够使用 xa_mk_value() 来转换,后面再使用 xa_to_value() 转换回来,但是用户使用预留值, 只可以使用高级API,普通 API 会被阻止。

Maple Tree 也能够配置支持查找指定 gap 大小或更大。

预分配 Node 也支持使用高级 API,当内存不足时,这是有用的,能够保证在指定范围内的 存储操作是成功,分配的 Node 是相对较小,大约 256B。

适用于需要高效管理连续或非连续范围数据的场景,如 VMA

普通 API

能够使用静态分配 DEFINE_MTREE() 或者 动态分配 mt_init() 来初始化 maple tree, 对于刚初始化完成的 maple tree,在 [0, ULONG_MAX] 范围内都只存储 NULL 指针。 当前支持的 maple tree 类型有 allocation tree and regular tree, regular tree 对于内部 nodes 有较高的平衡因素,allocation tree 有较低的平衡因素, 但是允许用户在范围 [0, ULONG_MAX] 中查找指定 gap 大小或更大。 allocation tree 需要在初始化时指定 MT_FLAGS_ALLOC_RANGE 标志

我们能够使用 mtree_store() or mtree_store_range() 来存储 entry, mtree_store() 将用新 entry 覆盖任何旧 entry, 成功返回0,失败返回错误码, mtree_store_range() 有相同的原理,但是它是覆盖一个范围内的 entry。 mtree_load() 获得指定 index 的 entry,mtree_erase() 能够擦除一个范围内的 entry, 或者 mtree_store() 存储 NULLl 来擦除一个范围内的 entry

如果只想要存储新 entry 到旧 entry 等于 NULL 的 range or index,能够使用 mtree_insert_range() or mtree_insert(),如果返回 -EEXIST,代表这个范围不为空

通过 mt_find() 能够查找 entry

前提必须提供一个临时变量来存储 cursor,通过 mt_for_each() 能够获得某范围内的所有 entry, 将 [0, ULONG_MAX] 作为范围能够获得整个 tree 的每一个 entry,如果调用者在遍历期间已经 hold lock,使用高级API mas_for_each() 是更好的选择

最后,能够通过 mtree_destroy() 删除 maple tree 所有 entry,如果 entry 是一个指针, 你可能需要先释放 entry 实体

分配 Nodes

分配 Nodes 操作是内部实现的代码,请看高级 API 分配 Nodes 部分

Locking

你不必担心 locking 问题,请看高级API locking

Maple tree 使用 RCU 和 内部 spinlock 来同步访问

Takes RCU read lock:

mtree_load()
mt_find()
mt_for_each()
mt_next()
mt_prev()

Takes ma_lock internally:

mtree_store()
mtree_store_range()
mtree_insert()
mtree_insert_range()
mtree_erase()
mtree_destroy()
mt_set_in_rcu()
mt_clear_in_rcu()

如果想利用内部 lock 来保护你存储在 maple tree 中的数据,可以在调用 mtree_load() 之前 调用 mtree_lock(),然后在调用 mtree_unlock() 之前对 object 进行 reference 计数, 这将防止在 unlock object 并且减少 reference 计数时,从 maple tree 中删除 object。 也可以使用 RCU 来避免取消 reference 来释放内存,但对此解释超出了本文档的范围。

Test demo

源码:

#include <linux/maple_tree.h>

void test_mt_normal(void)
{
	void *tmp;
	int value = 0x11;

	DEFINE_MTREE(mt);

	tmp = mtree_load(&mt, 0x1);
	pr_info("tmp %p\n", tmp);

	mtree_store(&mt, 0x1, &value, GFP_KERNEL);
	mtree_store(&mt, 0x2, xa_mk_value(0x22), GFP_KERNEL);

	tmp = mtree_load(&mt, 0x1);
	pr_info("tmp 0x%x\n", *(int *)tmp);

	tmp = mtree_load(&mt, 0x2);
	pr_info("tmp 0x%lx\n", xa_to_value(tmp));
}

执行结果:

$ insmod mapletree/mt.ko ; dmesg | tail
[   20.458599] mt: loading out-of-tree module taints kernel.
[   20.465357] tmp 0000000000000000
[   20.465665] tmp 0x11
[   20.465686] tmp 0x22

高级 API

高级 API 提供更加灵活和更好性能的接口,但是比较难使用和安全措施更少。在使用高级 API 时, 用户自己必须关心 locking 的使用,能够使用 ma_lock, RCU or external lock 来保护。也可以 在同一个 tree 混合使用高级与普通 API,只要 locking 是兼容就可以,普通 API 是基于高级 API 实现的。

高级 API 是围绕 ma_state 结构体实现的,简称 mas,为了内部与外部用户的容易使用, ma_state 结构体保持跟踪 tree 的操作。

初始化操作,与普通 API 是一样的,请参考之前的说明

maple tree ma_state 的跟踪范围 [start, end] 分别对应 mas->index and mas->last

mas_walk() 从 mas->index 开始 walk maple tree,并且根据 entry 范围 设置 mas->index and mas->last

通过 mas_store() 存储 entry,将用 new entry 覆盖任何 old entry,然后返回第一个 被覆盖 old entry,其中覆盖范围是通过 ma_state 的 index and last 成员来指定。

通过 mas_erase() 来擦除某个范围,由 ma_state 的 index and last 成员来描述擦除范围。 返回在原来位置上的 entry

通过 mas_for_each() 来 walk 某个范围内的所有 entry,如果想要 walk 整个 tree 的 所有 entry,设置 [0, ULONG_MAX] 作为范围,进行调用 mas_for_each()。 如果需要定期地 drop lock,看 mas_pause()

如果将 maple tree 作为链表,使用 mas_next() and mas_prev()。 mas_next() 返回下一个 entry,entry 是在 index 之后, mas_prev() 返回前一个 entry,entry 是在 index 之前。

第一次调用 mas_find() 时,将返回在 index 或 后面第一个存在的 entry,后面再一次调用时, 将返回下一个 entry。

第一次调用 mas_find_rev() 时,将返回在 last 或 前面第一个存在的entry,后面再一次调用时, 将返回上一个 entry

如果用户需要在操作期间 yield lock,必须调用 mas_pause() 来暂停

当使用 allocation tree 时,如果想要在范围内查找一个 gap,调用 mas_empty_area() or mas_empty_area_rev()。mas_empty_area() 查找一个gap,从最低的 index 开始,到最大值, mas_empty_area_rev() 查找一个 gap,从最高的 index 开始,到最小值。

高级分配 Nodes

分配 Nodes 通常是在 tree 内部进行处理的,然而,如果需要在写操作之前分配 Nodes, 调用 mas_expected_entries() 将分配足够的 Nodes 来插入到提供的范围中,这也将 导致 tree 进行批量插入模式。一旦插入操作是完成后,调用 mas_destroy() 来释放 没有使用的 Nodes

高级 Locking

maple tree 默认是使用 spinlock,但是外部 lock 使用是更好的。使用外部 lock, 需要指定 MT_FLAGS_LOCK_EXTERN 标志来初始化 tree,通常将外部 lock 作为参数, 使用 MTREE_INIT_EXT() 来定义

Test demo

源码:

#include <linux/maple_tree.h>

void test_mt_advanced(void)
{
	DEFINE_MTREE(mt);
	MA_STATE(mas, &mt, 0, 0);
	int value = 0x11;
	void *tmp;

	mas_set(&mas, 0x1);
	mas_store(&mas, &value);
	tmp = mas_walk(&mas);
	pr_info("tmp 0x%x\n", *(int *)tmp);

	mas_set(&mas, 0x2);
	mas_store(&mas, xa_mk_value(0x22));
	tmp = mas_walk(&mas);
	pr_info("tmp 0x%lx\n", xa_to_value(tmp));
}

执行结果:

$ insmod mapletree/mt.ko ; dmesg | tail
[   50.528376] mt: loading out-of-tree module taints kernel.
[   50.534884] tmp 0x11
[   50.534949] tmp 0x22

参考

Documentation/core-api/maple_tree.rst

PreviousrbtreeNextsyscall

Last updated 9 months ago

Was this helpful?