Path: blob/master/tools/testing/radix-tree/regression2.c
26285 views
// SPDX-License-Identifier: GPL-2.01/*2* Regression23* Description:4* Toshiyuki Okajima describes the following radix-tree bug:5*6* In the following case, we can get a hangup on7* radix_radix_tree_gang_lookup_tag_slot.8*9* 0. The radix tree contains RADIX_TREE_MAP_SIZE items. And the tag of10* a certain item has PAGECACHE_TAG_DIRTY.11* 1. radix_tree_range_tag_if_tagged(, start, end, , PAGECACHE_TAG_DIRTY,12* PAGECACHE_TAG_TOWRITE) is called to add PAGECACHE_TAG_TOWRITE tag13* for the tag which has PAGECACHE_TAG_DIRTY. However, there is no tag with14* PAGECACHE_TAG_DIRTY within the range from start to end. As the result,15* There is no tag with PAGECACHE_TAG_TOWRITE but the root tag has16* PAGECACHE_TAG_TOWRITE.17* 2. An item is added into the radix tree and then the level of it is18* extended into 2 from 1. At that time, the new radix tree node succeeds19* the tag status of the root tag. Therefore the tag of the new radix tree20* node has PAGECACHE_TAG_TOWRITE but there is not slot with21* PAGECACHE_TAG_TOWRITE tag in the child node of the new radix tree node.22* 3. The tag of a certain item is cleared with PAGECACHE_TAG_DIRTY.23* 4. All items within the index range from 0 to RADIX_TREE_MAP_SIZE - 1 are24* released. (Only the item which index is RADIX_TREE_MAP_SIZE exist in the25* radix tree.) As the result, the slot of the radix tree node is NULL but26* the tag which corresponds to the slot has PAGECACHE_TAG_TOWRITE.27* 5. radix_tree_gang_lookup_tag_slot(PAGECACHE_TAG_TOWRITE) calls28* __lookup_tag. __lookup_tag returns with 0. And __lookup_tag doesn't29* change the index that is the input and output parameter. Because the 1st30* slot of the radix tree node is NULL, but the tag which corresponds to31* the slot has PAGECACHE_TAG_TOWRITE.32* Therefore radix_tree_gang_lookup_tag_slot tries to get some items by33* calling __lookup_tag, but it cannot get any items forever.34*35* The fix is to change that radix_tree_tag_if_tagged doesn't tag the root tag36* if it doesn't set any tags within the specified range.37*38* Running:39* This test should run to completion immediately. The above bug would cause it40* to hang indefinitely.41*42* Upstream commit:43* Not yet44*/45#include <linux/kernel.h>46#include <linux/gfp.h>47#include <linux/slab.h>48#include <linux/radix-tree.h>49#include <stdlib.h>50#include <stdio.h>5152#include "regression.h"53#include "test.h"5455#define PAGECACHE_TAG_DIRTY XA_MARK_056#define PAGECACHE_TAG_WRITEBACK XA_MARK_157#define PAGECACHE_TAG_TOWRITE XA_MARK_25859static RADIX_TREE(mt_tree, GFP_KERNEL);60unsigned long page_count = 0;6162struct page {63unsigned long index;64};6566static struct page *page_alloc(void)67{68struct page *p;69p = malloc(sizeof(struct page));70p->index = page_count++;7172return p;73}7475void regression2_test(void)76{77int i;78struct page *p;79int max_slots = RADIX_TREE_MAP_SIZE;80unsigned long int start, end;81struct page *pages[1];8283printv(1, "running regression test 2 (should take milliseconds)\n");84/* 0. */85for (i = 0; i <= max_slots - 1; i++) {86p = page_alloc();87radix_tree_insert(&mt_tree, i, p);88}89radix_tree_tag_set(&mt_tree, max_slots - 1, PAGECACHE_TAG_DIRTY);9091/* 1. */92start = 0;93end = max_slots - 2;94tag_tagged_items(&mt_tree, start, end, 1,95PAGECACHE_TAG_DIRTY, PAGECACHE_TAG_TOWRITE);9697/* 2. */98p = page_alloc();99radix_tree_insert(&mt_tree, max_slots, p);100101/* 3. */102radix_tree_tag_clear(&mt_tree, max_slots - 1, PAGECACHE_TAG_DIRTY);103104/* 4. */105for (i = max_slots - 1; i >= 0; i--)106free(radix_tree_delete(&mt_tree, i));107108/* 5. */109// NOTE: start should not be 0 because radix_tree_gang_lookup_tag_slot110// can return.111start = 1;112end = max_slots - 2;113radix_tree_gang_lookup_tag_slot(&mt_tree, (void ***)pages, start, end,114PAGECACHE_TAG_TOWRITE);115116/* We remove all the remained nodes */117free(radix_tree_delete(&mt_tree, max_slots));118119BUG_ON(!radix_tree_empty(&mt_tree));120121printv(1, "regression test 2, done\n");122}123124125