[BACK]Return to xfs_log.c CVS log [TXT][DIR] Up to [Development] / xfs-linux

File: [Development] / xfs-linux / xfs_log.c (download)

Revision 1.6, Mon Dec 13 19:52:16 1993 UTC (23 years, 10 months ago) by miken
Branch: MAIN
Changes since 1.5: +16 -151 lines

Remove old ticket code where ticket handles were integers rather than
pointers to tickets.  Even though this exposes tickets outside the log
manager interface, it allows far fewer locks.

/*
 * High level interface routines for log manager
 */

#include <sys/types.h>

#include <sys/param.h>
#define _KERNEL
#include <sys/sysmacros.h>
#include <sys/buf.h>
#ifdef SIM
#undef _KERNEL
#include <bstring.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#endif

#include <sys/kmem.h>
#include <sys/sema.h>
#include <sys/uuid.h>
#include <sys/vnode.h>

#include "xfs_inum.h"
#include "xfs.h"
#include "xfs_types.h"
#include "xfs_sb.h"		/* depends on xfs_types.h & xfs_inum.h */
#include "xfs_trans.h"
#include "xfs_mount.h"		/* depends on xfs_trans.h & xfs_sb.h */
#include "xfs_log.h"		/* depends on xfs_mount.h */

#ifndef LOG_DEBUG
int
xfs_log_reserve(struct xfs_mount *mp, int reserve, int flags)
/* ARGSUSED */
{
        return (1);
}

#else


#include "xfs_log_priv.h"

/* Local function prototypes */
STATIC void log_alloc(xfs_mount_t *mp, dev_t log_dev);
STATIC void log_clean(log_t *log);
STATIC void log_commit_record(xfs_mount_t *mp, log_ticket_t *ticket);
STATIC void log_copy(in_core_log_t *, xfs_log_iovec_t region[], int i, int n,
		     int offset, xfs_lsn_t *lsn);
STATIC void log_sync(log_t *log, xfs_lsn_t *lsn, uint flags);
STATIC void log_unalloc(void);
STATIC int  log_write(xfs_mount_t *mp, xfs_log_iovec_t	region[], int nentries,
		      xfs_log_ticket_t	tic, int commit);


/*
 * NOTES:
 *
 *	1. currblock field gets updated at startup and after in-core logs
 *		are written out to the disk.
 *
 */

/*
 * name:	xfs_log_notify()
 * purpose: This function will take a log sequence number and check to
 *	see if that lsn has been flushed to disk.  If it has, then the
 *	callback function is called with the callback argument.
 */
void
xfs_log_notify(xfs_mount_t *mp,		/* mount of partition */
	       xfs_lsn_t   lsn,		/* lsn looking for */
	       void	   callback_func(void *),
	       void	   *callback_arg)
{
    log_t *log = mp->m_log;
    
    if (log->l_iclog->ic_state == LOG_CURRENT ||
	log->l_iclog->ic_state == LOG_SYNCING) {
    }
}	/* xfs_log_notify */


void
xfs_log_done(xfs_mount_t	*mp,
	     xfs_log_ticket_t	tic)
{
    log_t	 *log = mp->m_log;
    log_ticket_t *ticket = (xfs_log_ticket_t) tic;

    log_commit_record(mp, ticket);
    log_putticket(log, ticket);
}	/* xfs_log_done */


/*
 * name:	xfs_log_force()
 * purpose: Force the in-core log to disk.  If flags == XFS_LOG_SYNC,
 *	the force is done synchronously.
 */
int
xfs_log_force(xfs_mount_t	*mp,
	      xfs_log_ticket_t	ticket,
	      uint		flags)
{
    xfs_lsn_t	lsn;
    log_t	*log = mp->m_log;

    ASSIGN_LSN(lsn, log->l_cycle, log->l_currblock);

    if (flags & XFS_LOG_FORCE)
	log_sync(log, &lsn, flags);
    else if (flags & XFS_LOG_URGE)
	return -1;	/* not implemented yet */
    else
	return -1;	/* XFS_LOG_FORCE | XFS_LOG_URGE must be set */
	
}	/* xfs_log_force */


/*
 * name:	xfs_log_new_transaction()
 * purpose: Given an old transaction id for a <mp, slot> pair, replace
 *	it with a new transaction id.  Do not change the reservation.
 */
int
xfs_log_new_transaction(xfs_mount_t	 *mp,	/* mount point */
			xfs_log_ticket_t tic,
			xfs_tid_t	 otid,	/* old tid */
			xfs_tid_t	 ntid)	/* new tid */
{
    log_ticket_t *ticket = (log_ticket_t *)tic;

    if (ticket->t_tid != otid)
	return (-1);
    ticket->t_tid = ntid;
}


/*
 * Initialize log manager data.
 */
int
xfs_log_init()
{
}


/*
 *  1. Reserve an amount of on-disk log space and return a ticket corresponding
 *	to the reservation.
 *  2. Potentially, push buffers at tail of log to disk.
 */
int
xfs_log_reserve(xfs_mount_t	 *mp,
		xfs_tid_t	 tid,
		uint		 len,
		xfs_log_ticket_t *x_ticket,
		char		 log_client,
		uint		 flags)
{
    log_t *log = mp->m_log;

    if (log_client != XFS_TRANSACTION_MANAGER)
	return -1;

    if (flags & XFS_LOG_SLEEP)
	return XFS_ENOTSUP;
    if (flags & XFS_LOG_PERM_RESERV)
	return XFS_ENOTSUP;
    
    /* Eventually force out buffers */
    if (log->l_logreserved + len > log->l_logsize)
	return XFS_ENOLOGSPACE;
    log->l_logreserved += len;
    *x_ticket =
	(xfs_log_ticket_t) log_maketicket(mp->m_log, tid, len, log_client);

    return 0;
}	/* log_reserve */


#ifdef SIM
#include <sys/stat.h>
int
log_findlogsize(dev_t log_dev)
{
    struct stat buf;

    if (fstat(bmajor(log_dev), &buf) == -1)
	return -1;

    return buf.st_size;
}
#endif /* SIM */


/*
 * Mount a log filesystem.
 *
 * mp	   -
 * log_dev - device number of on-disk log device
 * flags   -
 *
 */
int
xfs_log_mount(xfs_mount_t *mp, dev_t log_dev, uint flags)
{
    log_t *log;

    if ((flags & XFS_LOG_RECOVER) && log_recover(mp, log_dev) != 0) {
	return XFS_ERECOVER;
    }
    log_alloc(mp, log_dev);
    return 0;
}	/* log_mount */


int
xfs_log_unmount(xfs_mount_t *mp)
{
    log_unalloc();
}

int
xfs_log_write(xfs_mount_t *	mp,
	      xfs_log_iovec_t	reg[],
	      int		nentries,
	      xfs_log_ticket_t	tic)
{
    log_write(mp, reg, nentries, tic, 0);
}	/* xfs_log_write */


/*
 * ERRORS:
 *	Return error at any time if reservation is overrun.
 */
int
log_write(xfs_mount_t *		mp,
	  xfs_log_iovec_t	reg[],
	  int			nentries,
	  xfs_log_ticket_t	tic,
	  int			commit)
{
    int len, i, error, log_offset;
    log_ticket_t	*ticket;
    log_op_header_t	logop_head;
    in_core_log_t	*ic_log;
    xfs_lsn_t		lsn;
    log_t		*log = mp->m_log;

    for (len=0, i=0; i<nentries; i++) {
	len += reg[i].i_len;
    }

    ticket = (log_ticket_t *)tic;
    if (ticket->t_reservation < len) {
	return -1;
    } else {
	ticket->t_reservation -= len;
    }

    log_clean(log);
    ic_log		   = log->l_iclog;
    logop_head.oh_tid	   = ticket->t_tid;
    logop_head.oh_clientid = ticket->t_clientid;
    logop_head.oh_len	   = 0;
    if (commit)
	logop_head.oh_flags = LOG_COMMIT;
    else
	logop_head.oh_flags = 0;
	

    /* Create log sequence number */
    ASSIGN_LSN(lsn, log->l_cycle, log->l_currblock);

    i = 0;
    while (i < nentries) {
	if ((len + sizeof(log_op_header_t)) < ICLOG_LEFT(ic_log)) {
	    if (ticket->t_reservation < sizeof(log_op_header_t)) {
		return -1;
	    } else {
		ticket->t_reservation -= sizeof(log_op_header_t);
	    }

	    log_offset = ic_log->ic_offset + sizeof(log_op_header_t);
	    ic_log->ic_offset += (len + sizeof(log_op_header_t));
	    logop_head.oh_len += len;
	    bcopy(&logop_head, ic_log->ic_data, sizeof(log_op_header_t));

	    log_copy(ic_log, reg, i, nentries, log_offset, &lsn);
	    log_sync(log, &lsn, XFS_LOG_SYNC);
	    return 0;
	} else {	/* can't fit all regions */
	    log_panic("help!");
	}
    }
}	/* log_write */


void
log_alloc(xfs_mount_t *mp, dev_t log_dev)
{
    register log_t	*log;
    log_rec_header_t	*head;

    log = mp->m_log = (void *)kmem_zalloc(sizeof(log_t), 0);
    log_alloc_tickets(log);

    log->l_dev = log_dev;
    if ((log->l_logsize = log_findlogsize(log_dev)) == -1)
	log_panic("log_findlogsize");
    log->l_logreserved = 0;
    log->l_currblock = 0;

    /* Assign 1st in-core log */
    log->l_iclog = (in_core_log_t *)kmem_zalloc(sizeof(in_core_log_t), 0);
    head = &log->l_iclog->ic_header;
    head->h_magicno = LOG_HEADER_MAGIC_NUM;
    head->h_version = 1;
    head->h_lsn = 0;
    log->l_iclog->ic_size = LOG_RECORD_SIZE-LOG_HEADER_SIZE;
    log->l_iclog->ic_state = LOG_CURRENT;
    log->l_iclog->ic_bp = getrbuf(0);

    /* Assign backup in-core log */
    log->l_iclog2 = (in_core_log_t *)kmem_zalloc(sizeof(in_core_log_t), 0);
    head = &log->l_iclog2->ic_header;
    head->h_magicno = LOG_HEADER_MAGIC_NUM;
    head->h_version = 1;
    log->l_iclog2->ic_size = LOG_RECORD_SIZE-LOG_HEADER_SIZE;
    log->l_iclog2->ic_state = LOG_NOTUSED;
    log->l_iclog2->ic_bp = getrbuf(0);
}	/* log_alloc */


void
log_unalloc(void)
{
}


void
log_clean(log_t *log)
{
    switch (log->l_iclog->ic_state) {
	case LOG_CURRENT: {
	    /* do nothing */
	    break;
	};
	case LOG_SYNCING: {
	    log_panic("log_clean: log_sync");
	    break;
	};
	case LOG_NOTUSED: {
	    log_panic("log_clean: log_notused");
	    break;
	};
	case LOG_DIRTY: {
	    bzero((void *)&log->l_iclog->ic_header, sizeof(log_rec_header_t));
	    break;
	};
	default: {
	    log_panic("log_clean: Illegal state");
	}
    };
}	/* log_clean */



void
log_commit_record(xfs_mount_t  *mp,
		  log_ticket_t *ticket)
{
    int			error;
    xfs_log_iovec_t	reg[1];
    int			nentries = 0;

    error = log_write(mp, reg, nentries, ticket, 1);
    if (error)
	log_panic("log_commit_record");
}	/* log_commit_record */


void
log_copy(in_core_log_t *iclog,
	 xfs_log_iovec_t reg[],
	 int i,
	 int n,
	 int offset,
	 xfs_lsn_t *lsn)
{
    char *ptr = &iclog->ic_data[offset];

    iclog->ic_header.h_num_logops++;
    for ( ; i < n; i++) {
	bcopy(reg[i].i_addr, ptr, reg[i].i_len);
	reg[i].i_lsn = *lsn;
	ptr += reg[i].i_len;
    }
}	/* log_copy */


/*
 * name:	log_iodone()
 * purpose: Function which is called when an io completes.  The log manager
 *	needs its own routine, in order to control what happens with the buffer
 *	after the write completes.
 */
void
log_iodone(buf_t *bp)
{
    if ((bp->b_flags & B_ASYNC) == 0)
	vsema(&bp->b_iodonesema);
}	/* log_iodone */


/*
 * name:	log_sync()
 * purpose: Flush out the in-core log to the on-disk log in a synchronous or
 *	asynchronous fashion.  The current log to write out should always be
 *	l_iclog.  The two logs are switched, so another thread can begin
 *	writing to the non-syncing in-core log.  Before an in-core log can
 *	be written out, the data section must be scanned to make sure there
 *	are no occurrences of the log header magic number at log block
 *	boundaries.
 */
void
log_sync(log_t *log, xfs_lsn_t *lsn, uint flags)
{
    in_core_log_t *tic_log = log->l_iclog;
    uint	  *dptr;	/* pointer to integer sized element */
    buf_t	  *bp;
    int		  i = 0;
    uint	  count;

    if (flags != 0 && ((flags & XFS_LOG_SYNC) != XFS_LOG_SYNC))
	log_panic("log_sync: illegal flag");

    /* swap in-core logs */
    log->l_iclog = log->l_iclog2;
    log->l_iclog2 = tic_log;
    tic_log->ic_state = LOG_SYNCING;
    if (log->l_iclog->ic_state == LOG_NOTUSED)
	log->l_iclog->ic_state = LOG_CURRENT;
    
    tic_log->ic_header.h_lsn = *lsn;
    for (dptr = (uint *)tic_log->ic_data + (LOG_BBSIZE - LOG_HEADER_SIZE);
	 dptr < (uint *)tic_log->ic_data + tic_log->ic_offset;
	 dptr += 1024) {
	if (*dptr == LOG_HEADER_MAGIC_NUM) {
	    tic_log->ic_header.h_blocks_col[i] = ((uint)dptr >> LOG_BBSHIFT);
	    *dptr = 0;
	    i++;
	}
    }
    tic_log->ic_header.h_len = tic_log->ic_offset;

    bp = tic_log->ic_bp;
    bp->b_blkno = LOGBB_TO_BB(log->l_currblock);

    /* Round byte count up to a LOG_BBSIZE chunk */
    count = bp->b_bcount = (tic_log->ic_offset + LOG_BBSIZE - 1) & ~LOG_BBMASK;
    bp->b_dmaaddr = (caddr_t) tic_log;
    if (flags & XFS_LOG_SYNC)
	bp->b_flags |= (B_BUSY | B_HOLD);
    else
	bp->b_flags |= (B_BUSY | B_ASYNC);
    bp->b_bufsize = count;
    bp->b_iodone = log_iodone;
    bp->b_edev = log->l_dev;
    psema(&bp->b_lock, PINOD);

    bwrite(bp);

    if (bp->b_flags & B_ERROR == B_ERROR) {
	log_panic("log_sync: buffer error");
    } else {
	log->l_currblock += NBLOCKS(count);	/* 4k blocks */
    }
    tic_log->ic_state = LOG_DIRTY;
}	/* log_sync */


/*****************************************************************************
 *
 *		TICKET functions
 *
 *****************************************************************************
 */
/*
 *	Algorithm doesn't take into account page size. ;-(
 */
void
log_alloc_tickets(log_t *log)
{
    caddr_t buf;
    log_ticket_t *t_list;
    uint i = LOG_TICKET_TABLE_SIZE-1;


    /*
     * XXXmiken: may want to account for differing sizes of pointers
     * or allocation one page at a time.
     */
    buf = (caddr_t) kmem_zalloc(LOG_TICKET_TABLE_SIZE *
				sizeof(log_ticket_t), 0);

    t_list = log->l_freelist = (log_ticket_t *)buf;
    do {
	t_list->t_next = t_list+1;
	t_list = t_list->t_next;
    } while (i-- > 0);
    /* t_list->t_slot = 0; => zalloc() did this! */
    t_list->t_next = 0;
}	/* log_alloc_tickets */


/*
 *
 */
void log_putticket(log_t *log, log_ticket_t *ticket)
{
    log_ticket_t *t_list;

    ticket->t_next = log->l_freelist;
    log->l_freelist = ticket;
    /* no need to clear fields */
}	/* log_putticket */


/*
 *
 */
void
log_relticket(log_ticket_t *tic)
{
}	/* log_relticket */


log_ticket_t *
log_maketicket(log_t		*log,
	       xfs_tid_t	tid,
	       int		len,
	       char		log_clientid)
{
    log_ticket_t *tic;

    if (log->l_freelist == NULL) {
	/* do something here */
    }
    tic = log->l_freelist;
    log->l_freelist = tic->t_next;
    tic->t_reservation = len;
    tic->t_tid = tid;
    tic->t_clientid = log_clientid;
    return(tic);
}	/* log_maketicket */


/******************************************************************************
 *
 *		Log recover routines
 *
 ******************************************************************************
 */
uint xfs_log_end(struct xfs_mount *, dev_t);

int
log_recover(struct xfs_mount *mp, dev_t log_dev)
{
    return 0;
#if XXXmiken
    blkno = xfs_log_end(mp, log_dev);
    xfs_log_read(blkno, log_dev);
#endif
}

#if XXXmiken
uint
log_end(struct xfs_mount *mp, dev_t log_dev)
{
    struct stat buf;
    int err, log_size, log_blks;

    if ((err = fstat(major(log_dev), &buf)) != 0)
	return ERROR;

    log_size = buf.st_size;
    log_blks = log_size / 512;

}
#endif


/******************************************************************************
 *
 *		Log print routines
 *
 ******************************************************************************
 */

#ifndef _KERNEL
void print_lsn(caddr_t string, xfs_lsn_t *lsn)
{
    printf("%s: %x,%x", string, ((uint *)lsn)[0], ((uint *)lsn)[1]);
}


#if SIM
void print_tid(caddr_t string, xfs_tid_t *tid)
{
    printf("%s: %x", string, ((uint *)tid)[0]);
}
#else
void print_tid(caddr_t string, xfs_tid_t *tid)
{
    printf("%s: %x,%x,%x,%x", string,
	   ((uint *)tid)[0], ((uint *)tid)[1],
	   ((uint *)tid)[2], ((uint *)tid)[3]);
}
#endif

uint log_print_head(log_rec_header_t *head)
{
    uint *uint_ptr;

    if (head->h_magicno != LOG_HEADER_MAGIC_NUM) {
	printf("Bad log record header or end of log\n");
	exit(1);
    }
    printf("version: %d	", head->h_version);
    print_lsn("	lsn", &head->h_lsn);
    print_lsn("	sync_lsn", &head->h_sync_lsn);
    printf("\n");
    printf("length of LR: %d	prev offset: %d		num ops: %d\n",
	   head->h_len, head->h_prev_offset, head->h_num_logops);
    uint_ptr = head->h_blocks_col;
    while (uint_ptr) {
	printf("block no: %d\n", uint_ptr);
	uint_ptr++;
    }
    return(head->h_len);
}


void log_print_record(int fd, uint len)
{
    caddr_t buf = (caddr_t) kmem_zalloc(len, 0);
    caddr_t ptr = buf;
    log_op_header_t *head;
    int i = 1, n, read_len;

    /* read_len must read up to some block boundary */
    read_len = ((len >> 12) + LOG_BBSIZE-LOG_HEADER_SIZE);
    if (read(fd, buf, read_len) == -1) {
	printf("log_print_record: read error\n");
	exit(1);
    }
    do {
	head = (log_op_header_t *)ptr;
	printf("Operation: %d\n", i);
	print_tid("tid", &head->oh_tid);
	printf("	len: %d	clientid: %s\n",
	       head->oh_len, (head->oh_clientid == XFS_TRANSACTION_MANAGER ?
			      "XFS_TRANSACTION_MANAGER" : "ERROR"));
	ptr += sizeof(log_op_header_t);
	len -= sizeof(log_op_header_t);
	for (n = 0; n < head->oh_len; n++) {
	    printf("%c", *ptr);
	    ptr++; len--;
	}
	printf("\n");
    } while (len > 0);
}


void xfs_log_print(xfs_mount_t *mp, dev_t log_dev)
{
    int fd = bmajor(log_dev);
    char buf[512];
    int done = 0;
    uint len;

    do {
	if ((len=read(fd, buf, 512)) == -1) {
	    printf("xfs_log_print end of log\n");
	    done++;
	    continue;
	}
	len = log_print_head((log_rec_header_t *)buf);
	log_print_record(fd, len);
	printf("=================================\n");
    } while (!done);
}
#endif /* !_KERNEL */






#endif /* LOG_DEBUG */