Example #1
0
static void insert_ptr(struct btree_page *p, int s,
		       const void *key, struct btree_page *ptr)
{
	const struct btree_def *def = p->def;
	btree_t bt = p->owner;
	int r = p->num_children - s;

	assert (p->height);
	assert (p->num_children < def->branches);
	assert (s >= 0 && s <= p->num_children);

	memmove(PAGE_KEY(p, s + 1), PAGE_KEY(p, s),
		r * def->key_size);
	memmove(PAGE_PTR(p, s + 1), PAGE_PTR(p, s),
		r * sizeof(struct btree_page *));

	memcpy(PAGE_KEY(p, s), key, def->key_size);
	*PAGE_PTR(p, s) = ptr;
	p->num_children++;

	/* Fix up the cursor if we inserted before it, or if we just inserted
	 * the pointer for the active path (as in a split or borrow).
	 */
	if (bt->slot[0] >= 0) {
		if (ptr == bt->path[p->height - 1]) {
			bt->path[p->height] = p;
			bt->slot[p->height] = s;
		} else if (bt->path[p->height] == p &&
			   s <= bt->slot[p->height]) {
			bt->slot[p->height]++;
		}
	}
}
Example #2
0
static void split_page(struct btree_page *op, struct btree_page *np)
{
	const struct btree_def *def = op->def;
	btree_t bt = op->owner;
	const int halfsize = def->branches / 2;

	assert (op->num_children == def->branches);

	memcpy(PAGE_KEY(np, 0), PAGE_KEY(op, halfsize),
	       halfsize * def->key_size);

	if (op->height)
		memcpy(PAGE_PTR(np, 0), PAGE_PTR(op, halfsize),
		       halfsize * sizeof(struct btree_page *));
	else
		memcpy(PAGE_DATA(np, 0), PAGE_DATA(op, halfsize),
		       halfsize * def->data_size);

	op->num_children = halfsize;
	np->num_children = halfsize;

	/* Fix up the cursor if we split an active page */
	if (bt->slot[0] >= 0 && bt->path[op->height] == op &&
	    bt->slot[op->height] > op->num_children) {
		bt->slot[op->height] -= op->num_children;
		bt->path[op->height] = np;
	}
}
Example #3
0
static void delete_item(struct btree_page *p, int s)
{
	const struct btree_def *def = p->def;
	btree_t bt = p->owner;
	int r = p->num_children - s - 1;

	assert (s >= 0 && s < p->num_children);

	memmove(PAGE_KEY(p, s), PAGE_KEY(p, s + 1),
		r * def->key_size);

	if (p->height)
		memmove(PAGE_PTR(p, s), PAGE_PTR(p, s + 1),
			r * sizeof(struct btree_page *));
	else
		memmove(PAGE_DATA(p, s), PAGE_DATA(p, s + 1),
			r * def->data_size);

	p->num_children--;

	/* Fix up the cursor if we deleted before it */
	if (bt->slot[0] >= 0 && bt->path[p->height] == p &&
	    s <= bt->slot[p->height])
		bt->slot[p->height]--;
}
Example #4
0
static void merge_pages(struct btree_page *lower,
			struct btree_page *higher)
{
	const struct btree_def *def = lower->def;
	btree_t bt = lower->owner;

	assert (lower->num_children + higher->num_children < def->branches);

	memcpy(PAGE_KEY(lower, lower->num_children),
	       PAGE_KEY(higher, 0),
	       higher->num_children * def->key_size);

	if (lower->height)
		memcpy(PAGE_PTR(lower, lower->num_children),
		       PAGE_PTR(higher, 0),
		       higher->num_children * sizeof(struct btree_page *));
	else
		memcpy(PAGE_DATA(lower, lower->num_children),
		       PAGE_DATA(higher, 0),
		       higher->num_children * def->data_size);

	lower->num_children += higher->num_children;

	/* Fix up the cursor if we subsumed an active page */
	if (bt->slot[0] >= 0) {
		if (bt->path[higher->height] == higher) {
			bt->path[higher->height] = lower;
			bt->slot[higher->height] += lower->num_children;
		}
	}
}
Example #5
0
static void check_page(struct btree_page *p,
		       const void *lbound, const void *ubound,
		       int height)
{
	const struct btree_def *def = p->def;
	int i;

	assert (p);
	assert (p->height == height);

	if (p != p->owner->root) {
		assert (p->num_children >= def->branches / 2);
		assert (p->num_children <= def->branches);
	}

	for (i = 0; i < p->num_children; i++) {
		const void *key = PAGE_KEY(p, i);
		const void *next_key = ubound;

		if (i + 1 < p->num_children)
			next_key = PAGE_KEY(p, i + 1);

		assert (def->compare(key, lbound) >= 0);
		if (next_key) {
			assert (def->compare(key, next_key) < 0);
		}
		if (ubound) {
			assert (def->compare(key, ubound) < 0);
		}

		if (p->height)
			check_page(*PAGE_PTR(p, i), key, next_key, height - 1);
	}
}
Example #6
0
void btree_clear(btree_t bt)
{
	struct btree_page *p;
	struct btree_page *path_up = 0;

	check_btree(bt);

	/* The cursor will have nothing to point to after this. */
	bt->slot[0] = -1;

	/* First, find the last leaf node, which we can re-use as an
	 * empty root.
	 */
	p = bt->root;
	while (p->height) {
		path_up = p;
		p = *PAGE_PTR(p, p->num_children - 1);
	}

	/* Unlink it from the tree and then destroy everything else. */
	if (path_up) {
		path_up->num_children--;
		destroy_page(bt->root);
	}

	/* Clear it out and make it the new root */
	p->num_children = 0;
	bt->root = p;
}
Example #7
0
int btree_get(btree_t bt, const void *key, void *data)
{
	const struct btree_def *def = bt->def;
	struct btree_page *p = bt->root;
	int h;

	check_btree(bt);

	if (!key)
		return btree_select(bt, NULL, BTREE_READ, NULL, data);

	for (h = bt->root->height; h >= 0; h--) {
		int s = find_key_le(p, key);

		if (h) {
			assert (s >= 0 && s < p->num_children);
			p = *PAGE_PTR(p, s);
		} else if (s >= 0 && !def->compare(key, PAGE_KEY(p, s))) {
			memcpy(data, PAGE_DATA(p, s), def->data_size);
			return 0;
		}
	}

	return 1;
}
Example #8
0
/*
 * This routine gets a long from any process space by following the page
 * tables. NOTE! You should check that the long isn't on a page boundary,
 * and that it is in the task area before calling this: this routine does
 * no checking.
 */
static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr)
{
	pgd_t * pgdir;
	pte_t * pgtable;
	unsigned long page;

repeat:
	pgdir = PAGE_DIR_OFFSET(vma->vm_mm, addr);
	if (pgd_none(*pgdir)) {
		do_no_page(vma, addr, 0);
		goto repeat;
	}
	if (pgd_bad(*pgdir)) {
		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
		pgd_clear(pgdir);
		return 0;
	}
	pgtable = (pte_t *) (PAGE_PTR(addr) + pgd_page(*pgdir));
	if (!pte_present(*pgtable)) {
		do_no_page(vma, addr, 0);
		goto repeat;
	}
	page = pte_page(*pgtable);
/* this is a hack for non-kernel-mapped video buffers and similar */
	if (page >= high_memory)
		return 0;
	page += addr & ~PAGE_MASK;
	return *(unsigned long *) page;
}
Example #9
0
static void cursor_next(btree_t bt)
{
	int h;

	if (bt->slot[0] < 0)
		return;

	/* Ascend until we find a suitable sibling */
	for (h = 0; h <= bt->root->height; h++) {
		struct btree_page *p = bt->path[h];

		if (bt->slot[h] + 1 < p->num_children) {
			bt->slot[h]++;

			while (h > 0) {
				p = *PAGE_PTR(p, bt->slot[h]);
				h--;
				bt->slot[h] = 0;
				bt->path[h] = p;
			}

			return;
		}
	}

	/* Exhausted all levels */
	bt->slot[0] = -1;
}
Example #10
0
static void move_item(struct btree_page *from, int from_pos,
		      struct btree_page *to, int to_pos)
{
	if (from->height)
		insert_ptr(to, to_pos, PAGE_KEY(from, from_pos),
			   *PAGE_PTR(from, from_pos));
	else
		insert_data(to, to_pos, PAGE_KEY(from, from_pos),
			    PAGE_DATA(from, from_pos));

	delete_item(from, from_pos);
}
Example #11
0
static void destroy_page(struct btree_page *p)
{
	if (!p)
		return;

	if (p->height) {
		int i;

		for (i = 0; i < p->num_children; i++)
			destroy_page(*PAGE_PTR(p, i));
	}

	free(p);
}
Example #12
0
static unsigned long get_phys_addr(struct task_struct ** p, unsigned long ptr)
{
	unsigned long page;

	if (!p || !*p || ptr >= TASK_SIZE)
		return 0;
	page = *PAGE_DIR_OFFSET((*p)->tss.cr3,ptr);
	if (!(page & 1))
		return 0;
	page &= PAGE_MASK;
	page += PAGE_PTR(ptr);
	page = *(unsigned long *) page;
	if (!(page & 1))
		return 0;
	page &= PAGE_MASK;
	page += ptr & ~PAGE_MASK;
	return page;
}
Example #13
0
static void cursor_first(btree_t bt)
{
	int h;
	struct btree_page *p = bt->root;

	if (!bt->root->num_children) {
		bt->slot[0] = -1;
		return;
	}

	for (h = bt->root->height; h >= 0; h--) {
		assert (p->num_children > 0);

		bt->path[h] = p;
		bt->slot[h] = 0;

		if (h)
			p = *PAGE_PTR(p, 0);
	}
}
Example #14
0
/*
 * This routine puts a long into any process space by following the page
 * tables. NOTE! You should check that the long isn't on a page boundary,
 * and that it is in the task area before calling this: this routine does
 * no checking.
 *
 * Now keeps R/W state of page so that a text page stays readonly
 * even if a debugger scribbles breakpoints into it.  -M.U-
 */
static void put_long(struct vm_area_struct * vma, unsigned long addr,
	unsigned long data)
{
	pgd_t *pgdir;
	pte_t *pgtable;
	unsigned long page;

repeat:
	pgdir = PAGE_DIR_OFFSET(vma->vm_task, addr);
	if (!pgd_present(*pgdir)) {
		do_no_page(vma, addr, 1);
		goto repeat;
	}
	if (pgd_bad(*pgdir)) {
		printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
		pgd_clear(pgdir);
		return;
	}
	pgtable = (pte_t *) (PAGE_PTR(addr) + pgd_page(*pgdir));
	if (!pte_present(*pgtable)) {
		do_no_page(vma, addr, 1);
		goto repeat;
	}
	page = pte_page(*pgtable);
	if (!pte_write(*pgtable)) {
		do_wp_page(vma, addr, 1);
		goto repeat;
	}
/* this is a hack for non-kernel-mapped video buffers and similar */
	if (page < high_memory) {
		page += addr & ~PAGE_MASK;
		*(unsigned long *) page = data;
	}
/* we're bypassing pagetables, so we have to set the dirty bit ourselves */
/* this should also re-instate whatever read-only mode there was before */
	*pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot));
	invalidate();
}
Example #15
0
static int trace_path(btree_t bt, const void *key,
		      struct btree_page **path, int *slot)
{
	const struct btree_def *def = bt->def;
	struct btree_page *p = bt->root;
	int h;

	for (h = p->height; h >= 0; h--) {
		int s = find_key_le(p, key);

		path[h] = p;
		slot[h] = s;

		if (h) {
			assert (s >= 0);
			p = *PAGE_PTR(p, s);
		} else if (s >= 0 && !def->compare(key, PAGE_KEY(p, s))) {
			return 1;
		}
	}

	return 0;
}
Example #16
0
int btree_delete(btree_t bt, const void *key)
{
	const struct btree_def *def = bt->def;
	const int halfsize = def->branches / 2;
	struct btree_page *path[MAX_HEIGHT] = {0};
	int slot[MAX_HEIGHT] = {0};
	int h;

	check_btree(bt);

	/* Trace a path to the item to be deleted */
	if (!key) {
		if (bt->slot[0] < 0)
			return 1;

		memcpy(path, bt->path, sizeof(path));
		memcpy(slot, bt->slot, sizeof(slot));
	} else if (!trace_path(bt, key, path, slot)) {
		return 1;
	}

	/* Select the next item if we're deleting at the cursor */
	if (bt->slot[0] == slot[0] && bt->path[0] == path[0])
		cursor_next(bt);

	/* Delete from the leaf node. If it's still full enough, then we don't
	 * need to do anything else.
	 */
	delete_item(path[0], slot[0]);
	if (path[0]->num_children >= halfsize)
		return 0;

	/* Trace back up the tree, fixing underfull nodes. If we can fix by
	 * borrowing, do it and we're done. Otherwise, we need to fix by
	 * merging, which may result in another underfull node, and we need
	 * to continue.
	 */
	for (h = 1; h <= bt->root->height; h++) {
		struct btree_page *p = path[h];
		struct btree_page *c = path[h - 1];
		int s = slot[h];

		if (s > 0) {
			/* Borrow/merge from lower page */
			struct btree_page *d = *PAGE_PTR(p, s - 1);

			if (d->num_children > halfsize) {
				move_item(d, d->num_children - 1, c, 0);
				memcpy(PAGE_KEY(p, s), PAGE_KEY(c, 0),
				       def->key_size);
				return 0;
			}

			merge_pages(d, c);
			delete_item(p, s);
			free(c);
		} else {
			/* Borrow/merge from higher page */
			struct btree_page *d = *PAGE_PTR(p, s + 1);

			if (d->num_children > halfsize) {
				move_item(d, 0, c, c->num_children);
				memcpy(PAGE_KEY(p, s + 1),
				       PAGE_KEY(d, 0),
				       def->key_size);
				return 0;
			}

			merge_pages(c, d);
			delete_item(p, s + 1);
			free(d);
		}

		if (p->num_children >= halfsize)
			return 0;
	}

	/* If the root contains only a single pointer to another page,
	 * shrink the tree. This does not affect the cursor.
	 */
	if (bt->root->height && bt->root->num_children == 1) {
		struct btree_page *old = bt->root;

		bt->root = *PAGE_PTR(old, 0);
		free(old);
	}

	return 0;
}
Example #17
0
int btree_put(btree_t bt, const void *key, const void *data)
{
	const struct btree_def *def = bt->def;
	struct btree_page *new_root = NULL;
	struct btree_page *path_new[MAX_HEIGHT] = {0};
	struct btree_page *path_old[MAX_HEIGHT] = {0};
	int slot_old[MAX_HEIGHT] = {0};
	int h;

	check_btree(bt);

	/* Special case: cursor overwrite */
	if (!key) {
		if (bt->slot[0] < 0) {
			fprintf(stderr, "btree: put at invalid cursor\n");
			return -1;
		}

		memcpy(PAGE_DATA(bt->path[0], bt->slot[0]), data,
		       def->data_size);
		return 1;
	}

	/* Find a path down the tree that leads to the page which should
	 * contain this datum (though the page might be too big to hold it).
	 */
	if (trace_path(bt, key, path_old, slot_old)) {
		/* Special case: overwrite existing item */
		memcpy(PAGE_DATA(path_old[0], slot_old[0]), data,
		       def->data_size);
		return 1;
	}

	/* Trace from the leaf up. If the leaf is at its maximum size, it will
	 * need to split, and cause a pointer to be added in the parent page
	 * of the same node (which may in turn cause it to split).
	 */
	for (h = 0; h <= bt->root->height; h++) {
		if (path_old[h]->num_children < def->branches)
			break;

		path_new[h] = allocate_page(bt, h);
		if (!path_new[h])
			goto fail;
	}

	/* If the split reaches the top (i.e. the root splits), then we need
	 * to allocate a new root node.
	 */
	if (h > bt->root->height) {
		if (h >= MAX_HEIGHT) {
			fprintf(stderr, "btree: maximum height exceeded\n");
			goto fail;
		}

		new_root = allocate_page(bt, h);
		if (!new_root)
			goto fail;
	}

	/* Trace up to one page above the split. At each page that needs
	 * splitting, copy the top half of keys into the new page. Also,
	 * insert a key into one of the pages at all pages from the leaf
	 * to the page above the top of the split.
	 */
	for (h = 0; h <= bt->root->height; h++) {
		int s = slot_old[h] + 1;
		struct btree_page *p = path_old[h];

		/* If there's a split at this level, copy the top half of
		 * the keys from the old page to the new one. Check to see
		 * if the position we were going to insert into is in the
		 * old page or the new one.
		 */
		if (path_new[h]) {
			split_page(path_old[h], path_new[h]);

			if (s > p->num_children) {
				s -= p->num_children;
				p = path_new[h];
			}
		}

		/* Insert the key in the appropriate page */
		if (h)
			insert_ptr(p, s, PAGE_KEY(path_new[h - 1], 0),
				   path_new[h - 1]);
		else
			insert_data(p, s, key, data);

		/* If there was no split at this level, there's nothing to
		 * insert higher up, and we're all done.
		 */
		if (!path_new[h])
			return 0;
	}

	/* If we made it this far, the split reached the top of the tree, and
	 * we need to grow it using the extra page we allocated.
	 */
	assert (new_root);

	if (bt->slot[0] >= 0) {
		/* Fix up the cursor, if active */
		bt->slot[new_root->height] =
			bt->path[bt->root->height] == new_root ? 1 : 0;
		bt->path[new_root->height] = new_root;
	}

	memcpy(PAGE_KEY(new_root, 0), def->zero, def->key_size);
	*PAGE_PTR(new_root, 0) = path_old[h - 1];
	memcpy(PAGE_KEY(new_root, 1), PAGE_KEY(path_new[h - 1], 0),
	       def->key_size);
	*PAGE_PTR(new_root, 1) = path_new[h - 1];
	new_root->num_children = 2;
	bt->root = new_root;

	return 0;

 fail:
	for (h = 0; h <= bt->root->height; h++)
		if (path_new[h])
			free(path_new[h]);
	return -1;
}