void align_size_offset(size_t size, off_t offset, size_t *aligned_size, off_t *aligned_offset) { *aligned_offset = align_start(offset); off_t end_offset = offset + size; off_t aligned_end_offset = align_end(end_offset); *aligned_size = (size_t)(aligned_end_offset - *aligned_offset); }
/* * this is pretty ugly, but it is used to find a free spot in the * tree for a new iommu allocation. We start from a given * hint and try to find an aligned range of a given size. * * Send the slot pointer, and we'll update it with the location * we found. * * This will return -EAGAIN if we found a good spot but someone * raced in and allocated it before we could. This gives the * caller the chance to update their hint. * * This will return -EEXIST if we couldn't find anything at all * * returns 0 if all went well, or some other negative error * if things went badly. */ int skiplist_insert_hole(struct sl_list *list, unsigned long hint, unsigned long limit, unsigned long size, unsigned long align, struct sl_slot *slot, gfp_t gfp_mask) { unsigned long last_end = 0; struct sl_node *p; struct sl_leaf *leaf; int i; int ret = -EEXIST; int preload_token; int pending_level; preload_token = skiplist_preload(list, gfp_mask); if (preload_token < 0) { return preload_token; } pending_level = pending_insert_level(preload_token); /* step one, lets find our hint */ rcu_read_lock(); again: last_end = max(last_end, hint); last_end = align_start(last_end, align); slot->key = align_start(hint, align); slot->size = size; leaf = __skiplist_lookup_leaf(list, &p, hint, 1); if (!p) p = list->head; if (leaf && !verify_key_in_leaf(leaf, hint, size)) { goto again; } again_lock: sl_lock_node(p); if (sl_node_dead(p)) { sl_unlock_node(p); goto again; } if (p != list->head) { leaf = sl_entry(p); /* * the leaf we found was past the hint, * go back one */ if (sl_max_key(leaf) > hint) { struct sl_node *locked = p; p = p->ptrs[0].prev; sl_unlock_node(locked); goto again_lock; } last_end = align_start(sl_max_key(sl_entry(p)), align); } /* * now walk at level 0 and find a hole. We could use lockless walks * if we wanted to bang more on the insertion code, but this * instead holds the lock on each node as we inspect it * * This is a little sloppy, insert will return -eexist if we get it * wrong. */ while(1) { leaf = sl_next_leaf(list, p, 0); if (!leaf) break; /* p and leaf are locked */ sl_lock_node(&leaf->node); if (last_end > sl_max_key(leaf)) goto next; for (i = 0; i < leaf->nr; i++) { if (last_end > leaf->keys[i]) continue; if (leaf->keys[i] - last_end >= size) { if (last_end + size > limit) { sl_unlock_node(&leaf->node); goto out_rcu; } sl_unlock_node(p); slot->key = last_end; slot->size = size; goto try_insert; } last_end = leaf->keys[i] + leaf->ptrs[i]->size; last_end = align_start(last_end, align); if (last_end + size > limit) { sl_unlock_node(&leaf->node); goto out_rcu; } } next: sl_unlock_node(p); p = &leaf->node; } if (last_end + size <= limit) { sl_unlock_node(p); slot->key = last_end; slot->size = size; goto try_insert; } out_rcu: /* we've failed */ sl_unlock_node(p); rcu_read_unlock(); preempt_enable(); return ret; try_insert: /* * if the pending_level is zero or there is room in the * leaf, we're ready to insert. This is true most of the * time, and we won't have to drop our lock and give others * the chance to race in and steal our spot. */ if (leaf && (pending_level == 0 || leaf->nr < SKIP_KEYS_PER_NODE) && !sl_node_dead(&leaf->node) && (slot->key >= sl_min_key(leaf) && slot->key + slot->size <= sl_max_key(leaf))) { ret = find_or_add_key(list, slot->key, size, leaf, slot, preload_token, NULL); rcu_read_unlock(); goto out; } /* * no such luck, drop our lock and try the insert the * old fashioned way */ if (leaf) sl_unlock_node(&leaf->node); rcu_read_unlock(); ret = skiplist_insert(list, slot, preload_token, NULL); out: /* * if we get an EEXIST here, it just means we lost the race. * return eagain to the caller so they can update the hint */ if (ret == -EEXIST) ret = -EAGAIN; preempt_enable(); return ret; }