blob: 4a1cc548e747dd7e1b07e699dd47a15f26bfd970 [file] [log] [blame]
/*
* Copyright (c) 2000-2001 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef RING_H
#define RING_H
/* ring - readahead/writeahead abstraction
*
* the ring is conceptually an ordered set of messages circulating between the
* client thread and the I/O slave thread. a message can be in one of four
* places: on the ready queue, held by the client, on the active queue, or held
* by the slave. The client and slave can each hold at most one message at a
* time. all others must be on one of the two queues. the messages must
* circulate in that order: ready, client, active, slave, ready, ...
* initially all messages are on the ready queue, with status set to
* INIT. The client uses ring_get to remove a message from the ready queue.
* the client can then use the message to read or write. to read, the client
* sets the op field to READ, and places the message on the active queue. the
* slave will remove messages from the active queue, invoke the client-supplied
* read function with the message's buffer, record the read function's return
* value in the message, set the message status to OK (read function returned 0)
* or ERROR (read returned non-zero), and place the message on the ready queue.
* the client will see the message after removing all messages ahead of it on
* the ready queue. to write, the client follows almost the same procedure,
* except the client fills the buffer and sets the op to WRITE prior to placing
* the message on the active queue.
*
* if the client-supplied read or write function returns an error, the slave
* will set the message status to ERROR. the slave will pass all subsequent
* messages appearing on the active queue directly to the ready queue with
* no I/O done and the message status set to IGNORE. the slave will remain
* in this state until a reset is performed (see below).
*
* The client may at anytime place a NOP msg on the ring. the slave does
* nothing with this mmessage other than to place it back on the ready queue
* with NOPACK status. This is useful for inhibiting read-ahead.
*
* To flush the ring, the client must repetatively place TRACE messages on the
* active queue until it sees an IGNORE msg on the ready queue. the slave will
* simply transfer TRACErs from active to ready with no other action taken
* (other than to set the message status to IGNORE).
*
* the client may at any time reset the ring. the reset will return to the
* client when the current I/O being executed by the slave completes, and
* all messages have been wiped clean and placed on the ready queue with
* status set to INIT. the ring_reset function accomplishes this internally by
* placing a RESET message on the active QUEUE, and continuing to remove
* messages from the ready queue (without placing them on the active queue)
* until the RESET message is seen the slave responds to a reset message by
* setting the status to RESETACK, queueing the message on the ready queue, and
* waiting for a message from the active queue. ring_reset will then re-
* initialize the ring and return. note that the client may be holding one
* message at the time the reset is called. if so, it must pass a pointer to
* that message into the reset call. otherwise it must pass in NULL.
*
* the ring_destroy function may be invoked to shut down the ring and kill the
* slave thread. it simply places a DIE message on the active queue, and waits
* for a DIEACK response. it then de-allocates all semaphores memory allocated
* by ring_create.
*
* the message structure includes a 64 bit field for the convenience
* of the client. it is not perturbed during any ring operations.
*
* the ring maintains four performance metering values: the number of times
* the slave and client attempted to get a message, and the number of times
* those attempts resulting in blocking.
*/
/* ring_msg - structure of messages managed by ring
*/
enum ring_op { RING_OP_NONE,
RING_OP_READ,
RING_OP_WRITE,
RING_OP_NOP,
RING_OP_TRACE,
RING_OP_RESET,
RING_OP_DIE };
typedef enum ring_op ring_op_t;
enum ring_stat { RING_STAT_INIT,
RING_STAT_OK,
RING_STAT_ERROR,
RING_STAT_NOPACK,
RING_STAT_IGNORE,
RING_STAT_RESETACK,
RING_STAT_DIEACK };
typedef enum ring_stat ring_stat_t;
enum ring_loc { RING_LOC_READY,
RING_LOC_ACTIVE,
RING_LOC_CLIENT,
RING_LOC_SLAVE };
typedef enum ring_loc ring_loc_t;
struct ring_msg {
ring_op_t rm_op;
ring_stat_t rm_stat;
int rm_rval;
off64_t rm_user;
char *rm_bufp;
/* ALL BELOW PRIVATE!!! */
size_t rm_mix;
ring_loc_t rm_loc;
};
typedef struct ring_msg ring_msg_t;
/* ring - instantiation of a ring
*/
struct ring {
off64_t r_client_msgcnt;
off64_t r_slave_msgcnt;
off64_t r_client_blkcnt;
off64_t r_slave_blkcnt;
time32_t r_first_io_time;
off64_t r_all_io_cnt;
/* ALL BELOW PRIVATE!!! */
size_t r_len;
ring_msg_t *r_msgp;
size_t r_ready_in_ix;
size_t r_ready_out_ix;
qsemh_t r_ready_qsemh;
size_t r_active_in_ix;
size_t r_active_out_ix;
qsemh_t r_active_qsemh;
size_t r_client_cnt;
size_t r_slave_cnt;
int ( *r_readfunc )( void *contextp, char *bufp );
int ( *r_writefunc )( void *contextp, char *bufp );
void *r_clientctxp;
};
typedef struct ring ring_t;
/* ring_create - creates a ring. parameters supply the length of the ring,
* the read/write buffer size, the drive index, a function for reading, a
* function for writing, and a pointer to client context for the read and
* write functions. returns a pointer to a ring if successful, a NULL
* pointer if not. the read and write functions should return 0 on success,
* or an error code on failure which will be recorded in the rm_rval field
* of the message invoking the failed operation. if null pointer returned,
* the location pointed to by rvalp will contain one of the following:
* ENOMEM - could not allocate some portion of the ring memory;
* E2BIG - insufficient physical memory available for pinning;
* EPERM - exceeds allowed amount of pinned down memory.
*/
extern ring_t *ring_create( size_t ringlen,
size_t bufsz,
bool_t pinpr,
ix_t drive_index,
int ( * readfunc )( void *clientctxp, char *bufp ),
int ( * writefunc )( void *clientctxp, char *bufp ),
void *clientctxp,
int *rvalp );
/* ring_get - get a message off the ready queue
*/
extern ring_msg_t *ring_get( ring_t *ringp );
/* ring_put - put a message on the active queue
*/
extern void ring_put( ring_t *ringp, ring_msg_t *msgp );
/* ring_reset - re-initialize the ring, after the current I/O completes.
* msgp must be NULL if the client is not currently holding a ring message.
* otherwise it must point to that message.
*/
extern void ring_reset( ring_t *ringp, ring_msg_t *msgp );
/* ring_destroy - de-allocates ring
*/
extern void ring_destroy( ring_t *ringp );
#endif /* RING_H */