Beispiel #1
0
static void
printServerInfo(void)
{
    struct ubik_server *ts;
    int i, j = 1;
    char hoststr[16];

    ubik_print("Local CellServDB:\n");
    for (ts = ubik_servers; ts; ts = ts->next, j++) {
	ubik_print("  Server %d:\n", j);
	for (i = 0; (i < UBIK_MAX_INTERFACE_ADDR) && ts->addr[i]; i++)
	    ubik_print("  ... %s\n", afs_inet_ntoa_r(ts->addr[i], hoststr));
    }
}
Beispiel #2
0
/*!
 * \brief Release the transaction lock.
 */
void
ulock_relLock(struct ubik_trans *atrans)
{
    if (atrans->locktype == LOCKWRITE && (atrans->flags & TRREADWRITE)) {
	ubik_print("Ubik: Internal Error: unlocking write lock with "
	           "TRREADWRITE?\n");
	abort();
    }

    if (atrans->flags & TRREADWRITE) {
	/* noop, TRREADWRITE means we don't actually lock anything */
    } else if (atrans->locktype == LOCKREAD) {
	ReleaseReadLock(&rwlock);
    } else if (atrans->locktype == LOCKWRITE) {
	ReleaseWriteLock(&rwlock);
    }

/*
 *ubik_print("Ubik: DEBUG: Thread 0x%x %s unlock\n", lwp_cpptr,
 *	     ((atrans->locktype == LOCKREAD) ? "READ" : "WRITE"));
 */

    atrans->locktype = 0;
    return;
}
Beispiel #3
0
/*!
 * \brief Create a new slot for a particular dbase page.
 */
static struct buffer *
newslot(struct ubik_dbase *adbase, afs_int32 afid, afs_int32 apage)
{
    /* Find a usable buffer slot */
    afs_int32 i;
    struct buffer *pp, *tp;

    pp = 0;			/* last pure */
    for (i = 0, tp = LruBuffer; i < nbuffers; i++, tp = tp->lru_next) {
	if (!tp->lockers && !tp->dirty) {
	    pp = tp;
	    break;
	}
    }

    if (pp == 0) {
	/* There are no unlocked buffers that don't need to be written to the disk. */
	ubik_print
	    ("Ubik: Internal Error: Unable to find free buffer in ubik cache\n");
	return NULL;
    }

    /* Now fill in the header. */
    pp->dbase = adbase;
    pp->file = afid;
    pp->page = apage;

    FixupBucket(pp);		/* move to the right hash bucket */
    Dmru(pp);
    return pp;
}
Beispiel #4
0
/*!
 * \brief Get a pointer to a particular buffer.
 */
static char *
DRead(struct ubik_trans *atrans, afs_int32 fid, int page)
{
    /* Read a page from the disk. */
    struct buffer *tb, *lastbuffer, *found_tb = NULL;
    afs_int32 code;
    struct ubik_dbase *dbase = atrans->dbase;

    calls++;
    lastbuffer = LruBuffer->lru_prev;

    /* Skip for write transactions for a clean page - this may not be the right page to use */
    if (MatchBuffer(lastbuffer, page, fid, atrans)
		&& (atrans->type == UBIK_READTRANS || lastbuffer->dirty)) {
	tb = lastbuffer;
	tb->lockers++;
	lastb++;
	return tb->data;
    }
    for (tb = phTable[pHash(page)]; tb; tb = tb->hashNext) {
	if (MatchBuffer(tb, page, fid, atrans)) {
	    if (tb->dirty || atrans->type == UBIK_READTRANS) {
		found_tb = tb;
		break;
	    }
	    /* Remember this clean page - we might use it */
	    found_tb = tb;
	}
    }
    /* For a write transaction, use a matching clean page if no dirty one was found */
    if (found_tb) {
	Dmru(found_tb);
	found_tb->lockers++;
	return found_tb->data;
    }

    /* can't find it */
    tb = newslot(dbase, fid, page);
    if (!tb)
	return 0;
    memset(tb->data, 0, UBIK_PAGESIZE);

    tb->lockers++;
    code =
	(*dbase->read) (dbase, fid, tb->data, page * UBIK_PAGESIZE,
			UBIK_PAGESIZE);
    if (code < 0) {
	tb->file = BADFID;
	Dlru(tb);
	tb->lockers--;
	ubik_print("Ubik: Error reading database file: errno=%d\n", errno);
	return 0;
    }
    ios++;

    /* Note that findslot sets the page field in the buffer equal to
     * what it is searching for.
     */
    return tb->data;
}
Beispiel #5
0
/*!
 * \brief Set a transaction lock.
 * \param atype is #LOCKREAD or #LOCKWRITE.
 * \param await is TRUE if you want to wait for the lock instead of returning
 * #EWOULDBLOCK.
 *
 * \note The #DBHOLD lock must be held.
 */
extern int
ulock_getLock(struct ubik_trans *atrans, int atype, int await)
{
    struct ubik_dbase *dbase = atrans->dbase;

    if ((atype != LOCKREAD) && (atype != LOCKWRITE))
	return EINVAL;

    if (atrans->flags & TRDONE)
	return UDONE;

    if (atype != LOCKREAD && (atrans->flags & TRREADWRITE)) {
	return EINVAL;
    }

    if (atrans->locktype != 0) {
	ubik_print("Ubik: Internal Error: attempted to take lock twice\n");
	abort();
    }

/*
 *ubik_print("Ubik: DEBUG: Thread 0x%x request %s lock\n", lwp_cpptr,
 *	     ((atype == LOCKREAD) ? "READ" : "WRITE"));
 */

    /* Check if the lock would would block */
    if (!await && !(atrans->flags & TRREADWRITE)) {
	if (atype == LOCKREAD) {
	    if (WouldReadBlock(&rwlock))
		return EAGAIN;
	} else {
	    if (WouldWriteBlock(&rwlock))
		return EAGAIN;
	}
    }

    /* Create new lock record and add to spec'd transaction:
     * locktype. This field also tells us if the thread is
     * waiting for a lock: It will be equal to LOCKWAIT.
     */
    atrans->locktype = LOCKWAIT;
    DBRELE(dbase);
    if (atrans->flags & TRREADWRITE) {
	/* noop; don't actually lock anything for TRREADWRITE */
    } else if (atype == LOCKREAD) {
	ObtainReadLock(&rwlock);
    } else {
	ObtainWriteLock(&rwlock);
    }
    DBHOLD(dbase);
    atrans->locktype = atype;

/*
 *ubik_print("Ubik: DEBUG: Thread 0x%x took %s lock\n", lwp_cpptr,
 *	     ((atype == LOCKREAD) ? "READ" : "WRITE"));
 */
    return 0;
}
Beispiel #6
0
/*!
 * \brief send a Probe to all the network address of this server 
 * 
 * \return 0 if success, else return 1
 */
int
DoProbe(struct ubik_server *server)
{
    struct rx_connection *conns[UBIK_MAX_INTERFACE_ADDR];
    struct rx_connection *connSuccess = 0;
    int i, j;
    afs_uint32 addr;
    char buffer[32];
    char hoststr[16];
    extern afs_int32 ubikSecIndex;
    extern struct rx_securityClass *ubikSecClass;

    for (i = 0; (addr = server->addr[i]) && (i < UBIK_MAX_INTERFACE_ADDR);
	 i++) {
	conns[i] =
	    rx_NewConnection(addr, ubik_callPortal, DISK_SERVICE_ID,
			     ubikSecClass, ubikSecIndex);

	/* user requirement to use only the primary interface */
	if (ubikPrimaryAddrOnly) {
	    i = 1;
	    break;
	}
    }
    assert(i);			/* at least one interface address for this server */

    multi_Rx(conns, i) {
	multi_DISK_Probe();
	if (!multi_error) {	/* first success */
	    addr = server->addr[multi_i];	/* successful interface addr */

	    if (server->disk_rxcid)	/* destroy existing conn */
		rx_DestroyConnection(server->disk_rxcid);
	    if (server->vote_rxcid)
		rx_DestroyConnection(server->vote_rxcid);

	    /* make new connections */
	    server->disk_rxcid = conns[multi_i];
	    server->vote_rxcid = rx_NewConnection(addr, ubik_callPortal, VOTE_SERVICE_ID, ubikSecClass, ubikSecIndex);	/* for vote reqs */

	    connSuccess = conns[multi_i];
	    strcpy(buffer, afs_inet_ntoa_r(server->addr[0], hoststr));
	    ubik_print
		("ubik:server %s is back up: will be contacted through %s\n",
		 buffer, afs_inet_ntoa_r(addr, hoststr));

	    multi_Abort;
	}
    } multi_End_Ignore;
Beispiel #7
0
/*!
 * \brief Get a pointer to a particular buffer.
 */
static char *
DRead(struct ubik_trans *atrans, afs_int32 fid, int page)
{
    /* Read a page from the disk. */
    struct buffer *tb, *lastbuffer;
    afs_int32 code;
    struct ubik_dbase *dbase = atrans->dbase;

    calls++;
    lastbuffer = LruBuffer->lru_prev;

    if (MatchBuffer(lastbuffer, page, fid, atrans)) {
	tb = lastbuffer;
	tb->lockers++;
	lastb++;
	return tb->data;
    }
    for (tb = phTable[pHash(page)]; tb; tb = tb->hashNext) {
	if (MatchBuffer(tb, page, fid, atrans)) {
	    Dmru(tb);
	    tb->lockers++;
	    return tb->data;
	}
    }
    /* can't find it */
    tb = newslot(dbase, fid, page);
    if (!tb)
	return 0;
    memset(tb->data, 0, UBIK_PAGESIZE);

    tb->lockers++;
    code =
	(*dbase->read) (dbase, fid, tb->data, page * UBIK_PAGESIZE,
			UBIK_PAGESIZE);
    if (code < 0) {
	tb->file = BADFID;
	Dlru(tb);
	tb->lockers--;
	ubik_print("Ubik: Error reading database file: errno=%d\n", errno);
	return 0;
    }
    ios++;

    /* Note that findslot sets the page field in the buffer equal to
     * what it is searching for.
     */
    return tb->data;
}
Beispiel #8
0
/*!
 * \brief Main interaction loop for the recovery manager
 *
 * The recovery light-weight process only runs when you're the
 * synchronization site.  It performs the following tasks, if and only
 * if the prerequisite tasks have been performed successfully (it
 * keeps track of which ones have been performed in its bit map,
 * \p urecovery_state).
 *
 * First, it is responsible for probing that all servers are up.  This
 * is the only operation that must be performed even if this is not
 * yet the sync site, since otherwise this site may not notice that
 * enough other machines are running to even elect this guy to be the
 * sync site.
 *
 * After that, the recovery process does nothing until the beacon and
 * voting modules manage to get this site elected sync site.
 *
 * After becoming sync site, recovery first attempts to find the best
 * database available in the network (it must do this in order to
 * ensure finding the latest committed data).  After finding the right
 * database, it must fetch this dbase to the sync site.
 *
 * After fetching the dbase, it relabels it with a new version number,
 * to ensure that everyone recognizes this dbase as the most recent
 * dbase.
 *
 * One the dbase has been relabelled, this machine can start handling
 * requests.  However, the recovery module still has one more task:
 * propagating the dbase out to everyone who is up in the network.
 */
void *
urecovery_Interact(void *dummy)
{
    afs_int32 code, tcode;
    struct ubik_server *bestServer = NULL;
    struct ubik_server *ts;
    int dbok, doingRPC, now;
    afs_int32 lastProbeTime;
    /* if we're the sync site, the best db version we've found yet */
    static struct ubik_version bestDBVersion;
    struct ubik_version tversion;
    struct timeval tv;
    int length, tlen, offset, file, nbytes;
    struct rx_call *rxcall;
    char tbuffer[1024];
    struct ubik_stat ubikstat;
    struct in_addr inAddr;
    char hoststr[16];
    char pbuffer[1028];
    int fd = -1;
    afs_int32 pass;

    afs_pthread_setname_self("recovery");

    /* otherwise, begin interaction */
    urecovery_state = 0;
    lastProbeTime = 0;
    while (1) {
	/* Run through this loop every 4 seconds */
	tv.tv_sec = 4;
	tv.tv_usec = 0;
#ifdef AFS_PTHREAD_ENV
	select(0, 0, 0, 0, &tv);
#else
	IOMGR_Select(0, 0, 0, 0, &tv);
#endif

	ubik_dprint("recovery running in state %x\n", urecovery_state);

	/* Every 30 seconds, check all the down servers and mark them
	 * as up if they respond. When a server comes up or found to
	 * not be current, then re-find the the best database and
	 * propogate it.
	 */
	if ((now = FT_ApproxTime()) > 30 + lastProbeTime) {

	    for (ts = ubik_servers, doingRPC = 0; ts; ts = ts->next) {
		UBIK_BEACON_LOCK;
		if (!ts->up) {
		    UBIK_BEACON_UNLOCK;
		    doingRPC = 1;
		    code = DoProbe(ts);
		    if (code == 0) {
			UBIK_BEACON_LOCK;
			ts->up = 1;
			UBIK_BEACON_UNLOCK;
			DBHOLD(ubik_dbase);
			urecovery_state &= ~UBIK_RECFOUNDDB;
			DBRELE(ubik_dbase);
		    }
		} else {
		    UBIK_BEACON_UNLOCK;
		    DBHOLD(ubik_dbase);
		    if (!ts->currentDB)
			urecovery_state &= ~UBIK_RECFOUNDDB;
		    DBRELE(ubik_dbase);
		}
	    }

	    if (doingRPC)
		now = FT_ApproxTime();
	    lastProbeTime = now;
	}

	/* Mark whether we are the sync site */
	DBHOLD(ubik_dbase);
	if (!ubeacon_AmSyncSite()) {
	    urecovery_state &= ~UBIK_RECSYNCSITE;
	    DBRELE(ubik_dbase);
	    continue;		/* nothing to do */
	}
	urecovery_state |= UBIK_RECSYNCSITE;

	/* If a server has just come up or if we have not found the
	 * most current database, then go find the most current db.
	 */
	if (!(urecovery_state & UBIK_RECFOUNDDB)) {
	    DBRELE(ubik_dbase);
	    bestServer = (struct ubik_server *)0;
	    bestDBVersion.epoch = 0;
	    bestDBVersion.counter = 0;
	    for (ts = ubik_servers; ts; ts = ts->next) {
		UBIK_BEACON_LOCK;
		if (!ts->up) {
		    UBIK_BEACON_UNLOCK;
		    continue;	/* don't bother with these guys */
		}
		UBIK_BEACON_UNLOCK;
		if (ts->isClone)
		    continue;
		UBIK_ADDR_LOCK;
		code = DISK_GetVersion(ts->disk_rxcid, &ts->version);
		UBIK_ADDR_UNLOCK;
		if (code == 0) {
		    /* perhaps this is the best version */
		    if (vcmp(ts->version, bestDBVersion) > 0) {
			/* new best version */
			bestDBVersion = ts->version;
			bestServer = ts;
		    }
		}
	    }
	    /* take into consideration our version. Remember if we,
	     * the sync site, have the best version. Also note that
	     * we may need to send the best version out.
	     */
	    DBHOLD(ubik_dbase);
	    if (vcmp(ubik_dbase->version, bestDBVersion) >= 0) {
		bestDBVersion = ubik_dbase->version;
		bestServer = (struct ubik_server *)0;
		urecovery_state |= UBIK_RECHAVEDB;
	    } else {
		/* Clear the flag only when we know we have to retrieve
		 * the db. Because urecovery_AllBetter() looks at it.
		 */
		urecovery_state &= ~UBIK_RECHAVEDB;
	    }
	    urecovery_state |= UBIK_RECFOUNDDB;
	    urecovery_state &= ~UBIK_RECSENTDB;
	}
	if (!(urecovery_state & UBIK_RECFOUNDDB)) {
	    DBRELE(ubik_dbase);
	    continue;		/* not ready */
	}

	/* If we, the sync site, do not have the best db version, then
	 * go and get it from the server that does.
	 */
	if ((urecovery_state & UBIK_RECHAVEDB) || !bestServer) {
	    urecovery_state |= UBIK_RECHAVEDB;
	} else {
	    /* we don't have the best version; we should fetch it. */
	    urecovery_AbortAll(ubik_dbase);

	    /* Rx code to do the Bulk fetch */
	    file = 0;
	    offset = 0;
	    UBIK_ADDR_LOCK;
	    rxcall = rx_NewCall(bestServer->disk_rxcid);

	    ubik_print("Ubik: Synchronize database with server %s\n",
		       afs_inet_ntoa_r(bestServer->addr[0], hoststr));
	    UBIK_ADDR_UNLOCK;

	    code = StartDISK_GetFile(rxcall, file);
	    if (code) {
		ubik_dprint("StartDiskGetFile failed=%d\n", code);
		goto FetchEndCall;
	    }
	    nbytes = rx_Read(rxcall, (char *)&length, sizeof(afs_int32));
	    length = ntohl(length);
	    if (nbytes != sizeof(afs_int32)) {
		ubik_dprint("Rx-read length error=%d\n", code = BULK_ERROR);
		code = EIO;
		goto FetchEndCall;
	    }

	    /* give invalid label during file transit */
	    UBIK_VERSION_LOCK;
	    tversion.epoch = 0;
	    code = (*ubik_dbase->setlabel) (ubik_dbase, file, &tversion);
	    UBIK_VERSION_UNLOCK;
	    if (code) {
		ubik_dprint("setlabel io error=%d\n", code);
		goto FetchEndCall;
	    }
	    snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.TMP",
		     ubik_dbase->pathName, (file<0)?"SYS":"",
		     (file<0)?-file:file);
	    fd = open(pbuffer, O_CREAT | O_RDWR | O_TRUNC, 0600);
	    if (fd < 0) {
		code = errno;
		goto FetchEndCall;
	    }
	    code = lseek(fd, HDRSIZE, 0);
	    if (code != HDRSIZE) {
		close(fd);
		goto FetchEndCall;
	    }

	    pass = 0;
	    while (length > 0) {
		tlen = (length > sizeof(tbuffer) ? sizeof(tbuffer) : length);
#ifndef AFS_PTHREAD_ENV
		if (pass % 4 == 0)
		    IOMGR_Poll();
#endif
		nbytes = rx_Read(rxcall, tbuffer, tlen);
		if (nbytes != tlen) {
		    ubik_dprint("Rx-read bulk error=%d\n", code = BULK_ERROR);
		    code = EIO;
		    close(fd);
		    goto FetchEndCall;
		}
		nbytes = write(fd, tbuffer, tlen);
		pass++;
		if (nbytes != tlen) {
		    code = UIOERROR;
		    close(fd);
		    goto FetchEndCall;
		}
		offset += tlen;
		length -= tlen;
	    }
	    code = close(fd);
	    if (code)
		goto FetchEndCall;
	    code = EndDISK_GetFile(rxcall, &tversion);
	  FetchEndCall:
	    tcode = rx_EndCall(rxcall, code);
	    if (!code)
		code = tcode;
	    if (!code) {
		/* we got a new file, set up its header */
		urecovery_state |= UBIK_RECHAVEDB;
		UBIK_VERSION_LOCK;
		memcpy(&ubik_dbase->version, &tversion,
		       sizeof(struct ubik_version));
		snprintf(tbuffer, sizeof(tbuffer), "%s.DB%s%d",
			 ubik_dbase->pathName, (file<0)?"SYS":"",
			 (file<0)?-file:file);
#ifdef AFS_NT40_ENV
		snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.OLD",
			 ubik_dbase->pathName, (file<0)?"SYS":"",
			 (file<0)?-file:file);
		code = unlink(pbuffer);
		if (!code)
		    code = rename(tbuffer, pbuffer);
		snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.TMP",
			 ubik_dbase->pathName, (file<0)?"SYS":"",
			 (file<0)?-file:file);
#endif
		if (!code)
		    code = rename(pbuffer, tbuffer);
		if (!code) {
		    (*ubik_dbase->open) (ubik_dbase, file);
		    /* after data is good, sync disk with correct label */
		    code =
			(*ubik_dbase->setlabel) (ubik_dbase, 0,
						 &ubik_dbase->version);
		}
		UBIK_VERSION_UNLOCK;
#ifdef AFS_NT40_ENV
		snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.OLD",
			 ubik_dbase->pathName, (file<0)?"SYS":"",
			 (file<0)?-file:file);
		unlink(pbuffer);
#endif
	    }
	    if (code) {
		unlink(pbuffer);
		/*
		 * We will effectively invalidate the old data forever now.
		 * Unclear if we *should* but we do.
		 */
		UBIK_VERSION_LOCK;
		ubik_dbase->version.epoch = 0;
		ubik_dbase->version.counter = 0;
		UBIK_VERSION_UNLOCK;
		ubik_print("Ubik: Synchronize database failed (error = %d)\n",
			   code);
	    } else {
		ubik_print("Ubik: Synchronize database completed\n");
		urecovery_state |= UBIK_RECHAVEDB;
	    }
	    udisk_Invalidate(ubik_dbase, 0);	/* data has changed */
#ifdef AFS_PTHREAD_ENV
	    CV_BROADCAST(&ubik_dbase->version_cond);
#else
	    LWP_NoYieldSignal(&ubik_dbase->version);
#endif
	}
	if (!(urecovery_state & UBIK_RECHAVEDB)) {
	    DBRELE(ubik_dbase);
	    continue;		/* not ready */
	}

	/* If the database was newly initialized, then when we establish quorum, write
	 * a new label. This allows urecovery_AllBetter() to allow access for reads.
	 * Setting it to 2 also allows another site to come along with a newer
	 * database and overwrite this one.
	 */
	if (ubik_dbase->version.epoch == 1) {
	    urecovery_AbortAll(ubik_dbase);
	    UBIK_VERSION_LOCK;
	    version_globals.ubik_epochTime = 2;
	    ubik_dbase->version.epoch = version_globals.ubik_epochTime;
	    ubik_dbase->version.counter = 1;
	    code =
		(*ubik_dbase->setlabel) (ubik_dbase, 0, &ubik_dbase->version);
	    UBIK_VERSION_UNLOCK;
	    udisk_Invalidate(ubik_dbase, 0);	/* data may have changed */
#ifdef AFS_PTHREAD_ENV
	    CV_BROADCAST(&ubik_dbase->version_cond);
#else
	    LWP_NoYieldSignal(&ubik_dbase->version);
#endif
	}

	/* Check the other sites and send the database to them if they
	 * do not have the current db.
	 */
	if (!(urecovery_state & UBIK_RECSENTDB)) {
	    /* now propagate out new version to everyone else */
	    dbok = 1;		/* start off assuming they all worked */

	    /*
	     * Check if a write transaction is in progress. We can't send the
	     * db when a write is in progress here because the db would be
	     * obsolete as soon as it goes there. Also, ops after the begin
	     * trans would reach the recepient and wouldn't find a transaction
	     * pending there.  Frankly, I don't think it's possible to get past
	     * the write-lock above if there is a write transaction in progress,
	     * but then, it won't hurt to check, will it?
	     */
	    if (ubik_dbase->flags & DBWRITING) {
		struct timeval tv;
		int safety = 0;
		long cur_usec = 50000;
		while ((ubik_dbase->flags & DBWRITING) && (safety < 500)) {
		    DBRELE(ubik_dbase);
		    /* sleep for a little while */
		    tv.tv_sec = 0;
		    tv.tv_usec = cur_usec;
#ifdef AFS_PTHREAD_ENV
		    select(0, 0, 0, 0, &tv);
#else
		    IOMGR_Select(0, 0, 0, 0, &tv);
#endif
		    cur_usec += 10000;
		    safety++;
		    DBHOLD(ubik_dbase);
		}
	    }

	    for (ts = ubik_servers; ts; ts = ts->next) {
		UBIK_ADDR_LOCK;
		inAddr.s_addr = ts->addr[0];
		UBIK_ADDR_UNLOCK;
		UBIK_BEACON_LOCK;
		if (!ts->up) {
		    UBIK_BEACON_UNLOCK;
		    ubik_dprint("recovery cannot send version to %s\n",
				afs_inet_ntoa_r(inAddr.s_addr, hoststr));
		    dbok = 0;
		    continue;
		}
		UBIK_BEACON_UNLOCK;
		ubik_dprint("recovery sending version to %s\n",
			    afs_inet_ntoa_r(inAddr.s_addr, hoststr));
		if (vcmp(ts->version, ubik_dbase->version) != 0) {
		    ubik_dprint("recovery stating local database\n");

		    /* Rx code to do the Bulk Store */
		    code = (*ubik_dbase->stat) (ubik_dbase, 0, &ubikstat);
		    if (!code) {
			length = ubikstat.size;
			file = offset = 0;
			UBIK_ADDR_LOCK;
			rxcall = rx_NewCall(ts->disk_rxcid);
			UBIK_ADDR_UNLOCK;
			code =
			    StartDISK_SendFile(rxcall, file, length,
					       &ubik_dbase->version);
			if (code) {
			    ubik_dprint("StartDiskSendFile failed=%d\n",
					code);
			    goto StoreEndCall;
			}
			while (length > 0) {
			    tlen =
				(length >
				 sizeof(tbuffer) ? sizeof(tbuffer) : length);
			    nbytes =
				(*ubik_dbase->read) (ubik_dbase, file,
						     tbuffer, offset, tlen);
			    if (nbytes != tlen) {
				ubik_dprint("Local disk read error=%d\n",
					    code = UIOERROR);
				goto StoreEndCall;
			    }
			    nbytes = rx_Write(rxcall, tbuffer, tlen);
			    if (nbytes != tlen) {
				ubik_dprint("Rx-write bulk error=%d\n", code =
					    BULK_ERROR);
				goto StoreEndCall;
			    }
			    offset += tlen;
			    length -= tlen;
			}
			code = EndDISK_SendFile(rxcall);
		      StoreEndCall:
			code = rx_EndCall(rxcall, code);
		    }
		    if (code == 0) {
			/* we set a new file, process its header */
			ts->version = ubik_dbase->version;
			ts->currentDB = 1;
		    } else
			dbok = 0;
		} else {
		    /* mark file up to date */
		    ts->currentDB = 1;
		}
	    }
	    if (dbok)
		urecovery_state |= UBIK_RECSENTDB;
	}
	DBRELE(ubik_dbase);
    }
    return NULL;
}
Beispiel #9
0
/*!
 * \brief replay logs
 *
 * log format is defined here, and implicitly in disk.c
 *
 * 4 byte opcode, followed by parameters, each 4 bytes long.  All integers
 * are in logged in network standard byte order, in case we want to move logs
 * from machine-to-machine someday.
 *
 * Begin transaction: opcode \n
 * Commit transaction: opcode, version (8 bytes) \n
 * Truncate file: opcode, file number, length \n
 * Abort transaction: opcode \n
 * Write data: opcode, file, position, length, <length> data bytes \n
 *
 * A very simple routine, it just replays the log.  Note that this is a new-value only log, which
 * implies that no uncommitted data is written to the dbase: one writes data to the log, including
 * the commit record, then we allow data to be written through to the dbase.  In our particular
 * implementation, once a transaction is done, we write out the pages to the database, so that
 * our buffer package doesn't have to know about stable and uncommitted data in the memory buffers:
 * any changed data while there is an uncommitted write transaction can be zapped during an
 * abort and the remaining dbase on the disk is exactly the right dbase, without having to read
 * the log.
 */
static int
ReplayLog(struct ubik_dbase *adbase)
{
    afs_int32 opcode;
    afs_int32 code, tpos;
    int logIsGood;
    afs_int32 len, thisSize, tfile, filePos;
    afs_int32 buffer[4];
    afs_int32 syncFile = -1;
    afs_int32 data[1024];

    /* read the lock twice, once to see whether we have a transaction to deal
     * with that committed, (theoretically, we should support more than one
     * trans in the log at once, but not yet), and once replaying the
     * transactions.  */
    tpos = 0;
    logIsGood = 0;
    /* for now, assume that all ops in log pertain to one transaction; see if there's a commit */
    while (1) {
	code =
	    (*adbase->read) (adbase, LOGFILE, (char *)&opcode, tpos,
			     sizeof(afs_int32));
	if (code != sizeof(afs_int32))
	    break;
	opcode = ntohl(opcode);
	if (opcode == LOGNEW) {
	    /* handle begin trans */
	    tpos += sizeof(afs_int32);
	} else if (opcode == LOGABORT)
	    break;
	else if (opcode == LOGEND) {
	    logIsGood = 1;
	    break;
	} else if (opcode == LOGTRUNCATE) {
	    tpos += 4;
	    code =
		(*adbase->read) (adbase, LOGFILE, (char *)buffer, tpos,
				 2 * sizeof(afs_int32));
	    if (code != 2 * sizeof(afs_int32))
		break;		/* premature eof or io error */
	    tpos += 2 * sizeof(afs_int32);
	} else if (opcode == LOGDATA) {
	    tpos += 4;
	    code =
		(*adbase->read) (adbase, LOGFILE, (char *)buffer, tpos,
				 3 * sizeof(afs_int32));
	    if (code != 3 * sizeof(afs_int32))
		break;
	    /* otherwise, skip over the data bytes, too */
	    tpos += ntohl(buffer[2]) + 3 * sizeof(afs_int32);
	} else {
	    ubik_print("corrupt log opcode (%d) at position %d\n", opcode,
		       tpos);
	    break;		/* corrupt log! */
	}
    }
    if (logIsGood) {
	/* actually do the replay; log should go all the way through the commit record, since
	 * we just read it above. */
	tpos = 0;
	logIsGood = 0;
	syncFile = -1;
	while (1) {
	    code =
		(*adbase->read) (adbase, LOGFILE, (char *)&opcode, tpos,
				 sizeof(afs_int32));
	    if (code != sizeof(afs_int32))
		break;
	    opcode = ntohl(opcode);
	    if (opcode == LOGNEW) {
		/* handle begin trans */
		tpos += sizeof(afs_int32);
	    } else if (opcode == LOGABORT)
		panic("log abort\n");
	    else if (opcode == LOGEND) {
		struct ubik_version version;
		tpos += 4;
		code =
		    (*adbase->read) (adbase, LOGFILE, (char *)buffer, tpos,
				     2 * sizeof(afs_int32));
		if (code != 2 * sizeof(afs_int32))
		    return UBADLOG;
		version.epoch = ntohl(buffer[0]);
		version.counter = ntohl(buffer[1]);
		code = (*adbase->setlabel) (adbase, 0, &version);
		if (code)
		    return code;
		ubik_print("Successfully replayed log for interrupted "
		           "transaction; db version is now %ld.%ld\n",
		           (long) version.epoch, (long) version.counter);
		logIsGood = 1;
		break;		/* all done now */
	    } else if (opcode == LOGTRUNCATE) {
		tpos += 4;
		code =
		    (*adbase->read) (adbase, LOGFILE, (char *)buffer, tpos,
				     2 * sizeof(afs_int32));
		if (code != 2 * sizeof(afs_int32))
		    break;	/* premature eof or io error */
		tpos += 2 * sizeof(afs_int32);
		code =
		    (*adbase->truncate) (adbase, ntohl(buffer[0]),
					 ntohl(buffer[1]));
		if (code)
		    return code;
	    } else if (opcode == LOGDATA) {
		tpos += 4;
		code =
		    (*adbase->read) (adbase, LOGFILE, (char *)buffer, tpos,
				     3 * sizeof(afs_int32));
		if (code != 3 * sizeof(afs_int32))
		    break;
		tpos += 3 * sizeof(afs_int32);
		/* otherwise, skip over the data bytes, too */
		len = ntohl(buffer[2]);	/* total number of bytes to copy */
		filePos = ntohl(buffer[1]);
		tfile = ntohl(buffer[0]);
		/* try to minimize file syncs */
		if (syncFile != tfile) {
		    if (syncFile >= 0)
			code = (*adbase->sync) (adbase, syncFile);
		    else
			code = 0;
		    syncFile = tfile;
		    if (code)
			return code;
		}
		while (len > 0) {
		    thisSize = (len > sizeof(data) ? sizeof(data) : len);
		    /* copy sizeof(data) buffer bytes at a time */
		    code =
			(*adbase->read) (adbase, LOGFILE, (char *)data, tpos,
					 thisSize);
		    if (code != thisSize)
			return UBADLOG;
		    code =
			(*adbase->write) (adbase, tfile, (char *)data, filePos,
					  thisSize);
		    if (code != thisSize)
			return UBADLOG;
		    filePos += thisSize;
		    tpos += thisSize;
		    len -= thisSize;
		}
	    } else {
		ubik_print("corrupt log opcode (%d) at position %d\n",
			   opcode, tpos);
		break;		/* corrupt log! */
	    }
	}
	if (logIsGood) {
	    if (syncFile >= 0)
		code = (*adbase->sync) (adbase, syncFile);
	    if (code)
		return code;
	} else {
	    ubik_print("Log read error on pass 2\n");
	    return UBADLOG;
	}
    }

    /* now truncate the log, we're done with it */
    code = (*adbase->truncate) (adbase, LOGFILE, 0);
    return code;
}
Beispiel #10
0
/*!
 * \brief Update remote machines addresses in my server list
 *
 * Send back my addresses to caller of this RPC
 * \return zero on success, else 1.
 */
afs_int32
SDISK_UpdateInterfaceAddr(struct rx_call *rxcall,
			  UbikInterfaceAddr *inAddr,
			  UbikInterfaceAddr *outAddr)
{
    struct ubik_server *ts, *tmp;
    afs_uint32 remoteAddr;	/* in net byte order */
    int i, j, found = 0, probableMatch = 0;
    char hoststr[16];

    UBIK_ADDR_LOCK;
    /* copy the output parameters */
    for (i = 0; i < UBIK_MAX_INTERFACE_ADDR; i++)
	outAddr->hostAddr[i] = ntohl(ubik_host[i]);

    remoteAddr = htonl(inAddr->hostAddr[0]);
    for (ts = ubik_servers; ts; ts = ts->next)
	if (ts->addr[0] == remoteAddr) {	/* both in net byte order */
	    probableMatch = 1;
	    break;
	}

    if (probableMatch) {
	/* verify that all addresses in the incoming RPC are
	 ** not part of other server entries in my CellServDB
	 */
	for (i = 0; !found && (i < UBIK_MAX_INTERFACE_ADDR)
	     && inAddr->hostAddr[i]; i++) {
	    remoteAddr = htonl(inAddr->hostAddr[i]);
	    for (tmp = ubik_servers; (!found && tmp); tmp = tmp->next) {
		if (ts == tmp)	/* this is my server */
		    continue;
		for (j = 0; (j < UBIK_MAX_INTERFACE_ADDR) && tmp->addr[j];
		     j++)
		    if (remoteAddr == tmp->addr[j]) {
			found = 1;
			break;
		    }
	    }
	}
    }

    /* if (probableMatch) */
    /* inconsistent addresses in CellServDB */
    if (!probableMatch || found) {
	ubik_print("Inconsistent Cell Info from server: ");
	for (i = 0; i < UBIK_MAX_INTERFACE_ADDR && inAddr->hostAddr[i]; i++)
	    ubik_print("%s ", afs_inet_ntoa_r(htonl(inAddr->hostAddr[i]), hoststr));
	ubik_print("\n");
	fflush(stdout);
	fflush(stderr);
	printServerInfo();
	UBIK_ADDR_UNLOCK;
	return UBADHOST;
    }

    /* update our data structures */
    for (i = 1; i < UBIK_MAX_INTERFACE_ADDR; i++)
	ts->addr[i] = htonl(inAddr->hostAddr[i]);

    ubik_print("ubik: A Remote Server has addresses: ");
    for (i = 0; i < UBIK_MAX_INTERFACE_ADDR && ts->addr[i]; i++)
	ubik_print("%s ", afs_inet_ntoa_r(ts->addr[i], hoststr));
    ubik_print("\n");

    UBIK_ADDR_UNLOCK;
    return 0;
}
Beispiel #11
0
afs_int32
SDISK_SendFile(struct rx_call *rxcall, afs_int32 file,
	       afs_int32 length, struct ubik_version *avers)
{
    afs_int32 code;
    struct ubik_dbase *dbase = NULL;
    char tbuffer[1024];
    afs_int32 offset;
    struct ubik_version tversion;
    int tlen;
    struct rx_peer *tpeer;
    struct rx_connection *tconn;
    afs_uint32 otherHost = 0;
    char hoststr[16];
    char pbuffer[1028];
    int fd = -1;
    afs_int32 epoch = 0;
    afs_int32 pass;

    /* send the file back to the requester */

    dbase = ubik_dbase;

    if ((code = ubik_CheckAuth(rxcall))) {
	DBHOLD(dbase);
	goto failed;
    }

    /* next, we do a sanity check to see if the guy sending us the database is
     * the guy we think is the sync site.  It turns out that we might not have
     * decided yet that someone's the sync site, but they could have enough
     * votes from others to be sync site anyway, and could send us the database
     * in advance of getting our votes.  This is fine, what we're really trying
     * to check is that some authenticated bogon isn't sending a random database
     * into another configuration.  This could happen on a bad configuration
     * screwup.  Thus, we only object if we're sure we know who the sync site
     * is, and it ain't the guy talking to us.
     */
    offset = uvote_GetSyncSite();
    tconn = rx_ConnectionOf(rxcall);
    tpeer = rx_PeerOf(tconn);
    otherHost = ubikGetPrimaryInterfaceAddr(rx_HostOf(tpeer));
    if (offset && offset != otherHost) {
	/* we *know* this is the wrong guy */
	code = USYNC;
	DBHOLD(dbase);
	goto failed;
    }

    DBHOLD(dbase);

    /* abort any active trans that may scribble over the database */
    urecovery_AbortAll(dbase);

    ubik_print("Ubik: Synchronize database with server %s\n",
	       afs_inet_ntoa_r(otherHost, hoststr));

    offset = 0;
    UBIK_VERSION_LOCK;
    epoch = tversion.epoch = 0;		/* start off by labelling in-transit db as invalid */
    (*dbase->setlabel) (dbase, file, &tversion);	/* setlabel does sync */
    snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.TMP",
	     ubik_dbase->pathName, (file<0)?"SYS":"",
	     (file<0)?-file:file);
    fd = open(pbuffer, O_CREAT | O_RDWR | O_TRUNC, 0600);
    if (fd < 0) {
	code = errno;
	goto failed_locked;
    }
    code = lseek(fd, HDRSIZE, 0);
    if (code != HDRSIZE) {
	close(fd);
	goto failed_locked;
    }
    pass = 0;
    memcpy(&ubik_dbase->version, &tversion, sizeof(struct ubik_version));
    UBIK_VERSION_UNLOCK;
    while (length > 0) {
	tlen = (length > sizeof(tbuffer) ? sizeof(tbuffer) : length);
#if !defined(AFS_PTHREAD_ENV)
	if (pass % 4 == 0)
	    IOMGR_Poll();
#endif
	code = rx_Read(rxcall, tbuffer, tlen);
	if (code != tlen) {
	    ubik_dprint("Rx-read length error=%d\n", code);
	    code = BULK_ERROR;
	    close(fd);
	    goto failed;
	}
	code = write(fd, tbuffer, tlen);
	pass++;
	if (code != tlen) {
	    ubik_dprint("write failed error=%d\n", code);
	    code = UIOERROR;
	    close(fd);
	    goto failed;
	}
	offset += tlen;
	length -= tlen;
    }
    code = close(fd);
    if (code)
	goto failed;

    /* sync data first, then write label and resync (resync done by setlabel call).
     * This way, good label is only on good database. */
    snprintf(tbuffer, sizeof(tbuffer), "%s.DB%s%d",
	     ubik_dbase->pathName, (file<0)?"SYS":"", (file<0)?-file:file);
#ifdef AFS_NT40_ENV
    snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.OLD",
	     ubik_dbase->pathName, (file<0)?"SYS":"", (file<0)?-file:file);
    code = unlink(pbuffer);
    if (!code)
	code = rename(tbuffer, pbuffer);
    snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.TMP",
	     ubik_dbase->pathName, (file<0)?"SYS":"", (file<0)?-file:file);
#endif
    if (!code)
	code = rename(pbuffer, tbuffer);
    UBIK_VERSION_LOCK;
    if (!code) {
	(*ubik_dbase->open) (ubik_dbase, file);
	code = (*ubik_dbase->setlabel) (dbase, file, avers);
    }
#ifdef AFS_NT40_ENV
    snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d.OLD",
	     ubik_dbase->pathName, (file<0)?"SYS":"", (file<0)?-file:file);
    unlink(pbuffer);
#endif
    memcpy(&ubik_dbase->version, avers, sizeof(struct ubik_version));
    udisk_Invalidate(dbase, file);	/* new dbase, flush disk buffers */
#ifdef AFS_PTHREAD_ENV
    assert(pthread_cond_broadcast(&dbase->version_cond) == 0);
#else
    LWP_NoYieldSignal(&dbase->version);
#endif

failed_locked:
    UBIK_VERSION_UNLOCK;

failed:
    if (code) {
	unlink(pbuffer);
	/* Failed to sync. Allow reads again for now. */
	if (dbase != NULL) {
	    UBIK_VERSION_LOCK;
	    tversion.epoch = epoch;
	    (*dbase->setlabel) (dbase, file, &tversion);
	    UBIK_VERSION_UNLOCK;
	}
	ubik_print
	    ("Ubik: Synchronize database with server %s failed (error = %d)\n",
	     afs_inet_ntoa_r(otherHost, hoststr), code);
    } else {
	ubik_print("Ubik: Synchronize database completed\n");
    }
    DBRELE(dbase);
    return code;
}
Beispiel #12
0
/*!
 * \brief Update remote machines addresses in my server list
 *
 * Send back my addresses to caller of this RPC
 * \return zero on success, else 1.
 */
afs_int32
SDISK_UpdateInterfaceAddr(struct rx_call *rxcall,
			  UbikInterfaceAddr *inAddr,
			  UbikInterfaceAddr *outAddr)
{
    struct ubik_server *ts, *tmp;
    afs_uint32 remoteAddr;	/* in net byte order */
    int i, j, found = 0, probableMatch = 0;
    char hoststr[16];

    /* copy the output parameters */
    for (i = 0; i < UBIK_MAX_INTERFACE_ADDR; i++)
	outAddr->hostAddr[i] = ntohl(ubik_host[i]);

    remoteAddr = htonl(inAddr->hostAddr[0]);
    for (ts = ubik_servers; ts; ts = ts->next)
	if (ts->addr[0] == remoteAddr) {	/* both in net byte order */
	    probableMatch = 1;
	    break;
	}

    if (probableMatch) {
	/* verify that all addresses in the incoming RPC are
	 ** not part of other server entries in my CellServDB
	 */
	for (i = 0; !found && (i < UBIK_MAX_INTERFACE_ADDR)
	     && inAddr->hostAddr[i]; i++) {
	    remoteAddr = htonl(inAddr->hostAddr[i]);
	    for (tmp = ubik_servers; (!found && tmp); tmp = tmp->next) {
		if (ts == tmp)	/* this is my server */
		    continue;
		for (j = 0; (j < UBIK_MAX_INTERFACE_ADDR) && tmp->addr[j];
		     j++)
		    if (remoteAddr == tmp->addr[j]) {
			found = 1;
			break;
		    }
	    }
	}
    }

    /* if (probableMatch) */
    /* inconsistent addresses in CellServDB */
    if (!probableMatch || found) {
	ubik_print("Inconsistent Cell Info from server:\n");
	for (i = 0; i < UBIK_MAX_INTERFACE_ADDR && inAddr->hostAddr[i]; i++)
	    ubik_print("... %s\n", afs_inet_ntoa_r(htonl(inAddr->hostAddr[i]), hoststr));
	fflush(stdout);
	fflush(stderr);
	printServerInfo();
	return UBADHOST;
    }

    /* update our data structures */
    for (i = 1; i < UBIK_MAX_INTERFACE_ADDR; i++)
	ts->addr[i] = htonl(inAddr->hostAddr[i]);

    ubik_print("ubik: A Remote Server has addresses:\n");
    for (i = 0; i < UBIK_MAX_INTERFACE_ADDR && ts->addr[i]; i++)
	ubik_print("... %s\n", afs_inet_ntoa_r(ts->addr[i], hoststr));

    /*
     * The most likely cause of a DISK_UpdateInterfaceAddr RPC
     * is because the server was restarted.  Reset its state
     * so that no DISK_Begin RPCs will be issued until the
     * known database version is current.
     */
    ts->beaconSinceDown = 0;
    for (i=0; i<MAX_UBIK_DBASES; i++)
        ts->currentDB[i] = 0;
    urecovery_LostServer();
    return 0;
}
Beispiel #13
0
/*!
 * \brief Set a transaction lock.
 * \param atype is #LOCKREAD or #LOCKWRITE.
 * \param await is TRUE if you want to wait for the lock instead of returning
 * #EWOULDBLOCK.
 *
 * \note The #DBHOLD lock must be held.
 */
extern int
ulock_getLock(struct ubik_trans *atrans, int atype, int await)
{
    struct ubik_dbase *dbase = atrans->dbase;

    /* On first pass, initialize the lock */
    if (rwlockinit) {
	Lock_Init(&rwlock);
	rwlockinit = 0;
    }

    if ((atype != LOCKREAD) && (atype != LOCKWRITE))
	return EINVAL;

    if (atrans->flags & TRDONE)
	return UDONE;

    if (atype != LOCKREAD && (atrans->flags & TRREADWRITE)) {
	return EINVAL;
    }

    if (atrans->locktype != 0) {
	ubik_print("Ubik: Internal Error: attempted to take lock twice\n");
	abort();
    }

/*
 *ubik_print("Ubik: DEBUG: Thread 0x%x request %s lock\n", lwp_cpptr,
 *	     ((atype == LOCKREAD) ? "READ" : "WRITE"));
 */

    /* Check if the lock would would block */
    if (!await && !(atrans->flags & TRREADWRITE)) {
	if (atype == LOCKREAD) {
	    if (WouldReadBlock(&rwlock))
		return EAGAIN;
	} else {
	    if (WouldWriteBlock(&rwlock))
		return EAGAIN;
	}
    }

    /* Create new lock record and add to spec'd transaction:
     * #if defined(UBIK_PAUSE)
     * * locktype.  Before doing that, set TRSETLOCK,
     * * to tell udisk_end that another thread (us) is waiting.
     * #else
     * * locktype. This field also tells us if the thread is
     * * waiting for a lock: It will be equal to LOCKWAIT.
     * #endif
     */
#if defined(UBIK_PAUSE)
    if (atrans->flags & TRSETLOCK) {
	printf("Ubik: Internal Error: TRSETLOCK already set?\n");
	return EBUSY;
    }
    atrans->flags |= TRSETLOCK;
#else
    atrans->locktype = LOCKWAIT;
#endif /* UBIK_PAUSE */
    DBRELE(dbase);
    if (atrans->flags & TRREADWRITE) {
	/* noop; don't actually lock anything for TRREADWRITE */
    } else if (atype == LOCKREAD) {
	ObtainReadLock(&rwlock);
    } else {
	ObtainWriteLock(&rwlock);
    }
    DBHOLD(dbase);
    atrans->locktype = atype;
#if defined(UBIK_PAUSE)
    atrans->flags &= ~TRSETLOCK;
#if 0
    /* We don't do this here, because this can only happen in SDISK_Lock,
     *  and there's already code there to catch this condition.
     */
    if (atrans->flags & TRSTALE) {
	udisk_end(atrans);
	return UINTERNAL;
    }
#endif
#endif /* UBIK_PAUSE */

/*
 *ubik_print("Ubik: DEBUG: Thread 0x%x took %s lock\n", lwp_cpptr,
 *	     ((atype == LOCKREAD) ? "READ" : "WRITE"));
 */
    return 0;
}