18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * bitext.c: kernel little helper (of bit shuffling variety). 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2002 Pete Zaitcev <zaitcev@yahoo.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * The algorithm to search a zero bit string is geared towards its application. 88c2ecf20Sopenharmony_ci * We expect a couple of fixed sizes of requests, so a rotating counter, reset 98c2ecf20Sopenharmony_ci * by align size, should provide fast enough search while maintaining low 108c2ecf20Sopenharmony_ci * fragmentation. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/bitext.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/** 198c2ecf20Sopenharmony_ci * bit_map_string_get - find and set a bit string in bit map. 208c2ecf20Sopenharmony_ci * @t: the bit map. 218c2ecf20Sopenharmony_ci * @len: requested string length 228c2ecf20Sopenharmony_ci * @align: requested alignment 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Returns offset in the map or -1 if out of space. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Not safe to call from an interrupt (uses spin_lock). 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ciint bit_map_string_get(struct bit_map *t, int len, int align) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci int offset, count; /* siamese twins */ 318c2ecf20Sopenharmony_ci int off_new; 328c2ecf20Sopenharmony_ci int align1; 338c2ecf20Sopenharmony_ci int i, color; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (t->num_colors) { 368c2ecf20Sopenharmony_ci /* align is overloaded to be the page color */ 378c2ecf20Sopenharmony_ci color = align; 388c2ecf20Sopenharmony_ci align = t->num_colors; 398c2ecf20Sopenharmony_ci } else { 408c2ecf20Sopenharmony_ci color = 0; 418c2ecf20Sopenharmony_ci if (align == 0) 428c2ecf20Sopenharmony_ci align = 1; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci align1 = align - 1; 458c2ecf20Sopenharmony_ci if ((align & align1) != 0) 468c2ecf20Sopenharmony_ci BUG(); 478c2ecf20Sopenharmony_ci if (align < 0 || align >= t->size) 488c2ecf20Sopenharmony_ci BUG(); 498c2ecf20Sopenharmony_ci if (len <= 0 || len > t->size) 508c2ecf20Sopenharmony_ci BUG(); 518c2ecf20Sopenharmony_ci color &= align1; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci spin_lock(&t->lock); 548c2ecf20Sopenharmony_ci if (len < t->last_size) 558c2ecf20Sopenharmony_ci offset = t->first_free; 568c2ecf20Sopenharmony_ci else 578c2ecf20Sopenharmony_ci offset = t->last_off & ~align1; 588c2ecf20Sopenharmony_ci count = 0; 598c2ecf20Sopenharmony_ci for (;;) { 608c2ecf20Sopenharmony_ci off_new = find_next_zero_bit(t->map, t->size, offset); 618c2ecf20Sopenharmony_ci off_new = ((off_new + align1) & ~align1) + color; 628c2ecf20Sopenharmony_ci count += off_new - offset; 638c2ecf20Sopenharmony_ci offset = off_new; 648c2ecf20Sopenharmony_ci if (offset >= t->size) 658c2ecf20Sopenharmony_ci offset = 0; 668c2ecf20Sopenharmony_ci if (count + len > t->size) { 678c2ecf20Sopenharmony_ci spin_unlock(&t->lock); 688c2ecf20Sopenharmony_ci/* P3 */ printk(KERN_ERR 698c2ecf20Sopenharmony_ci "bitmap out: size %d used %d off %d len %d align %d count %d\n", 708c2ecf20Sopenharmony_ci t->size, t->used, offset, len, align, count); 718c2ecf20Sopenharmony_ci return -1; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (offset + len > t->size) { 758c2ecf20Sopenharmony_ci count += t->size - offset; 768c2ecf20Sopenharmony_ci offset = 0; 778c2ecf20Sopenharmony_ci continue; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci i = 0; 818c2ecf20Sopenharmony_ci while (test_bit(offset + i, t->map) == 0) { 828c2ecf20Sopenharmony_ci i++; 838c2ecf20Sopenharmony_ci if (i == len) { 848c2ecf20Sopenharmony_ci bitmap_set(t->map, offset, len); 858c2ecf20Sopenharmony_ci if (offset == t->first_free) 868c2ecf20Sopenharmony_ci t->first_free = find_next_zero_bit 878c2ecf20Sopenharmony_ci (t->map, t->size, 888c2ecf20Sopenharmony_ci t->first_free + len); 898c2ecf20Sopenharmony_ci if ((t->last_off = offset + len) >= t->size) 908c2ecf20Sopenharmony_ci t->last_off = 0; 918c2ecf20Sopenharmony_ci t->used += len; 928c2ecf20Sopenharmony_ci t->last_size = len; 938c2ecf20Sopenharmony_ci spin_unlock(&t->lock); 948c2ecf20Sopenharmony_ci return offset; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci count += i + 1; 988c2ecf20Sopenharmony_ci if ((offset += i + 1) >= t->size) 998c2ecf20Sopenharmony_ci offset = 0; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_civoid bit_map_clear(struct bit_map *t, int offset, int len) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci int i; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (t->used < len) 1088c2ecf20Sopenharmony_ci BUG(); /* Much too late to do any good, but alas... */ 1098c2ecf20Sopenharmony_ci spin_lock(&t->lock); 1108c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 1118c2ecf20Sopenharmony_ci if (test_bit(offset + i, t->map) == 0) 1128c2ecf20Sopenharmony_ci BUG(); 1138c2ecf20Sopenharmony_ci __clear_bit(offset + i, t->map); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci if (offset < t->first_free) 1168c2ecf20Sopenharmony_ci t->first_free = offset; 1178c2ecf20Sopenharmony_ci t->used -= len; 1188c2ecf20Sopenharmony_ci spin_unlock(&t->lock); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_civoid bit_map_init(struct bit_map *t, unsigned long *map, int size) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci bitmap_zero(map, size); 1248c2ecf20Sopenharmony_ci memset(t, 0, sizeof *t); 1258c2ecf20Sopenharmony_ci spin_lock_init(&t->lock); 1268c2ecf20Sopenharmony_ci t->map = map; 1278c2ecf20Sopenharmony_ci t->size = size; 1288c2ecf20Sopenharmony_ci} 129