SCore-D Device Programming Manual (6)


SYNOPSIS

#include <scdev.h>

int device_initialize( int argc, char** argv )
void device_log( char* format, ... )
void device_terminate( int exit_code )
int get_iocell( int cell, char** iobuf )
int read_device_op( int* tag, int* cell, ioarg_t* offset0, ioarg_t* offset1, int fd )
void set_iocell_length( int cell, ioarg_t length )
void set_tcp_nodelay( int fd )
int write_device_ack( int fd, int status, int cell )

DESCRIPTION
SCore-D invokes a device server process when SCore-D accepts device addition request via sc_devadd() systemcall. When a user parallel process requests a device open, SCore-D informs the request to the corresponding device server process. Upon accepting the open request, a device server process must fork a child process, and the child process must handle actual I/O request, such as read or write.

A library documented in this page supports the protocol handling between SCore-D and device (server) process. device_initialize() function establishs the connection between SCore-D and device (server) process. device_log() function outputs some messages output from device (server) process to SCore-D syslog facility. device_terminate() function is to terminate device (server) process and never returns from the function. The actual SCore-D and device (server) communication uses via System-V shared memory IPC mechanism, so that number memory copying can be reduced. The shared memory region is segmented, and each segmented region is called "(I/O) cell." read_device_op() function waits for an I/O request from a user parallel process through SCore-D. offset0 and offset1 arguments are passed from an I/O request associated with sc_read() or sc_write(). The semantics of those arguments are device dependent. get_iocell() function extracts a buffer region of a transaction, and return length information associated with the I/O request. set_iocell_length() sets the length as a result of a read operation. write_device_ack() function returns a status of an I/O transaction. set_tcp_nodelay() function is a utility routine for a TCP/IP port.

SAMPLE CODING
This SCOre-D device example realizes a simple null device.
Device Server Process
The first part of device program is a main loop waiting for device open request or device shutdown request. At the very beginning of the main function, device_initialize() function should be called. Each I/O request has a tag indicating which I/O operation is requested. In this main loop, only IO_MESG_OPEN for device open request and IO_MESG_TERM for device server shutdown request are acceptable.

Upon receiving a IO_MESG_OPEN request, a device server process forks a child process in which actual I/O requests are handled.

#include <scdev.h>

int main( int argc, char **argv ) {
  struct sockaddr addr;
  pid_t pid;
  int addrsize;
  int fd;
  int tag;
  int cell;
  ioarg_t offset0, offset1;

  if( device_initialize( argc, argv ) < 0 ) device_terminate( 3 );
  device_log( "Device Initialized !!" );

  while( 1 ) {
    addrsize = sizeof( struct sockaddr );
    if( ( fd = accept( fd_listen, &addr, &addrsize ) ) < 0 ) {
      if( errno == EINTR ) continue; /* in case of SIGCHLD */
      device_log( "Accept error" );
      device_terminate( 1 );
    }
    set_tcp_nodelay( fd );
    if( read_device_op( &tag, &cell, &offset0, &offset1, fd ) != 0 ) {
      device_log( "Error in read device-op" );
      break;
    }
    switch( tag ) {
    case IO_MESG_OPEN:
      if( ( pid = fork() ) == 0 ) { /* if device process */
	null_device( fd );
      } else if ( pid > 0 ) {	/* if device server process */
	if( write_device_ack( fd, NORMAL_END, cell ) != 0 ) {
	  device_log( "Error in write device ack" );
	}
	close( fd );
      } else {			/* if error in fork() */
	if( write_device_ack( fd, EIO, cell ) != 0 ) {
	  device_log( "Error in write device ack" );
	}
	device_log( "Cannot fork device process" );
      }
      break;
    case IO_MESG_TERM:
      device_log( "device shutdown" );
      device_terminate( 0 );
      break;
    default:
      device_log( "Unknown I/O message tag (%d) (cell=%d,off=%d,wh=%d)",
		 tag, cell, offset, whence );
      device_terminate( 1 );
      break;
    }
  }
  return( 0 );
}

Device Process
In device process, there can be a loop in which waiting for an I/O request and processing the I/O request. In this example for null device, a write request (IO_MESG_WRITE) is a nop, and a read request fills corresponding I/O cell with zero.
#include <scdev.h>

void null_device ( int fd ) {
  int tag;
  int cell;
  ioarg_t offset0, offset1;
  char pseudo_dev[IO_BUFFER_SIZE];
  char *iobuf;
  int length;
  
  while( 1 ) {
    if( read_device_op( &tag, &cell, &offset0, &offset1, fd ) != 0 ) {
      device_log( "Error in read device-op" );
      device_terminate( 1 );
    }
    switch( tag ) {
    case IO_MESG_WRITE:
      length = get_iocell( cell, &iobuf );
      /* actual write operation here */
      if( write_device_ack( fd, NORMAL_END, cell ) != 0 ) {
	device_log( "Error in write device ack" );
      }
      break;
    case IO_MESG_READ:
      length = get_iocell( cell, &iobuf );
      bzero( iobuf, length );
      set_iocell_length( cell, length );
      if( write_device_ack( fd, NORMAL_END, cell ) != 0 ) {
	device_log( "Error in write device ack" );
      }
      break;
    case IO_MESG_CLOSE:
      if( write_device_ack( fd, NORMAL_END, cell ) != 0 ) {
	device_log( "Error in write device ack" );
      }
      close( fd );
      device_terminate( 0 );
      break;
    default:
      device_log( "Unknown I/O message tag" );
      device_terminate( 1 );
      break;
    }
  }
}

RETURN VALUES
All interger functions return zero if they succeed, and return -1 if they fail.

BUGS
If multiple requests are buffered in TCP/IP socket, they should be processed all together.

SEE ALSO

Real World Computing Partnership
Parallel Distributed System Software Tsukuba Laboratory
score-info@rwcp.or.jp

$Id: scored-devpro.html,v 1.4 1998/05/06 10:09:26 hori Exp $