#include <sys/types.h>
#include <sys/queue.h>
#include <bitmap.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <tlm.h>
#define BMAP_HASH_SIZE 64
#define BMAP_CHUNK_MAX 128
#define BMAP_MAX 256
#define BMAP_WSIZE (sizeof (int))
#define BMAP_BPW (BMAP_WSIZE * 8)
#define BMAP_BPW_SHIFT 5
#define BMAP_BPW_MASK (~(~0ULL << BMAP_BPW_SHIFT))
#define BMAP_CHUNK_WORDS 1024
#define BMAP_CHUNK_BYTES (BMAP_CHUNK_WORDS * BMAP_WSIZE)
#define BMAP_CHUNK_BITS (BMAP_CHUNK_WORDS * BMAP_BPW)
#define BMAP_CHUNK_NO(p) ((p) / BMAP_CHUNK_BITS)
#define BMAP_CHUNK_OFF(p) (BMAP_CHUNK_NO(p) * BMAP_CHUNK_BITS)
#define BMAP_BINIT_ONES 0x00000001
#define BMAP_INUSE 0x00000002
#define BMAP_SET_FLAGS(b, f) ((b)->bm_flags |= (f))
#define BMAP_UNSET_FLAGS(b, f) ((b)->bm_flags &= ~(f))
#define BMAP_IS_INIT_ONES(b) ((b)->bm_flags & BMAP_BINIT_ONES)
#define BMAP_IS_INUSE(b) ((b)->bm_flags & BMAP_INUSE)
#define HASH(p) (((p) / BMAP_CHUNK_BITS) % BMAP_HASH_SIZE)
#define ROUNDUP(n, d) (((n) + (d) - 1) / (d))
#define MEM_LEN(l) (ROUNDUP((l), BMAP_BPW) * BMAP_WSIZE)
#define BMAP_CSET_DIRTY(cp) (cp)->c_flags |= BMAP_CDIRTY
#define BMAP_CDIRTY 0x00000001
#define BMAP_CIS_DIRTY(cp) ((cp)->c_flags & BMAP_CDIRTY)
#define BMAP_NEW_CHUNK 1
#define BMAP_OLD_CHUNK 0
typedef struct dbmap_chunk {
TAILQ_ENTRY(dbmap_chunk) c_hash;
TAILQ_ENTRY(dbmap_chunk) c_lru;
uint_t c_flags;
u_quad_t c_off;
uint_t c_clen;
uint_t c_mlen;
uint_t *c_bmp;
} dbmap_chunk_t;
TAILQ_HEAD(dbmap_list, dbmap_chunk);
typedef struct dbmap_list dbmap_list_t;
typedef struct dbitmap {
char *bm_fname;
int bm_fd;
uint_t bm_flags;
u_quad_t bm_len;
uint_t bm_cmax;
uint_t bm_ccur;
dbmap_list_t bm_hash[BMAP_HASH_SIZE];
dbmap_list_t bm_lru;
} dbitmap_t;
static dbitmap_t dbitmap[BMAP_MAX];
typedef struct bmap_chunk {
TAILQ_ENTRY(bmap_chunk) c_hash;
u_quad_t c_off;
uint_t c_clen;
uint_t c_mlen;
uint_t *c_bmp;
} bmap_chunk_t;
TAILQ_HEAD(bmap_list, bmap_chunk);
typedef struct bmap_list bmap_list_t;
typedef struct bitmap {
uint_t bm_flags;
u_quad_t bm_len;
uint_t bm_cmax;
uint_t bm_ccur;
bmap_list_t bm_hash[BMAP_HASH_SIZE];
} bitmap_t;
typedef struct bitmap_stats {
ulong_t bs_alloc_cnt;
ulong_t bs_alloc_size;
ulong_t bs_free_cnt;
ulong_t bs_set_applied;
ulong_t bs_unset_applied;
ulong_t bs_cache_hit;
ulong_t bs_cache_miss;
ulong_t bs_chunk_new;
ulong_t bs_chunk_flush;
ulong_t bs_chunk_reclaim;
u_quad_t bs_get;
u_quad_t bs_get_bits;
u_quad_t bs_set;
u_quad_t bs_set_bits;
u_quad_t bs_unset;
u_quad_t bs_unset_bits;
} bitmap_stats_t;
static bitmap_t bitmap[BMAP_MAX];
bitmap_stats_t bitmap_stats;
static bitmap_t *
bmd2bmp(int bmd)
{
if (bmd < 0 || bmd >= BMAP_MAX)
return (NULL);
return (&bitmap[bmd]);
}
static int
bmd_alloc(void)
{
int i;
bitmap_t *bmp;
bmp = bitmap;
for (i = 0; i < BMAP_MAX; bmp++, i++)
if (!BMAP_IS_INUSE(bmp)) {
BMAP_SET_FLAGS(bmp, BMAP_INUSE);
return (i);
}
return (-1);
}
static void
bmd_free(int bmd)
{
bitmap_t *bmp;
bmp = bmd2bmp(bmd);
if (bmp)
BMAP_UNSET_FLAGS(bmp, BMAP_INUSE);
}
static inline int
bmp_set(bmap_chunk_t *cp, u_quad_t bn, uint_t *vp)
{
int rv;
uint_t mask;
uint_t *ip;
uint_t v;
bn -= cp->c_off;
if (bn < cp->c_clen) {
mask = 1 <<(bn & BMAP_BPW_MASK);
ip = &cp->c_bmp[bn >> BMAP_BPW_SHIFT];
v = (*vp <<(bn & BMAP_BPW_MASK)) & mask;
*ip = (*ip & ~mask) | v;
rv = 0;
} else
rv = -ERANGE;
return (rv);
}
static inline int
bmp_get(bmap_chunk_t *cp, u_quad_t bn)
{
int rv;
uint_t bit;
bn -= cp->c_off;
if (bn < cp->c_clen) {
bit = 1 <<(bn & BMAP_BPW_MASK);
rv = (cp->c_bmp[bn >> BMAP_BPW_SHIFT] & bit) != 0;
} else
rv = -ERANGE;
return (rv);
}
static bmap_chunk_t *
bm_chunk_setup(bitmap_t *bmp, bmap_chunk_t *cp, u_quad_t bn)
{
int h;
u_quad_t off, l;
uint_t cl, ml;
bmap_list_t *hp;
off = BMAP_CHUNK_OFF(bn);
l = bmp->bm_len - off;
if (l >= BMAP_CHUNK_BITS) {
cl = BMAP_CHUNK_BITS;
ml = BMAP_CHUNK_BYTES;
} else {
cl = l;
ml = MEM_LEN(l);
}
if (BMAP_IS_INIT_ONES(bmp))
(void) memset(cp->c_bmp, 0xff, ml);
else
(void) memset(cp->c_bmp, 0x00, ml);
h = HASH(bn);
hp = &bmp->bm_hash[h];
TAILQ_INSERT_HEAD(hp, cp, c_hash);
cp->c_off = off;
cp->c_clen = cl;
cp->c_mlen = ml;
return (cp);
}
static bmap_chunk_t *
bm_chunk_new(bitmap_t *bmp, u_quad_t bn)
{
bmap_chunk_t *cp;
bitmap_stats.bs_chunk_new++;
cp = ndmp_malloc(sizeof (bmap_chunk_t));
if (cp) {
cp->c_bmp = ndmp_malloc(sizeof (uint_t) * BMAP_CHUNK_WORDS);
if (!cp->c_bmp) {
free(cp);
cp = NULL;
} else {
(void) bm_chunk_setup(bmp, cp, bn);
bmp->bm_ccur++;
}
}
return (cp);
}
static bmap_chunk_t *
bm_chunk_alloc(bitmap_t *bmp, u_quad_t bn)
{
bmap_chunk_t *cp;
if (bmp->bm_ccur < bmp->bm_cmax)
cp = bm_chunk_new(bmp, bn);
else
cp = NULL;
return (cp);
}
void
hash_free(bmap_list_t *hp)
{
bmap_chunk_t *cp;
if (!hp)
return;
while (!TAILQ_EMPTY(hp)) {
cp = TAILQ_FIRST(hp);
TAILQ_REMOVE(hp, cp, c_hash);
free(cp->c_bmp);
free(cp);
}
}
static void
bm_chunks_free(bmap_list_t *hp)
{
int i;
for (i = 0; i < BMAP_HASH_SIZE; hp++, i++)
hash_free(hp);
}
static void
bm_chunk_reposition(bitmap_t *bmp, bmap_list_t *hp, bmap_chunk_t *cp)
{
if (!bmp || !hp || !cp)
return;
if (TAILQ_FIRST(hp) != cp) {
TAILQ_REMOVE(hp, cp, c_hash);
TAILQ_INSERT_HEAD(hp, cp, c_hash);
}
}
static bmap_chunk_t *
bm_chunk_find(bitmap_t *bmp, u_quad_t bn)
{
int h;
bmap_chunk_t *cp;
bmap_list_t *hp;
if (!bmp)
return (NULL);
h = HASH(bn);
hp = &bmp->bm_hash[h];
TAILQ_FOREACH(cp, hp, c_hash) {
if (bn >= cp->c_off && bn < (cp->c_off + cp->c_clen)) {
bitmap_stats.bs_cache_hit++;
bm_chunk_reposition(bmp, hp, cp);
return (cp);
}
}
bitmap_stats.bs_cache_miss++;
return (bm_chunk_alloc(bmp, bn));
}
static int
bmp_setval(bitmap_t *bmp, bm_iovec_t *vp)
{
int rv;
u_quad_t cl;
u_quad_t bn;
u_quad_t max;
bmap_chunk_t *cp;
bn = vp->bmv_base;
max = bn + vp->bmv_len;
if (bn >= bmp->bm_len || max > bmp->bm_len)
return (-EINVAL);
if (*vp->bmv_val) {
bitmap_stats.bs_set++;
bitmap_stats.bs_set_bits += vp->bmv_len;
} else {
bitmap_stats.bs_unset++;
bitmap_stats.bs_unset_bits += vp->bmv_len;
}
do {
cp = bm_chunk_find(bmp, bn);
if (!cp)
return (-ERANGE);
for (cl = cp->c_off + cp->c_clen; bn < cl && bn < max; bn++) {
rv = bmp_set(cp, bn, vp->bmv_val);
if (rv != 0)
return (rv);
}
} while (bn < max);
return (0);
}
static int
bmp_getval(bitmap_t *bmp, bm_iovec_t *vp)
{
uint_t cnt;
uint_t *ip;
int rv;
u_quad_t cl;
u_quad_t bn;
u_quad_t max;
bmap_chunk_t *cp;
bn = vp->bmv_base;
max = bn + vp->bmv_len;
if (bn >= bmp->bm_len || max > bmp->bm_len)
return (-EINVAL);
bitmap_stats.bs_get++;
bitmap_stats.bs_get_bits += 1;
cnt = 0;
ip = vp->bmv_val;
*ip = 0;
do {
cp = bm_chunk_find(bmp, bn);
if (!cp)
return (-ERANGE);
for (cl = cp->c_off + cp->c_clen; bn < cl && bn < max; bn++) {
rv = bmp_get(cp, bn);
if (rv < 0)
return (rv);
*ip |= rv << cnt;
if (++cnt >= BMAP_BPW) {
*++ip = 0;
cnt = 0;
}
}
} while (bn < max);
return (0);
}
static void
hash_init(bmap_list_t *hp)
{
int i;
for (i = 0; i < BMAP_HASH_SIZE; hp++, i++) {
TAILQ_INIT(hp);
}
}
int
bm_alloc(u_quad_t len, int set)
{
int bmd;
bitmap_t *bmp;
if (len == 0)
return (-1);
bmd = bmd_alloc();
if (bmd < 0)
return (bmd);
bmp = bmd2bmp(bmd);
bitmap_stats.bs_alloc_cnt++;
bitmap_stats.bs_alloc_size += len;
if (set)
BMAP_SET_FLAGS(bmp, BMAP_BINIT_ONES);
else
BMAP_UNSET_FLAGS(bmp, BMAP_BINIT_ONES);
bmp->bm_len = len;
bmp->bm_ccur = 0;
bmp->bm_cmax = BMAP_CHUNK_MAX;
hash_init(bmp->bm_hash);
return (bmd);
}
int
bm_free(int bmd)
{
int rv;
bitmap_t *bmp;
bmp = bmd2bmp(bmd);
if (bmp && BMAP_IS_INUSE(bmp)) {
bitmap_stats.bs_free_cnt++;
bm_chunks_free(bmp->bm_hash);
bmd_free(bmd);
rv = 0;
} else
rv = -1;
return (rv);
}
int
bm_getiov(int bmd, bm_io_t *iop)
{
int i;
int rv;
bm_iovec_t *vp;
bitmap_t *bmp;
if (!iop)
rv = -EINVAL;
else if (!(bmp = bmd2bmp(bmd)))
rv = -EINVAL;
else if (iop->bmio_iovcnt <= 0)
rv = -EINVAL;
else {
rv = 0;
vp = iop->bmio_iov;
for (i = 0; i < iop->bmio_iovcnt; vp++, i++) {
if (!vp)
return (-EINVAL);
rv |= bmp_getval(bmp, vp);
}
}
return (rv);
}
int
bm_setiov(int bmd, bm_io_t *iop)
{
int i;
int rv;
bm_iovec_t *vp;
bitmap_t *bmp;
if (!iop)
rv = -EINVAL;
else if (!(bmp = bmd2bmp(bmd)))
rv = -EINVAL;
else if (iop->bmio_iovcnt <= 0)
rv = -EINVAL;
else if (!iop->bmio_iov)
rv = -EINVAL;
else {
rv = 0;
vp = iop->bmio_iov;
for (i = 0; i < iop->bmio_iovcnt; vp++, i++)
rv |= bmp_setval(bmp, vp);
}
return (rv);
}
static dbitmap_t *
bmd2dbmp(int bmd)
{
if (bmd < 0 || bmd >= BMAP_MAX)
return (NULL);
return (&dbitmap[bmd]);
}
static int
dbmp2bmd(dbitmap_t *bmp)
{
int bmd;
bmd = bmp - dbitmap;
if (bmd < 0 || bmd >= BMAP_MAX)
bmd = -1;
return (bmd);
}
static int
dbmd_alloc(void)
{
int i;
dbitmap_t *bmp;
bmp = dbitmap;
for (i = 0; i < BMAP_MAX; bmp++, i++)
if (!BMAP_IS_INUSE(bmp)) {
BMAP_SET_FLAGS(bmp, BMAP_INUSE);
return (i);
}
return (-1);
}
static void
dbmd_free(int bmd)
{
dbitmap_t *bmp;
bmp = bmd2dbmp(bmd);
if (bmp)
BMAP_UNSET_FLAGS(bmp, BMAP_INUSE);
}
static inline int
dbmp_set(dbmap_chunk_t *cp, u_quad_t bn, uint_t *vp)
{
int rv;
uint_t mask;
uint_t *ip;
uint_t v;
bn -= cp->c_off;
if (bn < cp->c_clen) {
mask = 1 <<(bn & BMAP_BPW_MASK);
ip = &cp->c_bmp[bn >> BMAP_BPW_SHIFT];
v = (*vp <<(bn & BMAP_BPW_MASK)) & mask;
*ip = (*ip & ~mask) | v;
BMAP_CSET_DIRTY(cp);
rv = 0;
} else
rv = -ERANGE;
return (rv);
}
static u_quad_t
dbmp_getlen(dbitmap_t *bmp)
{
return (bmp ? bmp->bm_len : 0LL);
}
static inline int
dbmp_get(dbmap_chunk_t *cp, u_quad_t bn)
{
int rv;
uint_t bit;
bn -= cp->c_off;
if (bn < cp->c_clen) {
bit = 1 <<(bn & BMAP_BPW_MASK);
rv = (cp->c_bmp[bn >> BMAP_BPW_SHIFT] & bit) != 0;
} else
rv = -ERANGE;
return (rv);
}
static int
dbm_chunk_seek(dbitmap_t *bmp, u_quad_t bn)
{
int rv;
off_t off;
if (!bmp)
rv = -1;
else {
off = BMAP_CHUNK_NO(bn) * BMAP_CHUNK_BYTES;
rv = (lseek(bmp->bm_fd, off, SEEK_SET) != off) ? -1 : 0;
}
return (rv);
}
static int
dbm_chunk_flush(dbitmap_t *bmp, dbmap_chunk_t *cp)
{
int rv;
bitmap_stats.bs_chunk_flush++;
if (!bmp || !cp)
rv = -1;
else if (dbm_chunk_seek(bmp, cp->c_off) != 0)
rv = -1;
else if (write(bmp->bm_fd, cp->c_bmp, cp->c_mlen) != cp->c_mlen)
rv = -1;
else
rv = 0;
return (rv);
}
static dbmap_chunk_t *
dbm_chunk_load(dbitmap_t *bmp, dbmap_chunk_t *cp, u_quad_t bn, int new)
{
int h;
u_quad_t off, l;
uint_t cl, ml;
dbmap_list_t *hp;
off = BMAP_CHUNK_OFF(bn);
l = bmp->bm_len - off;
if (l >= BMAP_CHUNK_BITS) {
cl = BMAP_CHUNK_BITS;
ml = BMAP_CHUNK_BYTES;
} else {
cl = l;
ml = MEM_LEN(l);
}
if (new == BMAP_NEW_CHUNK) {
if (BMAP_IS_INIT_ONES(bmp))
(void) memset(cp->c_bmp, 0xff, ml);
else
(void) memset(cp->c_bmp, 0x00, ml);
} else {
if (dbm_chunk_seek(bmp, bn) != 0)
cp = NULL;
else if (read(bmp->bm_fd, cp->c_bmp, ml) != ml)
cp = NULL;
}
if (cp) {
TAILQ_INSERT_TAIL(&bmp->bm_lru, cp, c_lru);
h = HASH(bn);
hp = &bmp->bm_hash[h];
TAILQ_INSERT_HEAD(hp, cp, c_hash);
cp->c_flags = 0;
cp->c_off = off;
cp->c_clen = cl;
cp->c_mlen = ml;
}
return (cp);
}
static dbmap_chunk_t *
dbm_chunk_new(dbitmap_t *bmp, u_quad_t bn)
{
dbmap_chunk_t *cp;
bitmap_stats.bs_chunk_new++;
cp = ndmp_malloc(sizeof (dbmap_chunk_t));
if (cp) {
cp->c_bmp = ndmp_malloc(sizeof (uint_t) * BMAP_CHUNK_WORDS);
if (!cp->c_bmp) {
free(cp);
cp = NULL;
} else if (!dbm_chunk_load(bmp, cp, bn, BMAP_NEW_CHUNK)) {
free(cp->c_bmp);
free(cp);
cp = NULL;
} else
bmp->bm_ccur++;
}
return (cp);
}
static dbmap_chunk_t *
dbm_chunk_alloc(dbitmap_t *bmp, u_quad_t bn)
{
int h;
dbmap_list_t *hp;
dbmap_chunk_t *cp;
if (bmp->bm_ccur < bmp->bm_cmax)
return (dbm_chunk_new(bmp, bn));
bitmap_stats.bs_chunk_reclaim++;
cp = TAILQ_FIRST(&bmp->bm_lru);
if (BMAP_CIS_DIRTY(cp))
(void) dbm_chunk_flush(bmp, cp);
TAILQ_REMOVE(&bmp->bm_lru, cp, c_lru);
h = HASH(cp->c_off);
hp = &bmp->bm_hash[h];
TAILQ_REMOVE(hp, cp, c_hash);
return (dbm_chunk_load(bmp, cp, bn, BMAP_OLD_CHUNK));
}
static void
dbm_chunks_free(dbitmap_t *bmp)
{
dbmap_list_t *headp;
dbmap_chunk_t *cp;
if (!bmp)
return;
headp = &bmp->bm_lru;
if (!headp)
return;
while (!TAILQ_EMPTY(headp)) {
cp = TAILQ_FIRST(headp);
TAILQ_REMOVE(headp, cp, c_lru);
free(cp->c_bmp);
free(cp);
}
}
static void
dbm_chunk_reposition(dbitmap_t *bmp, dbmap_list_t *hp, dbmap_chunk_t *cp)
{
if (bmp && hp && cp) {
TAILQ_REMOVE(&bmp->bm_lru, cp, c_lru);
TAILQ_INSERT_TAIL(&bmp->bm_lru, cp, c_lru);
if (TAILQ_FIRST(hp) != cp) {
TAILQ_REMOVE(hp, cp, c_hash);
TAILQ_INSERT_HEAD(hp, cp, c_hash);
}
}
}
static dbmap_chunk_t *
dbm_chunk_find(dbitmap_t *bmp, u_quad_t bn)
{
int h;
dbmap_chunk_t *cp;
dbmap_list_t *hp;
if (!bmp)
return (NULL);
h = HASH(bn);
hp = &bmp->bm_hash[h];
TAILQ_FOREACH(cp, hp, c_hash) {
if (bn >= cp->c_off && bn < (cp->c_off + cp->c_clen)) {
bitmap_stats.bs_cache_hit++;
dbm_chunk_reposition(bmp, hp, cp);
return (cp);
}
}
bitmap_stats.bs_cache_miss++;
return (dbm_chunk_alloc(bmp, bn));
}
static int
dbmp_setval(dbitmap_t *bmp, bm_iovec_t *vp)
{
int rv;
u_quad_t cl;
u_quad_t bn;
u_quad_t max;
dbmap_chunk_t *cp;
bn = vp->bmv_base;
max = bn + vp->bmv_len;
if (bn >= bmp->bm_len || max > bmp->bm_len)
return (-EINVAL);
if (*vp->bmv_val) {
bitmap_stats.bs_set++;
bitmap_stats.bs_set_bits += vp->bmv_len;
} else {
bitmap_stats.bs_unset++;
bitmap_stats.bs_unset_bits += vp->bmv_len;
}
do {
cp = dbm_chunk_find(bmp, bn);
if (!cp)
return (-ERANGE);
for (cl = cp->c_off + cp->c_clen; bn < cl && bn < max; bn++) {
rv = dbmp_set(cp, bn, vp->bmv_val);
if (rv != 0)
return (rv);
}
} while (bn < max);
return (0);
}
static int
dbmp_getval(dbitmap_t *bmp, bm_iovec_t *vp)
{
uint_t cnt;
uint_t *ip;
int rv;
u_quad_t cl;
u_quad_t bn;
u_quad_t max;
dbmap_chunk_t *cp;
bn = vp->bmv_base;
max = bn + vp->bmv_len;
if (bn >= bmp->bm_len || max > bmp->bm_len)
return (-EINVAL);
bitmap_stats.bs_get++;
bitmap_stats.bs_get_bits += 1;
cnt = 0;
ip = vp->bmv_val;
*ip = 0;
do {
cp = dbm_chunk_find(bmp, bn);
if (!cp)
return (-ERANGE);
for (cl = cp->c_off + cp->c_clen; bn < cl && bn < max; bn++) {
rv = dbmp_get(cp, bn);
if (rv < 0)
return (rv);
*ip |= rv << cnt;
if (++cnt >= BMAP_BPW) {
*++ip = 0;
cnt = 0;
}
}
} while (bn < max);
return (0);
}
static int
dbyte_apply_ifset(dbitmap_t *bmp, u_quad_t off, uint_t b, int(*fp)(),
void *arg)
{
int bmd;
int rv;
u_quad_t l;
rv = 0;
l = dbmp_getlen(bmp);
bmd = dbmp2bmd(bmp);
for (; b && off < l; off++) {
if (b & 1) {
bitmap_stats.bs_set_applied++;
if ((rv = (*fp)(bmd, off, arg)))
break;
}
b >>= 1;
}
return (rv);
}
static int
dbm_chunk_apply_ifset(dbitmap_t *bmp, dbmap_chunk_t *cp, int(*fp)(),
void *arg)
{
int rv;
uint_t *bp;
uint_t i, m;
u_quad_t q;
rv = 0;
bp = cp->c_bmp;
q = cp->c_off;
m = cp->c_mlen / BMAP_WSIZE;
for (i = 0; i < m; q += BMAP_BPW, bp++, i++)
if (*bp) {
rv = dbyte_apply_ifset(bmp, q, *bp, fp, arg);
if (rv != 0)
break;
}
return (rv);
}
static int
swfile_trunc(int fd)
{
int rv;
off_t off;
rv = 0;
if ((off = lseek(fd, 0, SEEK_CUR)) < 0)
rv = -1;
else if (ftruncate(fd, off) != 0)
rv = -1;
return (rv);
}
static int
swfile_init(int fd, u_quad_t len, int set)
{
u_quad_t i, n;
uint_t cl, ml;
uint_t buf[BMAP_CHUNK_WORDS];
(void) memset(buf, set ? 0xff : 0x00, BMAP_CHUNK_BYTES);
n = len / BMAP_CHUNK_BITS;
for (i = 0; i < n; i++)
if (write(fd, buf, BMAP_CHUNK_BYTES) != BMAP_CHUNK_BYTES)
return (-1);
cl = (uint_t)(len % BMAP_CHUNK_BITS);
ml = MEM_LEN(cl);
if (write(fd, buf, ml) != ml)
return (-1);
return (swfile_trunc(fd));
}
int
dbm_alloc(char *fname, u_quad_t len, int set)
{
int fd;
int bmd;
dbitmap_t *bmp;
if (!fname || !*fname || !len)
return (-1);
bmd = dbmd_alloc();
if (bmd < 0)
return (bmd);
bmp = bmd2dbmp(bmd);
if ((fd = open(fname, O_RDWR|O_CREAT, 0600)) < 0)
bmd = -1;
else if (swfile_init(fd, len, set) < 0) {
bmd = -1;
(void) close(fd);
(void) unlink(fname);
dbmd_free(bmd);
bmd = -1;
} else if (!(bmp->bm_fname = strdup(fname))) {
(void) close(fd);
(void) unlink(fname);
dbmd_free(bmd);
bmd = -1;
} else {
bitmap_stats.bs_alloc_cnt++;
bitmap_stats.bs_alloc_size += len;
bmp->bm_fd = fd;
if (set)
BMAP_SET_FLAGS(bmp, BMAP_BINIT_ONES);
else
BMAP_UNSET_FLAGS(bmp, BMAP_BINIT_ONES);
bmp->bm_len = len;
bmp->bm_ccur = 0;
bmp->bm_cmax = BMAP_CHUNK_MAX;
TAILQ_INIT(&bmp->bm_lru);
hash_init((bmap_list_t *)bmp->bm_hash);
}
return (bmd);
}
int
dbm_free(int bmd)
{
int rv;
dbitmap_t *bmp;
bmp = bmd2dbmp(bmd);
if (bmp && BMAP_IS_INUSE(bmp)) {
bitmap_stats.bs_free_cnt++;
dbm_chunks_free(bmp);
(void) close(bmp->bm_fd);
(void) unlink(bmp->bm_fname);
free(bmp->bm_fname);
dbmd_free(bmd);
rv = 0;
} else
rv = -1;
return (rv);
}
u_quad_t
dbm_getlen(int bmd)
{
dbitmap_t *bmp;
bmp = bmd2dbmp(bmd);
return (dbmp_getlen(bmp));
}
int
dbm_set(int bmd, u_quad_t start, u_quad_t len, uint_t val)
{
bm_io_t io;
bm_iovec_t iov;
iov.bmv_base = start;
iov.bmv_len = len;
iov.bmv_val = &val;
io.bmio_iovcnt = 1;
io.bmio_iov = &iov;
return (dbm_setiov(bmd, &io));
}
int
dbm_getiov(int bmd, bm_io_t *iop)
{
int i;
int rv;
bm_iovec_t *vp;
dbitmap_t *bmp;
if (!iop)
rv = -EINVAL;
else if (!(bmp = bmd2dbmp(bmd)))
rv = -EINVAL;
else if (iop->bmio_iovcnt <= 0)
rv = -EINVAL;
else {
rv = 0;
vp = iop->bmio_iov;
for (i = 0; i < iop->bmio_iovcnt; vp++, i++) {
if (!vp)
return (-EINVAL);
rv |= dbmp_getval(bmp, vp);
}
}
return (rv);
}
int
dbm_setiov(int bmd, bm_io_t *iop)
{
int i;
int rv;
bm_iovec_t *vp;
dbitmap_t *bmp;
if (!iop)
rv = -EINVAL;
else if (!(bmp = bmd2dbmp(bmd)))
rv = -EINVAL;
else if (iop->bmio_iovcnt <= 0)
rv = -EINVAL;
else if (!iop->bmio_iov)
rv = -EINVAL;
else {
rv = 0;
vp = iop->bmio_iov;
for (i = 0; i < iop->bmio_iovcnt; vp++, i++)
rv |= dbmp_setval(bmp, vp);
}
return (rv);
}
int
dbm_apply_ifset(int bmd, int(*fp)(), void *arg)
{
int rv;
u_quad_t q;
dbitmap_t *bmp;
dbmap_chunk_t *cp;
bmp = bmd2dbmp(bmd);
if (!bmp || !fp)
return (-EINVAL);
rv = 0;
for (q = 0; q < bmp->bm_len; q += BMAP_CHUNK_BITS) {
cp = dbm_chunk_find(bmp, q);
if (!cp) {
rv = -ERANGE;
break;
}
rv = dbm_chunk_apply_ifset(bmp, cp, fp, arg);
if (rv != 0)
break;
}
return (rv);
}
int
bm_set(int bmd, u_quad_t start, u_quad_t len, uint_t val)
{
bm_io_t io;
bm_iovec_t iov;
iov.bmv_base = start;
iov.bmv_len = len;
iov.bmv_val = &val;
io.bmio_iovcnt = 1;
io.bmio_iov = &iov;
return (bm_setiov(bmd, &io));
}
int
bm_get(int bmd, u_quad_t start, u_quad_t len, uint_t *buf)
{
bm_io_t io;
bm_iovec_t iov;
iov.bmv_base = start;
iov.bmv_len = len;
iov.bmv_val = buf;
io.bmio_iovcnt = 1;
io.bmio_iov = &iov;
return (bm_getiov(bmd, &io));
}
int
bm_getone(int bmd, u_quad_t bitnum)
{
uint_t i;
if (bm_get(bmd, bitnum, 1, &i) == 0)
return (i ? 1 : 0);
return (0);
}
int
dbm_get(int bmd, u_quad_t start, u_quad_t len, uint_t *buf)
{
bm_io_t io;
bm_iovec_t iov;
iov.bmv_base = start;
iov.bmv_len = len;
iov.bmv_val = buf;
io.bmio_iovcnt = 1;
io.bmio_iov = &iov;
return (dbm_getiov(bmd, &io));
}
int
dbm_getone(int bmd, u_quad_t bitnum)
{
uint_t i;
if (dbm_get(bmd, bitnum, 1, &i) == 0)
return (i ? 1 : 0);
return (0);
}