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]++; } } }
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; } }
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]--; }
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; } } }
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); } }
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; }
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; }
/* * 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; }
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; }
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); }
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); }
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; }
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); } }
/* * 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(); }
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; }
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; }
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; }