rdc_bitmap.c revision 3270659f55e0928d6edec3d26217cc29398a8149
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "../solaris/nsc_thread.h"
#ifdef DS_DDICT
#include "../contract.h"
#endif
#include "rdc_io.h"
#include "rdc_bitmap.h"
#include "rdc_clnt.h"
#include "rdc_diskq.h"
#ifndef UINT8_MAX
#define UINT8_MAX 255
#endif
#ifndef UINT_MAX
#define UINT_MAX 0xffffffff
#endif
/*
* RDC bitmap functions.
*/
/*
* RDC cluster integration notes.
*
* 1. Configuration
*
*
* 2. Operation
*
* 2.1. SunCluster ensures that only one physical host has any rdc
* controlled device imported at any one time. Hence rdc will
* only be active on a single node for any set at a time.
*
* 2.2. So operation from the kernel perspective looks just like
* operation on a single, standalone, node.
*
*/
static int rdc_wrflag; /* write flag for io */
int rdc_bitmap_delay = 0;
extern nsc_io_t *_rdc_io_hc;
/*
* rdc_ns_io
* Perform read or write on an underlying ns device
*
* fd - nsc file descriptor
* flag - nsc io direction and characteristics flag
* fba_pos - offset from beginning of device in FBAs
* io_addr - pointer to data buffer
* io_len - length of io in bytes
*/
int
{
int vlen;
int rc;
nsc_size_t maxfbas = 0;
unsigned char *toaddr;
if (!RDC_SUCCESS(rc)) {
#ifdef DEBUG
#endif
maxfbas = 256;
}
loop:
if (!RDC_SUCCESS(rc)) {
if (tmp) {
(void) nsc_free_buf(tmp);
}
return (EIO);
}
/*
* Not overwriting all of the last FBA, so read in the
* old contents now before we overwrite it with the new
* data.
*/
if (!RDC_SUCCESS(rc)) {
(void) nsc_free_buf(tmp);
return (EIO);
}
}
while (tocopy > 0) {
#ifdef DEBUG
#endif
break;
}
else
if (vlen <= 0) {
vecp++;
}
}
if (!RDC_SUCCESS(rc)) {
(void) nsc_free_buf(tmp);
return (rc);
}
}
(void) nsc_free_buf(tmp);
if (fba_req > 0)
goto loop;
return (0);
}
/*
* Must be called with krdc->bmapmutex held.
*/
static void
{
#ifdef DEBUG
#endif
#ifdef DEBUG_REFCNT
#endif
}
/*
* Must be called with krdc->bmapmutex held.
*/
static int
{
int sts;
union {
} u_hdrp;
return (-1);
}
return (-1);
return (-1);
}
return (-1);
}
if (krdc->bmp_kstats) {
}
sizeof (rdc_header_t));
if (krdc->bmp_kstats) {
}
if (!RDC_SUCCESS(sts)) {
}
if (!RDC_SUCCESS(sts))
return (-1);
case RDC_HDR_V4:
/*
* old header format - upgrade incore copy, disk copy will
* be changed when state is re-written.
*/
#ifdef DEBUG
#endif
/* copy down items moved by new maxq??? sizes */
#ifdef DEBUG_REFCNT
#endif
return (0);
case RDC_HDR_MAGIC:
/* current header type */
return (0);
default:
/* not a header we currently understand */
return (0);
}
}
/*
* Must be called with krdc->bmapmutex held.
*/
static int
{
int sts;
return (-1);
}
return (-1);
return (-1);
}
return (-1);
}
if (krdc->bmp_kstats) {
}
sizeof (rdc_header_t));
if (krdc->bmp_kstats) {
sizeof (rdc_header_t);
}
if (!RDC_SUCCESS(sts)) {
}
if (!RDC_SUCCESS(sts))
return (-1);
else
return (0);
}
struct bm_ref_ops rdc_ref_byte_ops;
struct bm_ref_ops rdc_ref_int_ops;
static void
{
switch (refcntsize) {
default:
/* FALLTHRU */
case sizeof (unsigned char):
break;
case sizeof (unsigned int):
break;
}
#ifdef DEBUG_REFCNT
#endif
}
{
return (sizeof (unsigned int));
return (sizeof (unsigned char));
}
int
{
int sts;
return (-1);
}
return (-1);
}
return (-1);
}
if (!RDC_SUCCESS(sts)) {
return (-1);
}
case RDC_HDR_MAGIC:
#ifdef DEBUG_REFCNT
#endif
sts = 0;
break;
default:
sts = -1;
break;
}
return (sts);
}
int
{
int sts;
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
if (krdc->bmp_kstats) {
}
if (krdc->bmp_kstats) {
sizeof (rdc_header_t);
}
if (!RDC_SUCCESS(sts)) {
}
if (!RDC_SUCCESS(sts))
return (-1);
else
return (0);
}
void
{
int sts;
return;
}
return;
}
return;
}
return;
}
if (krdc->bmp_kstats) {
}
sizeof (header));
if (krdc->bmp_kstats) {
}
if (!RDC_SUCCESS(sts)) {
goto done;
}
if (krdc->bmp_kstats) {
}
if (krdc->bmp_kstats) {
}
if (!RDC_SUCCESS(sts)) {
}
done:
}
struct bitmapdata {
};
static int
{
int sts;
return (-1);
}
return (-1);
}
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
if (krdc->bmp_kstats) {
}
if (krdc->bmp_kstats) {
}
if (!RDC_SUCCESS(sts)) {
return (-1);
}
return (0);
}
int
{
int sts;
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
if (krdc->bmp_kstats) {
}
if (krdc->bmp_kstats) {
}
if (!RDC_SUCCESS(sts)) {
return (-1);
}
return (0);
}
int
{
int sts;
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
if (krdc->bmp_kstats) {
}
if (krdc->bmp_kstats) {
}
if (!RDC_SUCCESS(sts)) {
return (-1);
}
return (0);
}
static int
{
char *buffer;
nsc_buf_t *h;
nsc_vec_t *v;
int rc;
size_t i;
int off;
nsc_size_t maxfbas = 0;
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
for (i = 0; i < buffer_size; i++) {
}
if (!RDC_SUCCESS(rc)) {
#ifdef DEBUG
"!rdc_write_bitmap_pattern: maxfbas failed (%d)", rc);
#endif
maxfbas = 256;
}
loop:
h = NULL;
if (!RDC_SUCCESS(rc)) {
if (h) {
(void) nsc_free_handle(h);
}
rc = -1;
goto finish;
}
/* bytes to copy this time */
v = h->sb_vec;
off = 0;
while (len) {
off = 0;
v++;
}
#ifdef DEBUG
"!rdc_write_bitmap_pattern: ran off end of handle");
#endif
break;
}
}
if (!RDC_SUCCESS(rc)) {
(void) nsc_free_buf(h);
rc = -1;
goto finish;
}
(void) nsc_free_buf(h);
if (fba_req > 0)
goto loop;
rc = 0;
return (rc);
}
/*
* rdc_write_bitmap_fill()
*
* Write a bitmap full of 1's out to disk without touching the
* in-memory bitmap.
*/
int
{
}
void
{
return;
if (dst->bitmap_write > 0)
(void) rdc_write_bitmap(dst);
}
/*
* bitmap size in bytes, vol_size fba's
*/
{
return ((size_t)0);
return (sizeof (unsigned char));
return (sizeof (unsigned int));
}
int
{
int sts;
return (-1);
}
return (-1);
}
/* new volume must support at least the old refcntsize */
#ifdef DEBUG_REFCNT
#endif
}
#ifdef DEBUG
#endif
return (-1);
}
/* Can't open new bitmap */
"!rdc_move_bitmap: Cannot open new bitmap %s",
goto fail;
}
}
if (!RDC_SUCCESS(sts)) {
goto fail;
}
if (!RDC_SUCCESS(sts)) {
"!rdc_move_bitmap: nsc_partsize failed for %s", newbitmap);
goto fail;
}
"!rdc_move_bitmap: bitmap %s too small: %" NSC_SZFMT
goto fail;
}
if (!RDC_SUCCESS(sts)) {
"!rdc_move_bitmap: Reserve failed for %s",
goto fail;
}
}
#ifdef DEBUG_REFCNT
#endif
/* Forget newfd now it is krdc->bitmapfd */
/* Put new bitmap name into header and user-visible data structure */
} else {
}
"!rdc_move_bitmap: Write header %s failed", newbitmap);
goto fail;
}
if (rdc_write_bitmap(krdc) < 0) {
"!rdc_move_bitmap: Write bitmap %s failed", newbitmap);
goto fail;
}
/* Unintercept the old bitmap */
int rc;
if (rc)
"unregister bitmap failed %d", rc);
else
}
/* clear the old bitmap header */
if (sts == 0) {
if (krdc->bmp_kstats) {
}
if (krdc->bmp_kstats) {
sizeof (header);
}
}
#ifdef DEBUG
if (sts != 0) {
"!rdc_move_bitmap: unable to clear bitmap header on %s",
}
#endif
/* nsc_close will undo any reservation */
#ifdef DEBUG
#else
;
/*EMPTY*/
#endif
}
return (0);
fail:
/* Close newfd if it was unused */
}
return (-1);
}
void
{
return;
}
#ifdef DEBUG
#else
;
/*EMPTY*/
#endif
}
}
}
void
{
return;
}
if (cmd != RDC_CMD_SUSPEND) {
} else {
/* gotta drop mutex, in case q needs to fail */
"!rdc_free_bitmap: diskq suspend failed");
}
"!rdc_free_bitmap: Read header failed");
} else {
}
}
if (cmd == RDC_CMD_SUSPEND) {
(void) rdc_write_bitmap(krdc);
}
}
}
krdc->bitmap_size = 0;
}
static int
{
char *bitmapname;
return (-1);
}
else
if (krdc->dcio_bitmap) {
#ifdef DEBUG
"!rdc_alloc_bitmap: bitmap %s already allocated",
#endif
return (0);
}
if (urdc->volume_size == 0)
return (-1);
/* Round up */
KM_SLEEP);
return (-1);
}
/*
* use largest ref count type size as we haven't opened the bitmap
* volume yet to find out what has acutally be used.
*/
KM_SLEEP);
"!rdc_alloc_bitmap: ref alloc %" NSC_SZFMT
" failed for %s",
return (-1);
}
}
return (0);
}
static int
{
int sts;
char *bitmapname;
else
sizeof (unsigned char));
}
rdc_set_refcnt_ops(krdc, sizeof (unsigned char));
#ifdef DEBUG_REFCNT
#endif
goto fail;
}
}
if (!RDC_SUCCESS(sts)) {
goto fail;
}
if (!RDC_SUCCESS(sts)) {
"!rdc_open_bitmap: nsc_partsize failed for %s", bitmapname);
goto fail;
}
/* minimum size supports unsigned char reference counts */
NSC_SZFMT "blocks",
goto fail;
}
if (rdc_bitmap_mode == RDC_BMP_NEVER) {
} else if (rdc_bitmap_mode == RDC_BMP_ALWAYS ||
} else {
/* autodetect off */
krdc->bitmap_write = 0;
}
/* test for larger ref counts */
#ifdef DEBUG_REFCNT
#endif
sizeof (unsigned int));
rdc_set_refcnt_ops(krdc, sizeof (unsigned int));
}
#ifdef DEBUG_REFCNT
#endif
return (0);
fail:
return (-1);
}
int
{
char *bitmapname;
if (rdc_alloc_bitmap(krdc) < 0)
goto fail;
if (rdc_open_bitmap(krdc) < 0)
goto fail;
else
"!rdc_enable_bitmap: Read header %s failed", bitmapname);
goto fail;
}
if (set)
"!rdc_enable_bitmap: Write header %s failed",
goto fail;
}
if (rdc_write_bitmap(krdc) < 0) {
"!rdc_enable_bitmap: Write bitmap %s failed",
goto fail;
}
return (0);
fail:
return (-1);
}
static int
{
int rc;
#ifdef DEBUG_REFCNT
#endif
goto fail;
}
if (krdc->bitmap_size == 0) {
goto fail;
}
if (!RDC_SUCCESS(rc)) {
goto fail;
}
#ifdef DEBUG_REFCNT
#endif
return (0);
fail:
return (-1);
}
/*
* rdc_read_refcount
* read the stored refcount from disk
* queue lock is held
*/
int
{
int rc;
return (rc);
}
/*
* rdc_write_refcount
* writes krdc->bitmap_ref to the diskq
* called with qlock held
*/
int
{
int rc;
return (rc);
}
static int
{
char *bitmapname;
else
return (-1);
}
NSC_MAXPATH) != 0) {
#ifdef DEBUG
"!rdc_resume_state: Found %s Expected %s",
#endif /* DEBUG */
return (-1);
}
NSC_MAXPATH) != 0) {
#ifdef DEBUG
"!rdc_resume_state: Found %s Expected %s",
#endif /* DEBUG */
return (-1);
}
NSC_MAXPATH) != 0) {
#ifdef DEBUG
"!rdc_resume_state: Found %s Expected %s",
#endif /* DEBUG */
return (-1);
}
NSC_MAXPATH) != 0) {
#ifdef DEBUG
"!rdc_resume_state: Found %s Expected %s",
#endif /* DEBUG */
return (-1);
}
#ifdef DEBUG_REFCNT
#endif
/* Our disk was failed so set all the bits in the bitmap */
"!rdc_resume_state: Fill bitmap %s failed",
return (-1);
}
} else {
/* Header was good, so read in the bitmap */
"!rdc_resume_state: Read bitmap %s failed",
return (-1);
}
/*
* Check if another node went down with bits set, but
* without setting logging mode.
*/
}
}
/* if we are using a disk queue, read in the reference count bits */
mutex_enter(QLOCK(q));
if ((rdc_read_refcount(krdc) < 0)) {
"!rdc_resume_state: Resume bitmap %s's refcount"
"failed",
mutex_exit(QLOCK(q));
return (-1);
}
mutex_exit(QLOCK(q));
}
return (0);
}
int
{
char *bitmapname;
if (rdc_alloc_bitmap(krdc) < 0)
goto allocfail;
if (rdc_open_bitmap(krdc) < 0)
goto fail;
else
"!rdc_resume_bitmap: Read header %s failed", bitmapname);
goto fail;
}
/* Resuming from the bitmap, so do some checking */
/*CONSTCOND*/
/*CONSTCOND*/
#ifdef DEBUG
"!rdc_resume_bitmap: Converting v2 header for bitmap %s",
#endif
if (hdr_v2->volume_failed)
} else {
}
"!rdc_resume_bitmap: Write header %s failed",
goto fail;
}
/*
* just update asyncthr and magic, and then we're done
*/
"!rdc_resume_bitmap: Write header %s failed",
goto fail;
}
}
return (0);
fail:
if (krdc->bitmap_ref)
return (-1);
}
void
{
int sts;
if (krdc->bitmap_ref) {
/* see if we can upgrade the size of the ref counters */
if (!RDC_SUCCESS(sts)) {
goto nochange;
}
vol_size);
#ifdef DEBUG_REFCNT
#endif
}
}
}
}
int
{
char *bitmapname;
else
"!rdc_reset_bitmap: Read header %s failed", bitmapname);
goto fail_with_mutex;
}
"!rdc_reset_bitmap: Write header %s failed",
goto fail_with_mutex;
}
krdc->bitmap_write = 0;
if (krdc->bitmap_write == 0) {
if (rdc_write_bitmap_fill(krdc) < 0) {
"!rdc_reset_bitmap: Write bitmap %s failed",
goto fail;
}
} else if (rdc_write_bitmap(krdc) < 0) {
"!rdc_reset_bitmap: Write bitmap %s failed",
goto fail;
}
return (0);
fail:
#ifdef DEBUG
#endif
return (-1);
}
/*
* General bitmap operations
*/
/*
* rdc_set_bitmap_many()
*
* Used during reverse syncs to a 1-to-many primary to keep the 'many'
* bitmaps up to date.
*/
void
{
#ifdef DEBUG
(void *) urdc);
}
#endif
if (!IS_ENABLED(urd))
continue;
}
}
}
static int
{
struct timeval t;
int e, ret;
t.tv_sec = rdc_rpc_tmout;
t.tv_usec = 0;
return (EINVAL);
}
while (left) {
if (left >= bmap_blksize)
else
left = 0;
} else {
}
/*
* mark the last block sent.
*/
if (left == 0) {
}
} else {
}
if (e || ret) {
if (e)
ret = e;
return (ret);
}
}
return (0);
}
/*
*/
/*
* rdc_std_set_bitmask(pos, len, &bitmask)
* set a bitmask for this range. used to clear the correct
* bits after flushing
*/
static void
{
if (bitmask)
*bitmask = 0;
else
return;
st++;
}
}
/*
* rdc_std_set_bitmap(krdc, fba_pos, fba_len, &bitmask)
*
* Mark modified segments in the dual copy file bitmap
* to provide fast recovery
* Note that bitmask allows for 32 segments, which at 32k per segment equals
* 1 megabyte. If we ever allow more than this to be transferred in one
* operation, or decrease the segment size, then this code will have to be
* changed accordingly.
*/
static int
{
int fbaset = 0;
int printerr = 10;
int tries = RDC_FUTILE_ATTEMPTS;
int queuing = RDC_QUEUING;
if (bitmask)
*bitmask = 0;
else
return (-1);
return (-1);
if (krdc->bitmap_write == 0) {
if (rdc_write_bitmap_fill(krdc) < 0)
return (-1);
}
int use_ref;
#ifdef DEBUG
"recovery bitmaps not allocated");
#endif
return (-1);
}
if (use_ref) {
0);
}
return (-1);
}
fbaset = 1;
}
} else {
/*
* Just bump reference count
* For logging or syncing we do not care what the reference
* is as it will be forced back on the state transition.
*/
if (use_ref) {
BMAP_REF_MAXVAL(krdc)) {
/*
* Rollover of reference count.
*/
if (!(rdc_get_vflags(urdc) &
RDC_VOL_FAILED)) {
/*
* Impose throttle to help dump
* queue
*/
delay(4);
if (printerr--) {
}
if ((tries-- <= 0) &&
"ref count retry limit exceeded");
}
goto again;
}
} else {
}
}
}
st++;
}
return (-1);
}
return (0);
}
static void
{
int fbaset = 0;
return;
#ifdef DEBUG
"recovery bitmaps not allocated");
#endif
return;
}
if (((bitmask == 0xffffffff) ||
if (krdc->bitmap_ref)
} else if (use_ref) {
}
if (fbaset &&
krdc->bitmap_write > 0) {
if (rdc_write_bitmap_fba(krdc,
fba) < 0)
return;
}
fbaset = 1;
}
}
}
st++;
}
return;
}
}
/*
* make sure that this bit is set. if it isn't, set it
* used when transitioning from async to sync while going
* from rep to log. an overlapping sync write may unconditionally
* clear the bit that has not been replicated. when the queue
* is being dumped or this is called just to make sure pending stuff
* is in the bitmap
*/
void
{
int st;
int en;
return;
#ifdef DEBUG
"recovery bitmaps not allocated");
#endif
return;
}
if (krdc->bitmap_write > 0) {
}
}
st++;
}
}
/*
* rdc_std_count_dirty(krdc):
*
* Determine the number of segments that need to be flushed, This should
* agree with the number of segments logged, but since we don't lock when
* we increment, we force these values to agree
*/
static int
{
#ifdef DEBUG
"!rdc_std_count_dirty: no bitmap configured for %s",
#endif
return (0);
}
count = 0;
for (i = 0; i < size; i++)
count++;
return (count);
}
static int
{
}
/*
* rdc_std_fill_bitmap(krdc, write)
*
* Called to force bitmaps to a fully dirty state
*/
static int
{
int i, size;
#ifdef DEBUG
"!rdc_std_fill_bitmap: no bitmap configured for %s",
#endif
return (-1);
}
for (i = 0; i < size; i++)
if (write)
return (rdc_write_bitmap(krdc));
return (0);
}
/*
* rdc_std_zero_bitmap(krdc)
*
* Called on the secondary after a sync has completed to force bitmaps
* to a fully clean state
*/
static void
{
int i, size;
#ifdef DEBUG
"!rdc_std_zero_bitmap: no bitmap configured for %s",
#endif
return;
}
#ifdef DEBUG
#endif
for (i = 0; i < size; i++)
if (krdc->bitmap_write > 0)
(void) rdc_write_bitmap(krdc);
}
/*
* rdc_std_net_bmap()
*
* WARNING acts as both client and server
*/
static int
rdc_std_net_bmap(const struct bmap6 *b)
{
struct net_bdata6 bd;
return (_rdc_net_bmap(b, &bd));
}
/*
* rdc_std_net_bdata
*/
static int
{
return (0);
}
static struct rdc_bitmap_ops rdc_std_bitmap_ops = {
};
void
{
}
static void
{
}
static void
{
}
static unsigned int
{
}
static void
{
}
/* ARGSUSED */
static unsigned int
{
return ((unsigned int)(UINT8_MAX));
}
struct bm_ref_ops rdc_ref_byte_ops = {
sizeof (unsigned char)
};
static void
{
}
static void
{
}
static unsigned int
{
}
static void
{
}
/* ARGSUSED */
static unsigned int
{
return ((unsigned int)(UINT_MAX));
}
struct bm_ref_ops rdc_ref_int_ops = {
sizeof (unsigned int)
};