PM Application Program Interface Ver. 1.2

[English], [Japanese]

  1. Initialization

  2. Configuration

  3. Sending Messages

  4. Receiving Messages
  5. Remote Memory Access
  6. Network Context Switch

  7. Miscellaneous

  8. PM v1.0 Compatible Entries

  9. Sample Programs

  1. Initialization
    NAME
    _pmInit - initialize a Myrinet interface

    SYNOPSIS
    int _pmInit(int unit, char *mcp, char *config)

    DESCRIPTION
    _pmInit loads the LANai program mcp into the Myrinet interface unit, and initializes the routing information from the config file. It must be called firstly and only once on each host.

    RETURN VALUES
    _pmInit returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The value of unit is out of range.
    ENOENT
    The LANai program or the configuration file does not exit.
    ENODEV
    The LANai device unit does not exist.
    The Myrinet interface cannot be mmap(2)ed.
    ENXIO
    The LANai program cannot be loaded.
    The LANai program and PM library versions are different.
    EBUSY
    Some node names or routes conflict.
    EHOSTUNREACH
    No route to some nodes exists.
    EMLINK
    The hop count to some nodes exceeds 4.


    NAME
    _pmOpenContext - open a context

    SYNOPSIS
    int _pmOpenContext(int unit, int ctx, pmCtx *pmcp)

    DESCRIPTION
    _pmOpenContext opens the context ctx of the Myrinet device unit, and stores the context pointer in *pmcp.

    RETURN VALUES
    _pmOpenContext returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The value of unit or ctx is out of range.
    ENODEV
    The Myrinet interface unit does not exist.
    The Myrinet interface cannot be mmap(2)ed.
    ENXIO
    The LANai processor is not running.


    NAME
    _pmCloseContext - close a context

    SYNOPSIS
    int _pmCloseContext(pmCtx pmc)

    DESCRIPTION
    _pmCloseContext closes the context pmc.

    RETURN VALUES
    _pmCloseContext returns zero on success. On failure, it returns the following error codes:

    ERRORS
    No errors.


    NAME
    _pmInitContext - initialize a context

    SYNOPSIS
    int _pmInitContext(int pmdesc, pmCtx *pmcp)

    DESCRIPTION
    _pmInitContext initializes the channel and the context loaded into the channel specified by the descriptor pmdesc, and stores the context pointer in *pmcp.
    It is used to initialize a channel/context which is opened and passed by the parent process.

    RETURN VALUES
    _pmInitContext returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EBADF
    The value of pmdesc is incorrect.
    ENODEV
    The Myrinet interface does not exist.
    The Myrinet interface cannot be mmap(2)ed.
    ENXIO
    The LANai processor is not running.


    NAME
    _pmResetContext - reset a context to its initial state

    SYNOPSIS
    int _pmResetContext(pmCtx pmc)

    DESCRIPTION
    _pmResetContext resets the context pmc to its initial state.
    After resetting, all physical PEs are assigned as logical PEs of the context.

    NOTICE
    The context must not be allowed to send and must be in stable state.

    RETURN VALUES
    _pmResetContext returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EIO
    The context is allowed sending.
    EBUSY
    The context is not in stable state.


    NAME
    _pmLoadContext - load a context into a channel

    SYNOPSYS
    int _pmLoadContext(pmCtx pmc, int chan)

    DESCRIPTION
    _pmLoadContext loads a context pmc into a channel chan.
    When a context is used to communicate, it must be loaded into a channel.

    RETURN VALUES
    _pmLoadContext returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The value of chan is invalid.
    EBUSY
    The context is already loaded into some channel.


    NAME
    _pmUnloadContext - unload a context

    SYNOPSYS
    int _pmUnloadContext(pmCtx pmc)

    DESCRIPTION
    _pmUnloadContext unloads a loaded context pmc from the channel in which the context is loaded.

    RETURN VALUES
    _pmUnloadContext returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The context is not loaded into any channel.


  2. Configuration
    NAME
    _pmGetConfig - get a configuration information

    SYNOPSYS
    int _pmGetConfig(int unit, pmConfig *cfp)

    DESCRIPTION
    _pmGetConfig extracts the configuration information of the Myrinet interface unit such as the number of nodes, channels and contexts into the pmConfig structure pointed by *cfp.
    typedef struct pm_config {
    	int	nodes;		/* The number of nodes */
    	int	channels;	/* The number of channels */
    	int	contexts;	/* The number of contexts */
    } pmConfig;
    

    RETURN VALUES
    _pmGetConfig returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The value of unit is invalid.


    NAME
    _pmBindPE - assign physical PEs to a context

    SYNOPSIS
    int _pmBindPE(pmCtx pmc, int pe_base, int pe_num)

    DESCRIPTION
    _pmBindPE assigns pe_num physical PEs starting physical PE number pe_base to the context pmc as logical PE number 0..(pe_num-1).

    RETURN VALUES
    _pmBindPE returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The value specified by pe_base and pe_num exceeds the range of physical PEs.


    NAME
    _pmPEs - get the number of logical PEs of a context

    SYNOPSIS
    int _pmPEs(pmCtx pmc, int *npep)

    DESCRIPTION
    _pmPEs returns the number of logical PEs of the context pmc in *npep.

    RETURN VALUES
    _pmPEs returns zero on success. On failure, it returns the following error codes:

    ERRORS
    No errors.


    NAME
    _pmSelf - get the logical PE number of myself

    SYNOPSIS
    int _pmSelf(pmCtx pmc, int *selfp)

    DESCRIPTION
    _pmSelf returns the logical PE number of myself in the context pmc in *selfp.

    RETURN VALUES
    _pmSelf returns zero on success. On failure, it returns the following error codes:

    ERRORS
    No errors.


    NAME
    _pmFd - get file descriptor of a context

    SYNOPSIS
    int _pmFd(pmCtx pmc, int *fdp)

    DESCRIPTION
    _pmFd returns the file descriptor of the context pmc in *fdp.
    The process can wait until a message arrives by calling select(2) using the file descriptor.

    RETURN VALUES
    _pmFd returns zero on success. On failure, it returns the following error codes:

    ERRORS
    No errors.

    BUGS
    In current implementation, because the file descriptor is assigned to a Myrinet unit, not each context, the process cannot wait for multiple contexts using select(2).


    NAME
    _pmChanDesc - get a channel descriptor of a channel

    SYNOPSIS
    int _pmChanDesc(int unit, int chan, int *descp)

    DESCRIPTION
    _pmChanDesc returns a descriptor of the channel chan of the Myrinet interface unit in *descp.
    It is used to pass a channel to a child process.

    RETURN VALUES
    _pmChanDesc returns zero on success. On failure, it returns the following error codes:

    ERRORS
    No errors.


  3. Sending Messages
    NAME
    _pmGetSendBuf - get a send buffer

    SYNOPSIS
    int _pmGetSendBuf(pmCtx pmc, caddr_t *bufp, size_t length)

    DESCRIPTION
    _pmGetSendBuf reserves a length byte length send buffer of the context pmc, and stores its address in *bufp.

    RETURN VALUES
    _pmGetSendBuf returns zero on success. On failure, it returns the following errors.

    ERRORS
    EINVAL
    The value of length is out of range.
    ENOBUFS
    There is no available buffer.


    NAME
    _pmSend - send a message

    SYNOPSIS
    int _pmSend(pmCtx pmc, int dst_node)

    DESCRIPTION
    _pmSend sends a message to the logical PE dst_node of the context pmc.

    RETURN VALUES
    _pmSend returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The value of dst_node is out of range.
    EHOSTUNREACH
    No route to dst_node exits.


    NAME
    _pmGetMutiBuf - get a send buffer for multicast

    SYNOPSIS
    int _pmGetMultiBuf(pmCtx pmc, caddr_t *bufp, size_t length, int num_nodes)

    DESCRIPTION
    _pmGetMultiBuf reserves a length byte length multicast buffer, and stores its address in *bufp. It also reserves the resources to multicast to num_nodes PEs.

    RETURN VALUES
    _pmGetMultiBuf returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The value of length is out of range.
    ENOBUFS
    There is no available buffer.
    There is no enough resource to multicast.


    NAME
    _pmSendMulticast - multicast a message

    SYNOPSIS
    int _pmSendMulticast(pmCtx pmc, int *dst_nodes, int num_dst)

    DESCRIPTION
    _pmSendMulticast multicasts a message to num_dest logical PEs of the context pmc stored in dst_nodes.

    RETURN VALUES
    _pmSendMulticast returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The logical PE number stored in dst_nodes exceeds the range that is assigned to the context.
    EHOSTUNREACH
    Routes to some logical PEs do not exist.


    NAME
    _pmSendDone - tests all outstanding sends have done

    SYNOPSIS
    int _pmSendDone(pmCtx pmc)

    DESCRIPTION
    _pmSendDone tests whether all outstanding sending of context pmc have done.

    RETURN VALUES
    _pmSendDone returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EBUSY
    There are messages not yet sent.


  4. Receiving Messages
    NAME
    _pmReceiveInterrupt - control the receive interrupt

    SYNOPSIS
    int _pmReceiveInterrupt(pmCtx pmc, int sw)

    DESCRIPTION
    _pmReceiveInterrupt controls the receive interrupt of the context pmc. It enables the receive interrupt when (sw != 0) and disables when (sw == 0).

    RETURN VALUES
    _pmReceiveInterrupt returns zero on success. On failure, it returns the following error codes:

    ERRORS
    No errors.


    NAME
    _pmSignal - controls the receive signal

    SYNOPSYS
    int _pmSignal(pmCtx pmc, int signum)

    DESCRIPTION
    _pmSignal controls the receive signal of the context pmc It enables the receive signal when (signum != 0) and disables when (signum == 0).

    NOTICE
    To enable the receive signal, the receive interrupt must be enabled by _pmReceiveInterrupt.

    RETURN VALUES
    _pmSignal returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The value of signum is invalid.


    NAME
    _pmReceive - receive a message

    SYNOPSIS
    int _pmReceive(pmCtx pmc, caddr_t *bufp, size_t *lengthp)

    DESCRIPTION
    _pmReceive returns the address of the received message of the context pmc in *bufp and its length in *lengthp.

    RETURN VALUES
    _pmReceive returns zero on success. On failure, it returns the following errors.

    ERRORS
    ENOBUFS
    No message has arrived.
    EIO
    CRC error has occurred.(Fatal)
    EPIPE
    NRES(Network RESet) has occurred.(Fatal)


    NAME
    _pmReceiveWait - receive a message using interrupt

    SYNOPSIS
    int _pmReceiveWait(pmCtx pmc, caddr_t *bufp, size_t *lengthp)

    DESCRIPTION
    _pmReceiveWait blocks the process until a message of the context pmc arrives, and returns the address of the message in *bufp and its length in *lengthp.

    RETURN VALUES
    _pmReceiveWait returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EIO
    CRC error has occurred.(Fatal)
    EPIPE
    NRES(Network RESet) has occurred.(Fatal)


    NAME
    _pmPutReceiveBuf - return a receive buffer

    SYNOPSIS
    int _pmPutReceiveBuf(pmCtx pmc)

    DESCRIPTION
    _pmPutReceiveBuf returns a receive buffer of the context pmc to its buffer pool.

    RETURN VALUES
    _pmPutReceiveBuf returns zero on success. On failure, it returns the following error codes:

    ERRORS
    No errors.


  5. Remote Memory Access
    NAME
    _pmMLock - map and pin down a memory region (i386 only)

    SYNOPSIS
    int _pmMLock(pmCtx pmc, caddr_t addr, size_t size, u_int *virtp)

    DESCRIPTION
    _pmMLock maps the size byte memory region beginning at address addr and pins down the region. The result of mapping is returned in *virtp.

    RETURN VALUES
    _pmMLock returns zero on success. On failure, it returns the following error codes:

    ERRORS
    ENOSPC
    There are no enough resources to map the region.
    EFAULT
    The size of region to be mapped is too large.
    EAGAIN
    Total size of pinned down area is too large.
    ENOMEM
    Failed to pin down memory.


    NAME
    _pmMUnlock - unlock a memory region (i386 only)

    SYNOPSIS
    int _pmMUnlock(pmCtx pmc, caddr_t addr, size_t size)

    DESCRIPTION
    _pmMUnlock marks the size byte memory region beginning at address addr pinned down by _pmMLock as "Unused".
    Actual unpin down is delayed until _pmMLock finds no enough resources are available to pin down a new memory region.

    RETURN VALUES
    _pmMUnlock returns zero on success. On failure, it returns the following error codes:

    ERRORS
    ENOENT
    The memory region has not been pinned down by _pmMLock.


    NAME
    _pmMInvalidate - unpin a memory region (i386 only)

    SYNOPSYS
    int _pmMInvalidate(pmCtx pmc, caddr_t addr, size_t size)

    DESCRIPTION
    _pmMInvalidate unpin the size byte memory region beginning at address addr unlocked by _pmMUnlock.

    RETURN VALUES
    _pmMInvalidate returns zero on success. On failure, it returns the following error codes:

    ERRORS
    ENOENT
    The memory region has not been pinned down by _pmMLock.
    EBUSY
    The memory region has not been unlocked by _pmMUnlock.


    NAME
    _pmVWrite - data transfer between virtual memory (i386 only)

    SYNOPSIS
    int _pmVWrite(pmCtx pmc, int dst_node, u_int dst_addr, u_int src_addr, size_t length)

    DESCRIPTION
    _pmVWrite transfers length byte data beginning at virtual address src_addr to virtual address dst_addr of the destination node dst_node.
    Both src_addr and dst_addr are the mapped virtual address returned by _pmMLock.

    RETURN VALUES
    _pmVWrite returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The source virtual address is not pinned down by _pmMLock.
    The value of length is out of range.
    EHOSTUNREACH
    No route to dst_node exists.
    ENOBUFS
    There are not enough resources.

    BUGS
    When the address range exceeds the area mapped by _pmMLock, the data transfer is not executed and no error is returned.


    NAME
    _pmWriteDone - tests all outstanding DMAs have done

    SYNOPSYS
    int _pmWriteDone(pmCtx pmc)

    DESCRIPTION
    _pmWriteDone tests whether all outstanding DMAs of the context pmc issued by _pmVWrite already have done.

    RETURN VALUES
    _pmWriteDone returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EBUSY
    Some DMAs have not done yet.


  6. Network Context Switch
    NAME
    _pmSendActivate - enable/disable sending

    SYNOPSIS
    int _pmSendActivate(pmCtx pmc, int sw)

    DESCRIPTION
    _pmSendActivate enables (sw != 0) or disables(sw == 0) of sending messages of the context pmc.

    RETURN VALUES
    _pmSendActivate returns zero on success. On failure, it returns the following error codes:

    ERRORS
    No errors.


    NAME
    _pmSendStable - test a context is stable

    SYNOPSIS
    int _pmSendStable(pmCtx pmc)

    DESCRIPTION
    _pmSendStable tests whether the context is in a stable state, otherwise it returns EBUSY.
    "A Stable State" means the condition that, for all messages already sent, 1) a ACK confirms it has been received, or 2) a NACK forces it to be re-sent.
    NOTICE
    Sending must have been disabled for the context.

    RETURN VALUES
    _pmSendStable returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EIO
    Sending is enabled.
    EBUSY
    The context is not in a stable state.


    NAME
    _pmSaveContext - save a context

    SYNOPSIS
    int _pmSaveContext(pmCtx pmc, caddr_t address)

    DESCRIPTION
    _pmSaveContext saves the context pmc to the memory area pointed by address.

    RETURN VALUES
    _pmSaveContext returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EIO
    Sending is enabled.
    EBUSY
    The context cannot be saved temporarily.


    NAME
    _pmRestoreContext - restore a context

    SYNOPSIS
    int _pmRestoreContext(pmCtx pmc, caddr_t address)

    DESCRIPTION
    _pmRestoreContext restores the context pmc from the memory area pointed by address.

    RETURN VALUES
    _pmRestoreContext returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EIO
    Sending is enabled.
    The saved context is inconsistent.


    NAME
    _pmSaveSize - get the size of a context save area

    SYNOPSIS
    int _pmSaveSize(void)

    DESCRIPTION
    _pmSendSize returns the size of the context save area.

    RETURN VALUES
    _pmSendSize returns the size of the context save area.


  7. Miscellaneous
    NAME
    _pmGetStatus - get a context status

    SYNOPSIS
    int _pmGetStatus(pmCtx pmc, pmStatus *statp)

    DESCRIPTION
    _pmGetContextStatus returns the status of the context pmc in a pmStatus structure pointed by statp.
    typedef struct pm_context_status {
    	int	recv_message;	/* # of messages in recv. queue */
    	int	recv_size;	/* # of bytes in recv. queue */
    	int	send_message;	/* # of messages in send queue */
    	int	send_size;	/* # of bytes in send queue */
    	int	pend_message;	/* # of messages waiting Ack or retrying */
    	int	pend_size;	/* # of bytes waiting Ack or retrying */
    	u_int	resend_count;	/* # of re-sent messages */
    	u_int	double_resend;	/* # of doulbly re-sent message */
    } pmStatus;
    

    RETURN VALUES
    _pmGetStatus returns zero on success. On failure, it returns the following error codes:

    ERRORS
    No errors.


    NAME
    _pmGetVersion - get a version string of mcp

    SYNOPSIS
    int _pmGetVersion(int unit, char **vp)

    DESCRIPTION
    _pmVersion returns a version strings of the LANai program of unit unit in *vp.

    RETURN VALUES
    _pmVersion returns zero on success. On failure, it returns the following error codes:

    ERRORS
    EINVAL
    The value of unit is out of range.
    ENODEV
    No LANai device is mmap(2)ed.


    NAME
    _pmErrorString - convert a error number to a string

    SYNOPSIS
    char *_pmErrorString(int error)

    DESCRIPTION
    _pmErrorString returns a string that corresponds to the error code error.

    RETURN VALUES
    _pmErrorString returns a error string.


  8. PM v1.0 Compatible Entries These entries are for the programs using PM API v1.0.
    
    int _pmLANaiInit(int unit, char *mcp, char *config);
    int _pmOpenChannel(int unit, int chan, pmCtx *pmcp);
    int _pmCloseChannel(pmCtx pmc);
    int _pmInitChannel(int pmdesc, pmCtx *pmcp);
    int _pmResetChannel(pmCtx pmc);
    int _pmSaveChannel(pmCtx pmc, caddr_t address);
    int _pmRestoreChannel(pmCtx pmc, caddr_t address);
    int _pmDesc(pmCtx pmc, int *descp);
    
    

  9. Sample Programs

    Initialization


    
    pmCtx	pmc;
    int	pmdesc;
    
    int
    init_system()
    {
    	int	unit, chan, ctx, error;
    	int	pe_base, pe_num;
    	char	*mcp, *config;
    
    	mcp = "pm.mcp";
    	config = "pm.cnf";
    	if ((error = _pmInit(unit, mcp, config)) != 0)
    		return (error);
    	if ((error = _pmOpenContext(unit, ctx, &pmc)) != 0)
    		return (error);
    	if ((error = _pmLoadContext(pmc, chan)) != 0)
    		return (error);
    	if ((error = _pmBindPE(pmc, pe_base, pe_num)) != 0)
    		return (error);
    	_pmChanDesc(unit, chan, &pmdesc);
    	return (0);
    }
    
    /*
     * pmdesc is passed to user program.
     */
    int
    init_user()
    {
    	int	error;
    
    	if ((error = _pmInitContext(pmdesc, &pmc)) != 0)
    		return (error);
    	return (0);
    }
    
    

    Sending Messages


    
    int
    send()
    {
    	pmCtx	pmc;
    	caddr_t	buf;
    	size_t	length;
    	int	error;
    
    	while ((error = _pmGetSendBuf(pmc, &buf, length)) == ENOBUFS)
    		;
    	if (error != 0)
    		return (error);
    	/*
    	 * Fill buffer
    	 */
    	if ((error = _pmSend(pmc, dest)) != 0)
    		return (error);
    	return (0);
    }
    
    

    Multicasting Messages


    
    int
    send_multi()
    {
    	pmCtx	pmc;
    	caddr_t	buf;
    	size_t	length;
    	int	error;
    	int	dest[NDEST];
    
    	while ((error = _pmGetMultiBuf(pmc, &buf, length, NDEST)) == ENOBUFS)
    		;
    	if (error != 0)
    		return (error);
    	/*
    	 * Fill buffer
    	 */
    	dest[0] = dest0;
    	dest[1] = dest1;
    	...
    	dest[NDEST-1] = destN_1;
    	while ((error = _pmSendMulticast(pmc, dest, NDEST)) != ENOBUFS)
    		;
    	if (error != 0)
    		return (error);
    	return (0);
    }
    
    

    Receiving Messages


    
    int
    receive()
    {
    	pmCtx	pmc;
    	caddr_t	buf;
    	size_t	length;
    	int	error;
    
    	while ((error = _pmReceive(pmc, &buf, &length)) == ENOBUFS)
    		;
    	if (error != 0)
    		return (error);
    	/*
    	 * Process Message
    	 */
    	if ((error = _pmPutReceiveBuf(pmc)) != 0)
    		return (error);
    	return (0);
    }
    
    

    Network Context Switch


    
    caddr_t	save;
    
    int
    save_context()
    {
    	pmCtx	pmc;
    	int	error;
    
    	save = malloc(_pmSaveSize());
    	if ((error = _pmSendActivate(pmc, 0)) != 0)
    		return (error);
    	while ((error = _pmSendStable(pmc)) == EBUSY)
    		;
    	if (error != 0)
    		return (error);
    	if ((error = _pmSaveContext(pmc, save)) != 0)
    		return (error);
    	return (0);
    }
    
    int
    restore_context()
    {
    	pmCtx	pmc;
    	int	error;
    
    	if ((error = _pmRestoreContext(pmc, save)) != 0)
    		return (error);
    	if ((error = _pmSendActivate(pmc, 1)) != 0)
    		return (error);
    	return (0);
    }
    
    

    A Ping-pong program


    
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    
    #include "Pm.h"
    
    #ifdef sun
    #define	htonl(x)	(x)
    #define	ntohl(x)	(x)
    
    extern int fprintf(FILE *, char *, ...);
    #endif
    
    void	reply(int);
    void	pingpong(int, int, int, int);
    void	print_error(char *, int);
    
    char	*mcp = "pm.mcp";
    char	*config = "pm.cnf";
    int	unit = 0;
    int	channel = 0;
    int	context = 0;
    int	dest;
    int	length = 8;
    int	iteration = 10000;
    int	reply_flag = 0;
    int	self;
    
    int
    main(int argc, char **argv)
    {
    	int	error;
    	pmCtx	pmc;
    
    	if (argc == 1) {
    		fprintf(stderr, "%s: option\n", argv[0]);
    		exit(1);
    	}
    	for (argc--, argv++; argc > 0; argc--, argv++) {
    		if (strcmp(argv[0], "-reply") == 0) {
    			reply_flag = 1;
    			continue;
    		}
    		if (strcmp(argv[0], "-unit") == 0) {
    			unit = atoi(argv[1]);
    			argc--;
    			argv++;
    			continue;
    		}
    		if (strcmp(argv[0], "-chan") == 0) {
    			channel = atoi(argv[1]);
    			argc--;
    			argv++;
    			continue;
    		}
    		if (strcmp(argv[0], "-ctx") == 0) {
    			context = atoi(argv[1]);
    			argc--;
    			argv++;
    			continue;
    		}
    		if (strcmp(argv[0], "-dest") == 0) {
    			dest = atoi(argv[1]);
    			argc--;
    			argv++;
    			continue;
    		}
    		if (strcmp(argv[0], "-len") == 0) {
    			length = atoi(argv[1]);
    			argc--;
    			argv++;
    			continue;
    		}
    		if (strcmp(argv[0], "-iter") == 0) {
    			iteration = atoi(argv[1]);
    			argc--;
    			argv++;
    			continue;
    		}
    		if (strcmp(argv[0], "-mcp") == 0) {
    			mcp = argv[1];
    			argc--;
    			argv++;
    			continue;
    		}
    		if (strcmp(argv[0], "-conf") == 0) {
    			config = argv[1];
    			argc--;
    			argv++;
    			continue;
    		}
    		fprintf(stderr, "unknown option %s\n", argv[0]);
    		exit(1);
    	}
    	if ((error = _pmInit(unit, mcp, config)) != 0) {
    		print_error("_pmLANaiInit", error);
    		exit(1);
    	}
    	if ((error = _pmOpenContext(unit, context, &pmc)) != 0) {
    		print_error("_pmOpenContext", error);
    		exit(1);
    	}
    	if ((error = _pmLoadContext(pmc, chan)) != 0) {
    		print_error("_pmLoadContext", error);
    		exit(1);
    	}
    
    	if ((error = _pmSelf(pmc, &self)) != 0) {
    		print_error("_pmSelf", error);
    		exit(1);
    	}
    
    	if (reply_flag)
    		reply(pmc);
    	else
    		pingpong(pmc, dest, length, iteration);
    
    	exit(0);
    }
    
    void
    reply(pmCtx pmc)
    {
    	caddr_t	addr, addr2;
    	size_t	length;
    	int	error, dest;
    
    	for (;;) {
    		while ((error = _pmReceive(pmc, &addr, &length)) == ENOBUFS)
    			;
    		if (error != 0) {
    			print_error("_pmReceive", error);
    			exit(1);
    		}
    		dest = ntohl(*(int *)addr);
    		while ((error = _pmGetSendBuf(pmc, &addr2, length)) ==
    		      ENOBUFS)
    			;
    		if (error != 0) {
    			print_error("_pmGetSendBuf", error);
    			exit(1);
    		}
    		*(int *)addr2 = htonl(self);
    		if ((error = _pmSend(pmc, dest)) != 0) {
    			print_error("_pmSend", error);
    			exit(1);
    		}
    		(void) _pmPutReceiveBuf(pmc);
    	}
    }
    
    void
    pingpong(pmCtx pmc, int dest, size_t length, int n)
    {
    	caddr_t	addr, addr2 = NULL;
    	int	i, error, length2;
    
    	for (i = 0; i < n; i++) {
    		while ((error = _pmGetSendBuf(pmc, &addr, length)) ==
    		    ENOBUFS)
    			;
    		if (error != 0) {
    			print_error("_pmGetSendBuf", error);
    			exit(1);
    		}
    		*(int *)addr = htonl(self);
    		if ((error = _pmSend(pmc, dest)) != 0) {
    			print_error("_pmSend", error);
    			exit(1);
    		}
    		if (addr2 != NULL)
    			(void) _pmPutReceiveBuf(pmc);
    		while ((error = _pmReceive(pmc, &addr2, &length2)) ==
    		    ENOBUFS)
    			;
    		if (error != 0) {
    			print_error("_pmReceive", error);
    			exit(1);
    		}
    	}
    	if (addr2 != NULL)
    		(void) _pmPutReceiveBuf(pmc);
    }
    
    void
    print_error(char *msg, int error)
    {
    
    	fprintf(stderr, "%s: %s\n", msg, _pmErrorString(error));
    }