Run a revision of the devstat interface:
Kernel: Change statistics to use the *uptime() timescale (ie: relative to boottime) rather than the UTC aligned timescale. This makes the device statistics code oblivious to clock steps. Change timestamps to bintime format, they are cheaper. Remove the "busy_count", and replace it with two counter fields: "start_count" and "end_count", which are updated in the down and up paths respectively. This removes the locking constraint on devstat. Add a timestamp argument to devstat_start_transaction(), this will normally be a timestamp set by the *_bio() function in bp->bio_t0. Use this field to calculate duration of I/O operations. Add two timestamp arguments to devstat_end_transaction(), one is the current time, a NULL pointer means "take timestamp yourself", the other is the timestamp of when this transaction started (see above). Change calculation of busy_time to operate on "the salami principle": Only when we are idle, which we can determine by the start+end counts being identical, do we update the "busy_from" field in the down path. In the up path we accumulate the timeslice in busy_time and update busy_from. Change the byte_* and num_* fields into two arrays: bytes[] and operations[]. Userland: Change the misleading "busy_time" name to be called "snap_time" and make the time long double since that is what most users need anyway, fill it using clock_gettime(CLOCK_MONOTONIC) to put it on the same timescale as the kernel fields. Change devstat_compute_etime() to operate on struct bintime. Remove the version 2 legacy interface: the change to bintime makes compatibility far too expensive. Fix a bug in systat's "vm" page where boot relative busy times would be bogus. Bump __FreeBSD_version to 500107 Review & Collaboration by: ken
This commit is contained in:
parent
d16675cd85
commit
f432014308
@ -3,7 +3,8 @@
|
||||
MAINTAINER=ken@FreeBSD.ORG
|
||||
|
||||
LIB= devstat
|
||||
SHLIB_MAJOR= 3
|
||||
# Bump DEVSTAT_USER_API_VER in devstat.h every time this is incremented.
|
||||
SHLIB_MAJOR= 4
|
||||
SRCS= devstat.c
|
||||
INCS= devstat.h
|
||||
|
||||
|
@ -315,6 +315,7 @@ devstat_getdevs(kvm_t *kd, struct statinfo *stats)
|
||||
int retval = 0;
|
||||
struct devinfo *dinfo;
|
||||
const char *func_name = "devstat_getdevs";
|
||||
struct timespec ts;
|
||||
|
||||
dinfo = stats->dinfo;
|
||||
|
||||
@ -327,8 +328,8 @@ devstat_getdevs(kvm_t *kd, struct statinfo *stats)
|
||||
oldnumdevs = dinfo->numdevs;
|
||||
oldgeneration = dinfo->generation;
|
||||
|
||||
/* Get the current time when we get the stats */
|
||||
gettimeofday(&stats->busy_time, NULL);
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
stats->snap_time = ts.tv_sec + ts.tv_nsec * 1e-9;
|
||||
|
||||
if (kd == NULL) {
|
||||
/* If this is our first time through, mem_ptr will be null. */
|
||||
@ -338,7 +339,7 @@ devstat_getdevs(kvm_t *kd, struct statinfo *stats)
|
||||
* error. Don't bother setting the error string, since
|
||||
* getnumdevs() has already done that for us.
|
||||
*/
|
||||
if ((dinfo->numdevs = getnumdevs()) < 0)
|
||||
if ((dinfo->numdevs = devstat_getnumdevs(NULL)) < 0)
|
||||
return(-1);
|
||||
|
||||
/*
|
||||
@ -371,9 +372,9 @@ devstat_getdevs(kvm_t *kd, struct statinfo *stats)
|
||||
if (errno == ENOMEM) {
|
||||
/*
|
||||
* No need to set the error string here,
|
||||
* getnumdevs() will do that if it fails.
|
||||
* devstat_getnumdevs() will do that if it fails.
|
||||
*/
|
||||
if ((dinfo->numdevs = getnumdevs()) < 0)
|
||||
if ((dinfo->numdevs = devstat_getnumdevs(NULL)) < 0)
|
||||
return(-1);
|
||||
|
||||
dssize = (dinfo->numdevs *
|
||||
@ -403,7 +404,7 @@ devstat_getdevs(kvm_t *kd, struct statinfo *stats)
|
||||
* This is of course non-atomic, but since we are working
|
||||
* on a core dump, the generation is unlikely to change
|
||||
*/
|
||||
if ((dinfo->numdevs = getnumdevs()) == -1)
|
||||
if ((dinfo->numdevs = devstat_getnumdevs(NULL)) == -1)
|
||||
return(-1);
|
||||
if ((dinfo->mem_ptr = get_devstat_kvm(kd)) == NULL)
|
||||
return(-1);
|
||||
@ -430,8 +431,8 @@ devstat_getdevs(kvm_t *kd, struct statinfo *stats)
|
||||
* necessary.
|
||||
*/
|
||||
if (oldgeneration != dinfo->generation) {
|
||||
if (getnumdevs() != dinfo->numdevs) {
|
||||
if ((dinfo->numdevs = getnumdevs()) < 0)
|
||||
if (devstat_getnumdevs(NULL) != dinfo->numdevs) {
|
||||
if ((dinfo->numdevs = devstat_getnumdevs(NULL)) < 0)
|
||||
return(-1);
|
||||
dssize = (dinfo->numdevs * sizeof(struct devstat)) +
|
||||
sizeof(long);
|
||||
@ -1142,21 +1143,21 @@ compute_stats(struct devstat *current, struct devstat *previous,
|
||||
DSM_NONE));
|
||||
}
|
||||
|
||||
|
||||
/* This is 1/2^64 */
|
||||
#define BINTIME_SCALE 5.42101086242752217003726400434970855712890625e-20
|
||||
|
||||
long double
|
||||
devstat_compute_etime(struct timeval cur_time, struct timeval prev_time)
|
||||
devstat_compute_etime(struct bintime *cur_time, struct bintime *prev_time)
|
||||
{
|
||||
struct timeval busy_time;
|
||||
u_int64_t busy_usec;
|
||||
long double etime;
|
||||
|
||||
timersub(&cur_time, &prev_time, &busy_time);
|
||||
|
||||
busy_usec = busy_time.tv_sec;
|
||||
busy_usec *= 1000000;
|
||||
busy_usec += busy_time.tv_usec;
|
||||
etime = busy_usec;
|
||||
etime /= 1000000;
|
||||
|
||||
etime = cur_time->sec;
|
||||
etime += cur_time->frac * BINTIME_SCALE;
|
||||
if (prev_time != NULL) {
|
||||
etime -= prev_time->sec;
|
||||
etime -= prev_time->frac * BINTIME_SCALE;
|
||||
}
|
||||
return(etime);
|
||||
}
|
||||
|
||||
@ -1186,21 +1187,21 @@ devstat_compute_statistics(struct devstat *current, struct devstat *previous,
|
||||
return(-1);
|
||||
}
|
||||
|
||||
totalbytesread = current->bytes_read -
|
||||
((previous) ? previous->bytes_read : 0);
|
||||
totalbyteswrite = current->bytes_written -
|
||||
((previous) ? previous->bytes_written : 0);
|
||||
totalbytesread = current->bytes[DEVSTAT_READ] -
|
||||
((previous) ? previous->bytes[DEVSTAT_READ] : 0);
|
||||
totalbyteswrite = current->bytes[DEVSTAT_WRITE] -
|
||||
((previous) ? previous->bytes[DEVSTAT_WRITE] : 0);
|
||||
|
||||
totalbytes = totalbytesread + totalbyteswrite;
|
||||
|
||||
totaltransfersread = current->num_reads -
|
||||
((previous) ? previous->num_reads : 0);
|
||||
totaltransfersread = current->operations[DEVSTAT_READ] -
|
||||
((previous) ? previous->operations[DEVSTAT_READ] : 0);
|
||||
|
||||
totaltransferswrite = current->num_writes -
|
||||
((previous) ? previous->num_writes : 0);
|
||||
totaltransferswrite = current->operations[DEVSTAT_WRITE] -
|
||||
((previous) ? previous->operations[DEVSTAT_WRITE] : 0);
|
||||
|
||||
totaltransfersother = current->num_other -
|
||||
((previous) ? previous->num_other : 0);
|
||||
totaltransfersother = current->operations[DEVSTAT_NO_DATA] -
|
||||
((previous) ? previous->operations[DEVSTAT_NO_DATA] : 0);
|
||||
|
||||
totaltransfers = totaltransfersread + totaltransferswrite +
|
||||
totaltransfersother;
|
||||
@ -1496,7 +1497,7 @@ get_devstat_kvm(kvm_t *kd)
|
||||
char *rv = NULL;
|
||||
const char *func_name = "get_devstat_kvm";
|
||||
|
||||
if ((num_devs = getnumdevs()) <= 0)
|
||||
if ((num_devs = devstat_getnumdevs(kd)) <= 0)
|
||||
return(NULL);
|
||||
error = 0;
|
||||
if (KREADNL(kd, X_DEVICE_STATQ, dhead) == -1)
|
||||
@ -1510,7 +1511,7 @@ get_devstat_kvm(kvm_t *kd)
|
||||
func_name);
|
||||
return(NULL);
|
||||
}
|
||||
gen = getgeneration();
|
||||
gen = devstat_getgeneration(kd);
|
||||
memcpy(rv, &gen, sizeof(gen));
|
||||
wp = sizeof(gen);
|
||||
/*
|
||||
@ -1536,66 +1537,3 @@ get_devstat_kvm(kvm_t *kd)
|
||||
}
|
||||
return(rv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compatability functions for libdevstat 2. These are deprecated and may
|
||||
* eventually be removed.
|
||||
*/
|
||||
int
|
||||
getnumdevs(void)
|
||||
{
|
||||
return(devstat_getnumdevs(NULL));
|
||||
}
|
||||
|
||||
long
|
||||
getgeneration(void)
|
||||
{
|
||||
return(devstat_getgeneration(NULL));
|
||||
}
|
||||
|
||||
int
|
||||
getversion(void)
|
||||
{
|
||||
return(devstat_getversion(NULL));
|
||||
}
|
||||
|
||||
int
|
||||
checkversion(void)
|
||||
{
|
||||
return(devstat_checkversion(NULL));
|
||||
}
|
||||
|
||||
int
|
||||
getdevs(struct statinfo *stats)
|
||||
{
|
||||
return(devstat_getdevs(NULL, stats));
|
||||
}
|
||||
|
||||
int
|
||||
selectdevs(struct device_selection **dev_select, int *num_selected,
|
||||
int *num_selections, long *select_generation,
|
||||
long current_generation, struct devstat *devices, int numdevs,
|
||||
struct devstat_match *matches, int num_matches,
|
||||
char **dev_selections, int num_dev_selections,
|
||||
devstat_select_mode select_mode, int maxshowdevs,
|
||||
int perf_select)
|
||||
{
|
||||
|
||||
return(devstat_selectdevs(dev_select, num_selected, num_selections,
|
||||
select_generation, current_generation, devices, numdevs,
|
||||
matches, num_matches, dev_selections, num_dev_selections,
|
||||
select_mode, maxshowdevs, perf_select));
|
||||
}
|
||||
|
||||
int
|
||||
buildmatch(char *match_str, struct devstat_match **matches,
|
||||
int *num_matches)
|
||||
{
|
||||
return(devstat_buildmatch(match_str, matches, num_matches));
|
||||
}
|
||||
|
||||
long double
|
||||
compute_etime(struct timeval cur_time, struct timeval prev_time)
|
||||
{
|
||||
return(devstat_compute_etime(cur_time, prev_time));
|
||||
}
|
||||
|
@ -35,6 +35,16 @@
|
||||
|
||||
#include <kvm.h>
|
||||
|
||||
/*
|
||||
* Bumped every time we change the userland API. Hopefully this doesn't
|
||||
* happen very often! This should be bumped every time we have to
|
||||
* increment SHLIB_MAJOR in the libdevstat Makefile (for non-backwards
|
||||
* compatible API changes) and should also be bumped every time we make
|
||||
* backwards-compatible API changes, so application writers have a way to
|
||||
* determine when a particular feature is available.
|
||||
*/
|
||||
#define DEVSTAT_USER_API_VER 4
|
||||
|
||||
#define DEVSTAT_ERRBUF_SIZE 2048 /* size of the devstat library error string */
|
||||
|
||||
extern char devstat_errbuf[];
|
||||
@ -111,7 +121,7 @@ struct statinfo {
|
||||
long tk_nin;
|
||||
long tk_nout;
|
||||
struct devinfo *dinfo;
|
||||
struct timeval busy_time;
|
||||
long double snap_time;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@ -122,31 +132,7 @@ typedef enum {
|
||||
} devstat_select_mode;
|
||||
|
||||
__BEGIN_DECLS
|
||||
/* Legacy interfaces. */
|
||||
int getnumdevs(void);
|
||||
long getgeneration(void);
|
||||
int getversion(void);
|
||||
int checkversion(void);
|
||||
int getdevs(struct statinfo *stats);
|
||||
int selectdevs(struct device_selection **dev_select, int *num_selected,
|
||||
int *num_selections, long *select_generation,
|
||||
long current_generation, struct devstat *devices, int numdevs,
|
||||
struct devstat_match *matches, int num_matches,
|
||||
char **dev_selections, int num_dev_selections,
|
||||
devstat_select_mode select_mode, int maxshowdevs,
|
||||
int perf_select);
|
||||
int buildmatch(char *match_str, struct devstat_match **matches,
|
||||
int *num_matches);
|
||||
int compute_stats(struct devstat *current, struct devstat *previous,
|
||||
long double etime, u_int64_t *total_bytes,
|
||||
u_int64_t *total_transfers, u_int64_t *total_blocks,
|
||||
long double *kb_per_transfer,
|
||||
long double *transfers_per_second, long double *mb_per_second,
|
||||
long double *blocks_per_second,
|
||||
long double *ms_per_transaction);
|
||||
long double compute_etime(struct timeval cur_time, struct timeval prev_time);
|
||||
|
||||
/* New interfaces. */
|
||||
int devstat_getnumdevs(kvm_t *kd);
|
||||
long devstat_getgeneration(kvm_t *kd);
|
||||
int devstat_getversion(kvm_t *kd);
|
||||
@ -164,8 +150,8 @@ int devstat_buildmatch(char *match_str, struct devstat_match **matches,
|
||||
int devstat_compute_statistics(struct devstat *current,
|
||||
struct devstat *previous,
|
||||
long double etime, ...);
|
||||
long double devstat_compute_etime(struct timeval cur_time,
|
||||
struct timeval prev_time);
|
||||
long double devstat_compute_etime(struct bintime *cur_time,
|
||||
struct bintime *prev_time);
|
||||
__END_DECLS
|
||||
|
||||
#endif /* _DEVSTAT_H */
|
||||
|
@ -370,7 +370,7 @@ haveadisk(void)
|
||||
void
|
||||
updatexfers(int numdevs, int *devs)
|
||||
{
|
||||
register int i, j, t;
|
||||
register int i, j, k, t;
|
||||
struct statinfo stats;
|
||||
int num_devices = 0;
|
||||
u_int64_t total_transfers;
|
||||
@ -400,9 +400,10 @@ updatexfers(int numdevs, int *devs)
|
||||
& DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT)
|
||||
&& ((stats.dinfo->devices[i].device_type
|
||||
& DEVSTAT_TYPE_PASS) == 0)) {
|
||||
total_transfers = stats.dinfo->devices[i].num_reads +
|
||||
stats.dinfo->devices[i].num_writes +
|
||||
stats.dinfo->devices[i].num_other;
|
||||
total_transfers = 0;
|
||||
for (k = 0; k < DEVSTAT_N_TRANS_FLAGS; k++)
|
||||
total_transfers +=
|
||||
stats.dinfo->devices[i].operations[k];
|
||||
/*
|
||||
* XXX KDM If the total transfers for this device
|
||||
* are greater than the amount we can fit in a
|
||||
|
@ -866,7 +866,7 @@ cam_periph_runccb(union ccb *ccb,
|
||||
* this particular type of ccb, record the transaction start.
|
||||
*/
|
||||
if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO))
|
||||
devstat_start_transaction(ds);
|
||||
devstat_start_transaction(ds, NULL);
|
||||
|
||||
xpt_action(ccb);
|
||||
|
||||
@ -896,7 +896,7 @@ cam_periph_runccb(union ccb *ccb,
|
||||
CAM_DIR_NONE) ? DEVSTAT_NO_DATA :
|
||||
(ccb->ccb_h.flags & CAM_DIR_OUT) ?
|
||||
DEVSTAT_WRITE :
|
||||
DEVSTAT_READ);
|
||||
DEVSTAT_READ, NULL, NULL);
|
||||
|
||||
return(error);
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ devstat_add_entry(struct devstat *ds, const char *dev_name,
|
||||
ds->flags = flags;
|
||||
ds->device_type = device_type;
|
||||
ds->priority = priority;
|
||||
getmicrotime(&ds->dev_creation_time);
|
||||
binuptime(&ds->creation_time);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -183,9 +183,12 @@ devstat_remove_entry(struct devstat *ds)
|
||||
|
||||
/*
|
||||
* Record a transaction start.
|
||||
*
|
||||
* See comments for devstat_end_transaction(). Ordering is very important
|
||||
* here.
|
||||
*/
|
||||
void
|
||||
devstat_start_transaction(struct devstat *ds)
|
||||
devstat_start_transaction(struct devstat *ds, struct bintime *now)
|
||||
{
|
||||
/* sanity check */
|
||||
if (ds == NULL)
|
||||
@ -196,9 +199,21 @@ devstat_start_transaction(struct devstat *ds)
|
||||
* to busy. The start time is really the start of the latest busy
|
||||
* period.
|
||||
*/
|
||||
if (ds->busy_count == 0)
|
||||
getmicrouptime(&ds->start_time);
|
||||
ds->busy_count++;
|
||||
if (ds->start_count == ds->end_count) {
|
||||
if (now != NULL)
|
||||
ds->busy_from = *now;
|
||||
else
|
||||
binuptime(&ds->busy_from);
|
||||
}
|
||||
ds->start_count++;
|
||||
}
|
||||
|
||||
void
|
||||
devstat_start_transaction_bio(struct devstat *ds, struct bio *bp)
|
||||
{
|
||||
|
||||
binuptime(&bp->bio_t0);
|
||||
devstat_start_transaction(ds, &bp->bio_t0);
|
||||
}
|
||||
|
||||
void
|
||||
@ -210,35 +225,35 @@ devstat_start_transaction_bio(struct devstat *ds, struct bio *bp)
|
||||
|
||||
/*
|
||||
* Record the ending of a transaction, and incrment the various counters.
|
||||
*
|
||||
* Ordering in this function, and in devstat_start_transaction() is VERY
|
||||
* important. The idea here is to run without locks, so we are very
|
||||
* careful to only modify some fields on the way "down" (i.e. at
|
||||
* transaction start) and some fields on the way "up" (i.e. at transaction
|
||||
* completion). One exception is busy_from, which we only modify in
|
||||
* devstat_start_transaction() when there are no outstanding transactions,
|
||||
* and thus it can't be modified in devstat_end_transaction()
|
||||
* simultaneously.
|
||||
*/
|
||||
void
|
||||
devstat_end_transaction(struct devstat *ds, u_int32_t bytes,
|
||||
devstat_tag_type tag_type, devstat_trans_flags flags)
|
||||
devstat_tag_type tag_type, devstat_trans_flags flags,
|
||||
struct bintime *now, struct bintime *then)
|
||||
{
|
||||
struct timeval busy_time;
|
||||
struct bintime dt, lnow;
|
||||
|
||||
/* sanity check */
|
||||
if (ds == NULL)
|
||||
return;
|
||||
|
||||
getmicrouptime(&ds->last_comp_time);
|
||||
ds->busy_count--;
|
||||
if (now == NULL) {
|
||||
now = &lnow;
|
||||
binuptime(now);
|
||||
}
|
||||
|
||||
/*
|
||||
* There might be some transactions (DEVSTAT_NO_DATA) that don't
|
||||
* transfer any data.
|
||||
*/
|
||||
if (flags == DEVSTAT_READ) {
|
||||
ds->bytes_read += bytes;
|
||||
ds->num_reads++;
|
||||
} else if (flags == DEVSTAT_WRITE) {
|
||||
ds->bytes_written += bytes;
|
||||
ds->num_writes++;
|
||||
} else if (flags == DEVSTAT_FREE) {
|
||||
ds->bytes_freed += bytes;
|
||||
ds->num_frees++;
|
||||
} else
|
||||
ds->num_other++;
|
||||
/* Update byte and operations counts */
|
||||
ds->bytes[flags] += bytes;
|
||||
ds->operations[flags]++;
|
||||
|
||||
/*
|
||||
* Keep a count of the various tag types sent.
|
||||
@ -247,21 +262,20 @@ devstat_end_transaction(struct devstat *ds, u_int32_t bytes,
|
||||
tag_type != DEVSTAT_TAG_NONE)
|
||||
ds->tag_types[tag_type]++;
|
||||
|
||||
/*
|
||||
* We only update the busy time when we go idle. Otherwise, this
|
||||
* calculation would require many more clock cycles.
|
||||
*/
|
||||
if (ds->busy_count == 0) {
|
||||
/* Calculate how long we were busy */
|
||||
busy_time = ds->last_comp_time;
|
||||
timevalsub(&busy_time, &ds->start_time);
|
||||
if (then != NULL) {
|
||||
/* Update duration of operations */
|
||||
dt = *now;
|
||||
bintime_sub(&dt, then);
|
||||
bintime_add(&ds->duration[flags], &dt);
|
||||
}
|
||||
|
||||
/* Add our busy time to the total busy time. */
|
||||
timevaladd(&ds->busy_time, &busy_time);
|
||||
} else if (ds->busy_count < 0)
|
||||
printf("devstat_end_transaction: HELP!! busy_count "
|
||||
"for %s%d is < 0 (%d)!\n", ds->device_name,
|
||||
ds->unit_number, ds->busy_count);
|
||||
/* Accumulate busy time */
|
||||
dt = *now;
|
||||
bintime_sub(&dt, &ds->busy_from);
|
||||
bintime_add(&ds->busy_time, &dt);
|
||||
ds->busy_from = *now;
|
||||
|
||||
ds->end_count++;
|
||||
}
|
||||
|
||||
void
|
||||
@ -277,7 +291,7 @@ devstat_end_transaction_bio(struct devstat *ds, struct bio *bp)
|
||||
flg = DEVSTAT_WRITE;
|
||||
|
||||
devstat_end_transaction(ds, bp->bio_bcount - bp->bio_resid,
|
||||
DEVSTAT_TAG_SIMPLE, flg);
|
||||
DEVSTAT_TAG_SIMPLE, flg, NULL, &bp->bio_t0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -427,3 +441,6 @@ devstat_free(struct devstat *dsp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SYSCTL_INT(_debug_sizeof, OID_AUTO, devstat, CTLFLAG_RD,
|
||||
0, sizeof(struct devstat), "sizeof(struct devstat)");
|
||||
|
@ -48,7 +48,7 @@
|
||||
* userland utilities to determine whether or not they are in sync with the
|
||||
* kernel.
|
||||
*/
|
||||
#define DEVSTAT_VERSION 5
|
||||
#define DEVSTAT_VERSION 6
|
||||
|
||||
/*
|
||||
* These flags specify which statistics features are supported or not
|
||||
@ -68,6 +68,7 @@ typedef enum {
|
||||
DEVSTAT_WRITE = 0x02,
|
||||
DEVSTAT_FREE = 0x03
|
||||
} devstat_trans_flags;
|
||||
#define DEVSTAT_N_TRANS_FLAGS 4
|
||||
|
||||
typedef enum {
|
||||
DEVSTAT_TAG_SIMPLE = 0x00,
|
||||
@ -124,8 +125,15 @@ typedef enum {
|
||||
} devstat_type_flags;
|
||||
|
||||
struct devstat {
|
||||
int sequence0; /* Update sequence# */
|
||||
/* Internal house-keeping fields */
|
||||
u_int sequence0; /* Update sequence# */
|
||||
int allocated; /* Allocated entry */
|
||||
u_int start_count; /* started ops */
|
||||
u_int end_count; /* completed ops */
|
||||
struct bintime busy_from; /*
|
||||
* busy time unaccounted
|
||||
* for since this time
|
||||
*/
|
||||
STAILQ_ENTRY(devstat) dev_links;
|
||||
u_int32_t device_number; /*
|
||||
* Devstat device
|
||||
@ -133,44 +141,13 @@ struct devstat {
|
||||
*/
|
||||
char device_name[DEVSTAT_NAME_LEN];
|
||||
int unit_number;
|
||||
u_int64_t bytes_read; /*
|
||||
* Total bytes read
|
||||
* from a device.
|
||||
*/
|
||||
u_int64_t bytes_written; /*
|
||||
* Total bytes written
|
||||
* to a device.
|
||||
*/
|
||||
u_int64_t bytes_freed; /*
|
||||
* Total bytes freed
|
||||
* from a device.
|
||||
*/
|
||||
u_int64_t num_reads; /*
|
||||
* Total number of
|
||||
* read requests to
|
||||
* the device.
|
||||
*/
|
||||
u_int64_t num_writes; /*
|
||||
* Total number of
|
||||
* write requests to
|
||||
* the device.
|
||||
*/
|
||||
u_int64_t num_frees; /*
|
||||
* Total number of
|
||||
* free requests to
|
||||
* the device.
|
||||
*/
|
||||
u_int64_t num_other; /*
|
||||
* Total number of
|
||||
* transactions that
|
||||
* don't read or write
|
||||
* data.
|
||||
*/
|
||||
int32_t busy_count; /*
|
||||
* Total number of
|
||||
* transactions
|
||||
* outstanding for
|
||||
* the device.
|
||||
u_int64_t bytes[DEVSTAT_N_TRANS_FLAGS];
|
||||
u_int64_t operations[DEVSTAT_N_TRANS_FLAGS];
|
||||
struct bintime duration[DEVSTAT_N_TRANS_FLAGS];
|
||||
struct bintime busy_time;
|
||||
struct bintime creation_time; /*
|
||||
* Time the device was
|
||||
* created.
|
||||
*/
|
||||
u_int32_t block_size; /* Block size, bytes */
|
||||
u_int64_t tag_types[3]; /*
|
||||
@ -179,28 +156,6 @@ struct devstat {
|
||||
* and head of queue
|
||||
* tags sent.
|
||||
*/
|
||||
struct timeval dev_creation_time; /*
|
||||
* Time the device was
|
||||
* created.
|
||||
*/
|
||||
struct timeval busy_time; /*
|
||||
* Total amount of
|
||||
* time drive has spent
|
||||
* processing requests.
|
||||
*/
|
||||
struct timeval start_time; /*
|
||||
* The time when
|
||||
* busy_count was
|
||||
* last == 0. Or, the
|
||||
* start of the latest
|
||||
* busy period.
|
||||
*/
|
||||
struct timeval last_comp_time; /*
|
||||
* Last time a
|
||||
* transaction was
|
||||
* completed.
|
||||
*/
|
||||
|
||||
devstat_support_flags flags; /*
|
||||
* Which statistics
|
||||
* are supported by a
|
||||
@ -208,8 +163,11 @@ struct devstat {
|
||||
*/
|
||||
devstat_type_flags device_type; /* Device type */
|
||||
devstat_priority priority; /* Controls list pos. */
|
||||
|
||||
int sequence1; /* Update sequence# */
|
||||
void *id; /*
|
||||
* Identification for
|
||||
* GEOM nodes
|
||||
*/
|
||||
u_int sequence1; /* Update sequence# */
|
||||
};
|
||||
|
||||
STAILQ_HEAD(devstatlist, devstat);
|
||||
@ -217,18 +175,19 @@ STAILQ_HEAD(devstatlist, devstat);
|
||||
#ifdef _KERNEL
|
||||
struct bio;
|
||||
|
||||
struct devstat *devstat_new_entry(const char *dev_name,
|
||||
int unit_number, u_int32_t block_size,
|
||||
devstat_support_flags flags,
|
||||
devstat_type_flags device_type,
|
||||
devstat_priority priority);
|
||||
struct devstat *devstat_new_entry(const char *dev_name, int unit_number,
|
||||
u_int32_t block_size,
|
||||
devstat_support_flags flags,
|
||||
devstat_type_flags device_type,
|
||||
devstat_priority priority);
|
||||
|
||||
void devstat_remove_entry(struct devstat *ds);
|
||||
void devstat_start_transaction(struct devstat *ds);
|
||||
void devstat_start_transaction(struct devstat *ds, struct bintime *now);
|
||||
void devstat_start_transaction_bio(struct devstat *ds, struct bio *bp);
|
||||
void devstat_end_transaction(struct devstat *ds, u_int32_t bytes,
|
||||
devstat_tag_type tag_type,
|
||||
devstat_trans_flags flags);
|
||||
devstat_trans_flags flags,
|
||||
struct bintime *now, struct bintime *then);
|
||||
void devstat_end_transaction_bio(struct devstat *ds, struct bio *bp);
|
||||
#endif
|
||||
|
||||
|
@ -55,7 +55,7 @@
|
||||
* doc/en_US.ISO8859-1/books/porters-handbook/book.sgml
|
||||
*/
|
||||
#undef __FreeBSD_version
|
||||
#define __FreeBSD_version 500106 /* Master, propagated to newvers */
|
||||
#define __FreeBSD_version 500107 /* Master, propagated to newvers */
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
|
@ -148,8 +148,6 @@ fetchiostat()
|
||||
last.dinfo = cur.dinfo;
|
||||
cur.dinfo = tmp_dinfo;
|
||||
|
||||
last.busy_time = cur.busy_time;
|
||||
|
||||
/*
|
||||
* Here what we want to do is refresh our device stats.
|
||||
* getdevs() returns 1 when the device list has changed.
|
||||
@ -322,7 +320,7 @@ devstats(row, _col, dn)
|
||||
|
||||
di = dev_select[dn].position;
|
||||
|
||||
busy_seconds = devstat_compute_etime(cur.busy_time, last.busy_time);
|
||||
busy_seconds = cur.snap_time - last.snap_time;
|
||||
|
||||
if (devstat_compute_statistics(&cur.dinfo->devices[di],
|
||||
&last.dinfo->devices[di], busy_seconds,
|
||||
|
@ -798,7 +798,7 @@ getinfo(ls)
|
||||
last.dinfo = cur.dinfo;
|
||||
cur.dinfo = tmp_dinfo;
|
||||
|
||||
last.busy_time = cur.busy_time;
|
||||
last.snap_time = cur.snap_time;
|
||||
switch (devstat_getdevs(NULL, &cur)) {
|
||||
case -1:
|
||||
errx(1, "%s", devstat_errbuf);
|
||||
@ -852,12 +852,19 @@ dinfo(dn, lc, now, then)
|
||||
|
||||
di = dev_select[dn].position;
|
||||
|
||||
elapsed_time = devstat_compute_etime(now->busy_time,
|
||||
then ? then->busy_time : now->dinfo->devices[di].dev_creation_time);
|
||||
|
||||
device_busy = devstat_compute_etime(now->dinfo->devices[di].busy_time,
|
||||
then ? then->dinfo->devices[di].busy_time :
|
||||
now->dinfo->devices[di].dev_creation_time);
|
||||
if (then != NULL) {
|
||||
/* Calculate relative to previous sample */
|
||||
elapsed_time = now->snap_time - then->snap_time;
|
||||
device_busy = devstat_compute_etime(
|
||||
&now->dinfo->devices[di].busy_time,
|
||||
&then->dinfo->devices[di].busy_time);
|
||||
} else {
|
||||
/* Calculate relative to device creation */
|
||||
elapsed_time = now->snap_time - devstat_compute_etime(
|
||||
&now->dinfo->devices[di].creation_time, NULL);
|
||||
device_busy = devstat_compute_etime(
|
||||
&now->dinfo->devices[di].busy_time, NULL);
|
||||
}
|
||||
|
||||
if (devstat_compute_statistics(&now->dinfo->devices[di], then ?
|
||||
&then->dinfo->devices[di] : NULL, elapsed_time,
|
||||
@ -866,11 +873,6 @@ dinfo(dn, lc, now, then)
|
||||
DSM_NONE) != 0)
|
||||
errx(1, "%s", devstat_errbuf);
|
||||
|
||||
if ((device_busy == 0) && (transfers_per_second > 5))
|
||||
/* the device has been 100% busy, fake it because
|
||||
* as long as the device is 100% busy the busy_time
|
||||
* field in the devstat struct is not updated */
|
||||
device_busy = elapsed_time;
|
||||
if (device_busy > elapsed_time)
|
||||
/* this normally happens after one or more periods
|
||||
* where the device has been 100% busy, correct it */
|
||||
|
@ -428,7 +428,7 @@ dovmstat(interval, reps)
|
||||
tmp_dinfo = last.dinfo;
|
||||
last.dinfo = cur.dinfo;
|
||||
cur.dinfo = tmp_dinfo;
|
||||
last.busy_time = cur.busy_time;
|
||||
last.snap_time = cur.snap_time;
|
||||
|
||||
/*
|
||||
* Here what we want to do is refresh our device stats.
|
||||
@ -680,7 +680,7 @@ devstats()
|
||||
last.cp_time[state] = tmp;
|
||||
}
|
||||
|
||||
busy_seconds = devstat_compute_etime(cur.busy_time, last.busy_time);
|
||||
busy_seconds = cur.snap_time - last.snap_time;
|
||||
|
||||
for (dn = 0; dn < num_devices; dn++) {
|
||||
int di;
|
||||
|
@ -407,12 +407,10 @@ main(int argc, char **argv)
|
||||
cur.tk_nin = 0;
|
||||
|
||||
/*
|
||||
* Set the busy time to the system boot time, so the stats are
|
||||
* calculated since system boot.
|
||||
* Set the snap time to the system boot time (ie: zero), so the
|
||||
* stats are calculated since system boot.
|
||||
*/
|
||||
if (readvar(kd, "kern.boottime", X_BOOTTIME, &cur.busy_time,
|
||||
sizeof(cur.busy_time)) != 0)
|
||||
exit(1);
|
||||
cur.snap_time = 0;
|
||||
|
||||
/*
|
||||
* If the user stops the program (control-Z) and then resumes it,
|
||||
@ -452,7 +450,7 @@ main(int argc, char **argv)
|
||||
last.dinfo = cur.dinfo;
|
||||
cur.dinfo = tmp_dinfo;
|
||||
|
||||
last.busy_time = cur.busy_time;
|
||||
last.snap_time = cur.snap_time;
|
||||
|
||||
/*
|
||||
* Here what we want to do is refresh our device stats.
|
||||
@ -538,7 +536,7 @@ main(int argc, char **argv)
|
||||
last.tk_nout = tmp;
|
||||
}
|
||||
|
||||
etime = devstat_compute_etime(cur.busy_time, last.busy_time);
|
||||
etime = cur.snap_time - last.snap_time;
|
||||
|
||||
if (etime == 0.0)
|
||||
etime = 1.0;
|
||||
|
Loading…
Reference in New Issue
Block a user