// Copyright 2006 Google Inc. All Rights Reserved.
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
// you may not use this file except in compliance with the License.
|
// You may obtain a copy of the License at
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
// Unless required by applicable law or agreed to in writing, software
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// See the License for the specific language governing permissions and
|
// limitations under the License.
|
|
// worker.h : worker thread interface
|
|
// This file contains the Worker Thread class interface
|
// for the SAT test. Worker Threads implement a repetative
|
// task used to test or stress the system.
|
|
#ifndef STRESSAPPTEST_WORKER_H_
|
#define STRESSAPPTEST_WORKER_H_
|
|
#include <pthread.h>
|
|
#include <sys/time.h>
|
#include <sys/types.h>
|
|
#ifdef HAVE_LIBAIO_H
|
#include <libaio.h>
|
#endif
|
|
#include <queue>
|
#include <set>
|
#include <string>
|
#include <vector>
|
|
// This file must work with autoconf on its public version,
|
// so these includes are correct.
|
#include "disk_blocks.h"
|
#include "queue.h"
|
#include "sattypes.h"
|
|
|
// Global Datastruture shared by the Cache Coherency Worker Threads.
|
struct cc_cacheline_data {
|
char *num;
|
};
|
|
// Typical usage:
|
// (Other workflows may be possible, see function comments for details.)
|
// - Control thread creates object.
|
// - Control thread calls AddWorkers(1) for each worker thread.
|
// - Control thread calls Initialize().
|
// - Control thread launches worker threads.
|
// - Every worker thread frequently calls ContinueRunning().
|
// - Control thread periodically calls PauseWorkers(), effectively sleeps, and
|
// then calls ResumeWorkers().
|
// - Some worker threads may exit early, before StopWorkers() is called. They
|
// call RemoveSelf() after their last call to ContinueRunning().
|
// - Control thread eventually calls StopWorkers().
|
// - Worker threads exit.
|
// - Control thread joins worker threads.
|
// - Control thread calls Destroy().
|
// - Control thread destroys object.
|
//
|
// Threadsafety:
|
// - ContinueRunning() may be called concurrently by different workers, but not
|
// by a single worker.
|
// - No other methods may ever be called concurrently, with themselves or
|
// eachother.
|
// - This object may be used by multiple threads only between Initialize() and
|
// Destroy().
|
//
|
// TODO(matthewb): Move this class and its unittest to their own files.
|
class WorkerStatus {
|
public:
|
//--------------------------------
|
// Methods for the control thread.
|
//--------------------------------
|
|
WorkerStatus() : num_workers_(0), status_(RUN) {}
|
|
// Called by the control thread to increase the worker count. Must be called
|
// before Initialize(). The worker count is 0 upon object initialization.
|
void AddWorkers(int num_new_workers) {
|
// No need to lock num_workers_mutex_ because this is before Initialize().
|
num_workers_ += num_new_workers;
|
}
|
|
// Called by the control thread. May not be called multiple times. If
|
// called, Destroy() must be called before destruction.
|
void Initialize();
|
|
// Called by the control thread after joining all worker threads. Must be
|
// called iff Initialize() was called. No methods may be called after calling
|
// this.
|
void Destroy();
|
|
// Called by the control thread to tell the workers to pause. Does not return
|
// until all workers have called ContinueRunning() or RemoveSelf(). May only
|
// be called between Initialize() and Stop(). Must not be called multiple
|
// times without ResumeWorkers() having been called inbetween.
|
void PauseWorkers();
|
|
// Called by the control thread to tell the workers to resume from a pause.
|
// May only be called between Initialize() and Stop(). May only be called
|
// directly after PauseWorkers().
|
void ResumeWorkers();
|
|
// Called by the control thread to tell the workers to stop. May only be
|
// called between Initialize() and Destroy(). May only be called once.
|
void StopWorkers();
|
|
//--------------------------------
|
// Methods for the worker threads.
|
//--------------------------------
|
|
// Called by worker threads to decrease the worker count by one. May only be
|
// called between Initialize() and Destroy(). May wait for ResumeWorkers()
|
// when called after PauseWorkers().
|
void RemoveSelf();
|
|
// Called by worker threads between Initialize() and Destroy(). May be called
|
// any number of times. Return value is whether or not the worker should
|
// continue running. When called after PauseWorkers(), does not return until
|
// ResumeWorkers() or StopWorkers() has been called. Number of distinct
|
// calling threads must match the worker count (see AddWorkers() and
|
// RemoveSelf()).
|
bool ContinueRunning(bool *paused);
|
|
// This is a hack! It's like ContinueRunning(), except it won't pause. If
|
// any worker threads use this exclusively in place of ContinueRunning() then
|
// PauseWorkers() should never be used!
|
bool ContinueRunningNoPause();
|
|
private:
|
enum Status { RUN, PAUSE, STOP };
|
|
void WaitOnPauseBarrier() {
|
#ifdef HAVE_PTHREAD_BARRIERS
|
int error = pthread_barrier_wait(&pause_barrier_);
|
if (error != PTHREAD_BARRIER_SERIAL_THREAD)
|
sat_assert(error == 0);
|
#endif
|
}
|
|
void AcquireNumWorkersLock() {
|
sat_assert(0 == pthread_mutex_lock(&num_workers_mutex_));
|
}
|
|
void ReleaseNumWorkersLock() {
|
sat_assert(0 == pthread_mutex_unlock(&num_workers_mutex_));
|
}
|
|
void AcquireStatusReadLock() {
|
sat_assert(0 == pthread_rwlock_rdlock(&status_rwlock_));
|
}
|
|
void AcquireStatusWriteLock() {
|
sat_assert(0 == pthread_rwlock_wrlock(&status_rwlock_));
|
}
|
|
void ReleaseStatusLock() {
|
sat_assert(0 == pthread_rwlock_unlock(&status_rwlock_));
|
}
|
|
Status GetStatus() {
|
AcquireStatusReadLock();
|
Status status = status_;
|
ReleaseStatusLock();
|
return status;
|
}
|
|
// Returns the previous status.
|
Status SetStatus(Status status) {
|
AcquireStatusWriteLock();
|
Status prev_status = status_;
|
status_ = status;
|
ReleaseStatusLock();
|
return prev_status;
|
}
|
|
pthread_mutex_t num_workers_mutex_;
|
int num_workers_;
|
|
pthread_rwlock_t status_rwlock_;
|
Status status_;
|
|
#ifdef HAVE_PTHREAD_BARRIERS
|
// Guaranteed to not be in use when (status_ != PAUSE).
|
pthread_barrier_t pause_barrier_;
|
#endif
|
|
DISALLOW_COPY_AND_ASSIGN(WorkerStatus);
|
};
|
|
|
// This is a base class for worker threads.
|
// Each thread repeats a specific
|
// task on various blocks of memory.
|
class WorkerThread {
|
public:
|
// Enum to mark a thread as low/med/high priority.
|
enum Priority {
|
Low,
|
Normal,
|
High,
|
};
|
WorkerThread();
|
virtual ~WorkerThread();
|
|
// Initialize values and thread ID number.
|
virtual void InitThread(int thread_num_init,
|
class Sat *sat_init,
|
class OsLayer *os_init,
|
class PatternList *patternlist_init,
|
WorkerStatus *worker_status);
|
|
// This function is DEPRECATED, it does nothing.
|
void SetPriority(Priority priority) { priority_ = priority; }
|
// Spawn the worker thread, by running Work().
|
int SpawnThread();
|
// Only for ThreadSpawnerGeneric().
|
void StartRoutine();
|
bool InitPriority();
|
|
// Wait for the thread to complete its cleanup.
|
virtual bool JoinThread();
|
// Kill worker thread with SIGINT.
|
virtual bool KillThread();
|
|
// This is the task function that the thread executes.
|
// This is implemented per subclass.
|
virtual bool Work();
|
|
// Starts per-WorkerThread timer.
|
void StartThreadTimer() {gettimeofday(&start_time_, NULL);}
|
// Reads current timer value and returns run duration without recording it.
|
int64 ReadThreadTimer() {
|
struct timeval end_time_;
|
gettimeofday(&end_time_, NULL);
|
return (end_time_.tv_sec - start_time_.tv_sec)*1000000ULL +
|
(end_time_.tv_usec - start_time_.tv_usec);
|
}
|
// Stops per-WorkerThread timer and records thread run duration.
|
// Start/Stop ThreadTimer repetitively has cumulative effect, ie the timer
|
// is effectively paused and restarted, so runduration_usec accumulates on.
|
void StopThreadTimer() {
|
runduration_usec_ += ReadThreadTimer();
|
}
|
|
// Acccess member variables.
|
bool GetStatus() {return status_;}
|
int64 GetErrorCount() {return errorcount_;}
|
int64 GetPageCount() {return pages_copied_;}
|
int64 GetRunDurationUSec() {return runduration_usec_;}
|
|
// Returns bandwidth defined as pages_copied / thread_run_durations.
|
virtual float GetCopiedData();
|
// Calculate worker thread specific copied data.
|
virtual float GetMemoryCopiedData() {return 0;}
|
virtual float GetDeviceCopiedData() {return 0;}
|
// Calculate worker thread specific bandwidth.
|
virtual float GetMemoryBandwidth()
|
{return GetMemoryCopiedData() / (
|
runduration_usec_ * 1.0 / 1000000.);}
|
virtual float GetDeviceBandwidth()
|
{return GetDeviceCopiedData() / (
|
runduration_usec_ * 1.0 / 1000000.);}
|
|
void set_cpu_mask(cpu_set_t *mask) {
|
memcpy(&cpu_mask_, mask, sizeof(*mask));
|
}
|
|
void set_cpu_mask_to_cpu(int cpu_num) {
|
cpuset_set_ab(&cpu_mask_, cpu_num, cpu_num + 1);
|
}
|
|
void set_tag(int32 tag) {tag_ = tag;}
|
|
// Returns CPU mask, where each bit represents a logical cpu.
|
bool AvailableCpus(cpu_set_t *cpuset);
|
// Returns CPU mask of CPUs this thread is bound to,
|
bool CurrentCpus(cpu_set_t *cpuset);
|
// Returns Current Cpus mask as string.
|
string CurrentCpusFormat() {
|
cpu_set_t current_cpus;
|
CurrentCpus(¤t_cpus);
|
return cpuset_format(¤t_cpus);
|
}
|
|
int ThreadID() {return thread_num_;}
|
|
// Bind worker thread to specified CPU(s)
|
bool BindToCpus(const cpu_set_t *cpuset);
|
|
protected:
|
// This function dictates whether the main work loop
|
// continues, waits, or terminates.
|
// All work loops should be of the form:
|
// do {
|
// // work.
|
// } while (IsReadyToRun());
|
virtual bool IsReadyToRun(bool *paused = NULL) {
|
return worker_status_->ContinueRunning(paused);
|
}
|
|
// Like IsReadyToRun(), except it won't pause.
|
virtual bool IsReadyToRunNoPause() {
|
return worker_status_->ContinueRunningNoPause();
|
}
|
|
// These are functions used by the various work loops.
|
// Pretty print and log a data miscompare.
|
virtual void ProcessError(struct ErrorRecord *er,
|
int priority,
|
const char *message);
|
|
// Compare a region of memory with a known data patter, and report errors.
|
virtual int CheckRegion(void *addr,
|
class Pattern *pat,
|
int64 length,
|
int offset,
|
int64 patternoffset);
|
|
// Fast compare a block of memory.
|
virtual int CrcCheckPage(struct page_entry *srcpe);
|
|
// Fast copy a block of memory, while verifying correctness.
|
virtual int CrcCopyPage(struct page_entry *dstpe,
|
struct page_entry *srcpe);
|
|
// Fast copy a block of memory, while verifying correctness, and heating CPU.
|
virtual int CrcWarmCopyPage(struct page_entry *dstpe,
|
struct page_entry *srcpe);
|
|
// Fill a page with its specified pattern.
|
virtual bool FillPage(struct page_entry *pe);
|
|
// Copy with address tagging.
|
virtual bool AdlerAddrMemcpyC(uint64 *dstmem64,
|
uint64 *srcmem64,
|
unsigned int size_in_bytes,
|
AdlerChecksum *checksum,
|
struct page_entry *pe);
|
// SSE copy with address tagging.
|
virtual bool AdlerAddrMemcpyWarm(uint64 *dstmem64,
|
uint64 *srcmem64,
|
unsigned int size_in_bytes,
|
AdlerChecksum *checksum,
|
struct page_entry *pe);
|
// Crc data with address tagging.
|
virtual bool AdlerAddrCrcC(uint64 *srcmem64,
|
unsigned int size_in_bytes,
|
AdlerChecksum *checksum,
|
struct page_entry *pe);
|
// Setup tagging on an existing page.
|
virtual bool TagAddrC(uint64 *memwords,
|
unsigned int size_in_bytes);
|
// Report a mistagged cacheline.
|
virtual bool ReportTagError(uint64 *mem64,
|
uint64 actual,
|
uint64 tag);
|
// Print out the error record of the tag mismatch.
|
virtual void ProcessTagError(struct ErrorRecord *error,
|
int priority,
|
const char *message);
|
|
// A worker thread can yield itself to give up CPU until it's scheduled again
|
bool YieldSelf();
|
|
protected:
|
// General state variables that all subclasses need.
|
int thread_num_; // Thread ID.
|
volatile bool status_; // Error status.
|
volatile int64 pages_copied_; // Recorded for memory bandwidth calc.
|
volatile int64 errorcount_; // Miscompares seen by this thread.
|
|
cpu_set_t cpu_mask_; // Cores this thread is allowed to run on.
|
volatile uint32 tag_; // Tag hint for memory this thread can use.
|
|
bool tag_mode_; // Tag cachelines with vaddr.
|
|
// Thread timing variables.
|
struct timeval start_time_; // Worker thread start time.
|
volatile int64 runduration_usec_; // Worker run duration in u-seconds.
|
|
// Function passed to pthread_create.
|
void *(*thread_spawner_)(void *args);
|
pthread_t thread_; // Pthread thread ID.
|
Priority priority_; // Worker thread priority.
|
class Sat *sat_; // Reference to parent stest object.
|
class OsLayer *os_; // Os abstraction: put hacks here.
|
class PatternList *patternlist_; // Reference to data patterns.
|
|
// Work around style guide ban on sizeof(int).
|
static const uint64 iamint_ = 0;
|
static const int wordsize_ = sizeof(iamint_);
|
|
private:
|
WorkerStatus *worker_status_;
|
|
DISALLOW_COPY_AND_ASSIGN(WorkerThread);
|
};
|
|
// Worker thread to perform File IO.
|
class FileThread : public WorkerThread {
|
public:
|
FileThread();
|
// Set filename to use for file IO.
|
virtual void SetFile(const char *filename_init);
|
virtual bool Work();
|
|
// Calculate worker thread specific bandwidth.
|
virtual float GetDeviceCopiedData()
|
{return GetCopiedData()*2;}
|
virtual float GetMemoryCopiedData();
|
|
protected:
|
// Record of where these pages were sourced from, and what
|
// potentially broken components they passed through.
|
struct PageRec {
|
class Pattern *pattern; // This is the data it should contain.
|
void *src; // This is the memory location the data was sourced from.
|
void *dst; // This is where it ended up.
|
};
|
|
// These are functions used by the various work loops.
|
// Pretty print and log a data miscompare. Disks require
|
// slightly different error handling.
|
virtual void ProcessError(struct ErrorRecord *er,
|
int priority,
|
const char *message);
|
|
virtual bool OpenFile(int *pfile);
|
virtual bool CloseFile(int fd);
|
|
// Read and write whole file to disk.
|
virtual bool WritePages(int fd);
|
virtual bool ReadPages(int fd);
|
|
// Read and write pages to disk.
|
virtual bool WritePageToFile(int fd, struct page_entry *src);
|
virtual bool ReadPageFromFile(int fd, struct page_entry *dst);
|
|
// Sector tagging support.
|
virtual bool SectorTagPage(struct page_entry *src, int block);
|
virtual bool SectorValidatePage(const struct PageRec &page,
|
struct page_entry *dst,
|
int block);
|
|
// Get memory for an incoming data transfer..
|
virtual bool PagePrepare();
|
// Remove memory allocated for data transfer.
|
virtual bool PageTeardown();
|
|
// Get memory for an incoming data transfer..
|
virtual bool GetEmptyPage(struct page_entry *dst);
|
// Get memory for an outgoing data transfer..
|
virtual bool GetValidPage(struct page_entry *dst);
|
// Throw out a used empty page.
|
virtual bool PutEmptyPage(struct page_entry *src);
|
// Throw out a used, filled page.
|
virtual bool PutValidPage(struct page_entry *src);
|
|
|
struct PageRec *page_recs_; // Array of page records.
|
int crc_page_; // Page currently being CRC checked.
|
string filename_; // Name of file to access.
|
string devicename_; // Name of device file is on.
|
|
bool page_io_; // Use page pool for IO.
|
void *local_page_; // malloc'd page fon non-pool IO.
|
int pass_; // Number of writes to the file so far.
|
|
// Tag to detect file corruption.
|
struct SectorTag {
|
volatile uint8 magic;
|
volatile uint8 block;
|
volatile uint8 sector;
|
volatile uint8 pass;
|
char pad[512-4];
|
};
|
|
DISALLOW_COPY_AND_ASSIGN(FileThread);
|
};
|
|
|
// Worker thread to perform Network IO.
|
class NetworkThread : public WorkerThread {
|
public:
|
NetworkThread();
|
// Set hostname to use for net IO.
|
virtual void SetIP(const char *ipaddr_init);
|
virtual bool Work();
|
|
// Calculate worker thread specific bandwidth.
|
virtual float GetDeviceCopiedData()
|
{return GetCopiedData()*2;}
|
|
protected:
|
// IsReadyToRunNoPause() wrapper, for NetworkSlaveThread to override.
|
virtual bool IsNetworkStopSet();
|
virtual bool CreateSocket(int *psocket);
|
virtual bool CloseSocket(int sock);
|
virtual bool Connect(int sock);
|
virtual bool SendPage(int sock, struct page_entry *src);
|
virtual bool ReceivePage(int sock, struct page_entry *dst);
|
char ipaddr_[256];
|
int sock_;
|
|
private:
|
DISALLOW_COPY_AND_ASSIGN(NetworkThread);
|
};
|
|
// Worker thread to reflect Network IO.
|
class NetworkSlaveThread : public NetworkThread {
|
public:
|
NetworkSlaveThread();
|
// Set socket for IO.
|
virtual void SetSock(int sock);
|
virtual bool Work();
|
|
protected:
|
virtual bool IsNetworkStopSet();
|
|
private:
|
DISALLOW_COPY_AND_ASSIGN(NetworkSlaveThread);
|
};
|
|
// Worker thread to detect incoming Network IO.
|
class NetworkListenThread : public NetworkThread {
|
public:
|
NetworkListenThread();
|
virtual bool Work();
|
|
private:
|
virtual bool Listen();
|
virtual bool Wait();
|
virtual bool GetConnection(int *pnewsock);
|
virtual bool SpawnSlave(int newsock, int threadid);
|
virtual bool ReapSlaves();
|
|
// For serviced incoming connections.
|
struct ChildWorker {
|
WorkerStatus status;
|
NetworkSlaveThread thread;
|
};
|
typedef vector<ChildWorker*> ChildVector;
|
ChildVector child_workers_;
|
|
DISALLOW_COPY_AND_ASSIGN(NetworkListenThread);
|
};
|
|
// Worker thread to perform Memory Copy.
|
class CopyThread : public WorkerThread {
|
public:
|
CopyThread() {}
|
virtual bool Work();
|
// Calculate worker thread specific bandwidth.
|
virtual float GetMemoryCopiedData()
|
{return GetCopiedData()*2;}
|
|
private:
|
DISALLOW_COPY_AND_ASSIGN(CopyThread);
|
};
|
|
// Worker thread to perform Memory Invert.
|
class InvertThread : public WorkerThread {
|
public:
|
InvertThread() {}
|
virtual bool Work();
|
// Calculate worker thread specific bandwidth.
|
virtual float GetMemoryCopiedData()
|
{return GetCopiedData()*4;}
|
|
private:
|
virtual int InvertPageUp(struct page_entry *srcpe);
|
virtual int InvertPageDown(struct page_entry *srcpe);
|
DISALLOW_COPY_AND_ASSIGN(InvertThread);
|
};
|
|
// Worker thread to fill blank pages on startup.
|
class FillThread : public WorkerThread {
|
public:
|
FillThread();
|
// Set how many pages this thread should fill before exiting.
|
virtual void SetFillPages(int64 num_pages_to_fill_init);
|
virtual bool Work();
|
|
private:
|
// Fill a page with the data pattern in pe->pattern.
|
virtual bool FillPageRandom(struct page_entry *pe);
|
int64 num_pages_to_fill_;
|
DISALLOW_COPY_AND_ASSIGN(FillThread);
|
};
|
|
// Worker thread to verify page data matches pattern data.
|
// Thread will check and replace pages until "done" flag is set,
|
// then it will check and discard pages until no more remain.
|
class CheckThread : public WorkerThread {
|
public:
|
CheckThread() {}
|
virtual bool Work();
|
// Calculate worker thread specific bandwidth.
|
virtual float GetMemoryCopiedData()
|
{return GetCopiedData();}
|
|
private:
|
DISALLOW_COPY_AND_ASSIGN(CheckThread);
|
};
|
|
|
// Worker thread to poll for system error messages.
|
// Thread will check for messages until "done" flag is set.
|
class ErrorPollThread : public WorkerThread {
|
public:
|
ErrorPollThread() {}
|
virtual bool Work();
|
|
private:
|
DISALLOW_COPY_AND_ASSIGN(ErrorPollThread);
|
};
|
|
// Computation intensive worker thread to stress CPU.
|
class CpuStressThread : public WorkerThread {
|
public:
|
CpuStressThread() {}
|
virtual bool Work();
|
|
private:
|
DISALLOW_COPY_AND_ASSIGN(CpuStressThread);
|
};
|
|
// Worker thread that tests the correctness of the
|
// CPU Cache Coherency Protocol.
|
class CpuCacheCoherencyThread : public WorkerThread {
|
public:
|
CpuCacheCoherencyThread(cc_cacheline_data *cc_data,
|
int cc_cacheline_count_,
|
int cc_thread_num_,
|
int cc_thread_count_,
|
int cc_inc_count_);
|
virtual bool Work();
|
|
protected:
|
// Used by the simple random number generator as a shift feedback;
|
// this polynomial (x^64 + x^63 + x^61 + x^60 + 1) will produce a
|
// psuedorandom cycle of period 2^64-1.
|
static const uint64 kRandomPolynomial = 0xD800000000000000ULL;
|
// A very simple psuedorandom generator that can be inlined and use
|
// registers, to keep the CC test loop tight and focused.
|
static uint64 SimpleRandom(uint64 seed);
|
|
cc_cacheline_data *cc_cacheline_data_; // Datstructure for each cacheline.
|
int cc_local_num_; // Local counter for each thread.
|
int cc_cacheline_count_; // Number of cache lines to operate on.
|
int cc_thread_num_; // The integer id of the thread which is
|
// used as an index into the integer array
|
// of the cacheline datastructure.
|
int cc_thread_count_; // Total number of threads being run, for
|
// calculations mixing up cache line access.
|
int cc_inc_count_; // Number of times to increment the counter.
|
|
private:
|
DISALLOW_COPY_AND_ASSIGN(CpuCacheCoherencyThread);
|
};
|
|
// Worker thread to perform disk test.
|
class DiskThread : public WorkerThread {
|
public:
|
explicit DiskThread(DiskBlockTable *block_table);
|
virtual ~DiskThread();
|
// Calculate disk thread specific bandwidth.
|
virtual float GetDeviceCopiedData() {
|
return (blocks_written_ * write_block_size_ +
|
blocks_read_ * read_block_size_) / kMegabyte;}
|
|
// Set filename for device file (in /dev).
|
virtual void SetDevice(const char *device_name);
|
// Set various parameters that control the behaviour of the test.
|
virtual bool SetParameters(int read_block_size,
|
int write_block_size,
|
int64 segment_size,
|
int64 cache_size,
|
int blocks_per_segment,
|
int64 read_threshold,
|
int64 write_threshold,
|
int non_destructive);
|
|
virtual bool Work();
|
|
virtual float GetMemoryCopiedData() {return 0;}
|
|
protected:
|
static const int kSectorSize = 512; // Size of sector on disk.
|
static const int kBufferAlignment = 512; // Buffer alignment required by the
|
// kernel.
|
static const int kBlockRetry = 100; // Number of retries to allocate
|
// sectors.
|
|
enum IoOp {
|
ASYNC_IO_READ = 0,
|
ASYNC_IO_WRITE = 1
|
};
|
|
virtual bool OpenDevice(int *pfile);
|
virtual bool CloseDevice(int fd);
|
|
// Retrieves the size (in bytes) of the disk/file.
|
virtual bool GetDiskSize(int fd);
|
|
// Retrieves the current time in microseconds.
|
virtual int64 GetTime();
|
|
// Do an asynchronous disk I/O operation.
|
virtual bool AsyncDiskIO(IoOp op, int fd, void *buf, int64 size,
|
int64 offset, int64 timeout);
|
|
// Write a block to disk.
|
virtual bool WriteBlockToDisk(int fd, BlockData *block);
|
|
// Verify a block on disk.
|
virtual bool ValidateBlockOnDisk(int fd, BlockData *block);
|
|
// Main work loop.
|
virtual bool DoWork(int fd);
|
|
int read_block_size_; // Size of blocks read from disk, in bytes.
|
int write_block_size_; // Size of blocks written to disk, in bytes.
|
int64 blocks_read_; // Number of blocks read in work loop.
|
int64 blocks_written_; // Number of blocks written in work loop.
|
int64 segment_size_; // Size of disk segments (in bytes) that the disk
|
// will be split into where testing can be
|
// confined to a particular segment.
|
// Allows for control of how evenly the disk will
|
// be tested. Smaller segments imply more even
|
// testing (less random).
|
int blocks_per_segment_; // Number of blocks that will be tested per
|
// segment.
|
int cache_size_; // Size of disk cache, in bytes.
|
int queue_size_; // Length of in-flight-blocks queue, in blocks.
|
int non_destructive_; // Use non-destructive mode or not.
|
int update_block_table_; // If true, assume this is the thread
|
// responsible for writing the data in the disk
|
// for this block device and, therefore,
|
// update the block table. If false, just use
|
// the block table to get data.
|
|
// read/write times threshold for reporting a problem
|
int64 read_threshold_; // Maximum time a read should take (in us) before
|
// a warning is given.
|
int64 write_threshold_; // Maximum time a write should take (in us) before
|
// a warning is given.
|
int64 read_timeout_; // Maximum time a read can take before a timeout
|
// and the aborting of the read operation.
|
int64 write_timeout_; // Maximum time a write can take before a timeout
|
// and the aborting of the write operation.
|
|
string device_name_; // Name of device file to access.
|
int64 device_sectors_; // Number of sectors on the device.
|
|
std::queue<BlockData*> in_flight_sectors_; // Queue of sectors written but
|
// not verified.
|
void *block_buffer_; // Pointer to aligned block buffer.
|
|
#ifdef HAVE_LIBAIO_H
|
io_context_t aio_ctx_; // Asynchronous I/O context for Linux native AIO.
|
#endif
|
|
DiskBlockTable *block_table_; // Disk Block Table, shared by all disk
|
// threads that read / write at the same
|
// device
|
|
DISALLOW_COPY_AND_ASSIGN(DiskThread);
|
};
|
|
class RandomDiskThread : public DiskThread {
|
public:
|
explicit RandomDiskThread(DiskBlockTable *block_table);
|
virtual ~RandomDiskThread();
|
// Main work loop.
|
virtual bool DoWork(int fd);
|
protected:
|
DISALLOW_COPY_AND_ASSIGN(RandomDiskThread);
|
};
|
|
// Worker thread to perform checks in a specific memory region.
|
class MemoryRegionThread : public WorkerThread {
|
public:
|
MemoryRegionThread();
|
~MemoryRegionThread();
|
virtual bool Work();
|
void ProcessError(struct ErrorRecord *error, int priority,
|
const char *message);
|
bool SetRegion(void *region, int64 size);
|
// Calculate worker thread specific bandwidth.
|
virtual float GetMemoryCopiedData()
|
{return GetCopiedData();}
|
virtual float GetDeviceCopiedData()
|
{return GetCopiedData() * 2;}
|
void SetIdentifier(string identifier) {
|
identifier_ = identifier;
|
}
|
|
protected:
|
// Page queue for this particular memory region.
|
char *region_;
|
PageEntryQueue *pages_;
|
bool error_injection_;
|
int phase_;
|
string identifier_;
|
static const int kPhaseNoPhase = 0;
|
static const int kPhaseCopy = 1;
|
static const int kPhaseCheck = 2;
|
|
private:
|
DISALLOW_COPY_AND_ASSIGN(MemoryRegionThread);
|
};
|
|
// Worker thread to check that the frequency of every cpu does not go below a
|
// certain threshold.
|
class CpuFreqThread : public WorkerThread {
|
public:
|
CpuFreqThread(int num_cpus, int freq_threshold, int round);
|
~CpuFreqThread();
|
|
// This is the task function that the thread executes.
|
virtual bool Work();
|
|
// Returns true if this test can run on the current machine. Otherwise,
|
// returns false.
|
static bool CanRun();
|
|
private:
|
static const int kIntervalPause = 10; // The number of seconds to pause
|
// between acquiring the MSR data.
|
static const int kStartupDelay = 5; // The number of seconds to wait
|
// before acquiring MSR data.
|
static const int kMsrTscAddr = 0x10; // The address of the TSC MSR.
|
static const int kMsrAperfAddr = 0xE8; // The address of the APERF MSR.
|
static const int kMsrMperfAddr = 0xE7; // The address of the MPERF MSR.
|
|
// The index values into the CpuDataType.msr[] array.
|
enum MsrValues {
|
kMsrTsc = 0, // MSR index 0 = TSC.
|
kMsrAperf = 1, // MSR index 1 = APERF.
|
kMsrMperf = 2, // MSR index 2 = MPERF.
|
kMsrLast, // Last MSR index.
|
};
|
|
typedef struct {
|
uint32 msr; // The address of the MSR.
|
const char *name; // A human readable string for the MSR.
|
} CpuRegisterType;
|
|
typedef struct {
|
uint64 msrs[kMsrLast]; // The values of the MSRs.
|
struct timeval tv; // The time at which the MSRs were read.
|
} CpuDataType;
|
|
// The set of MSR addresses and register names.
|
static const CpuRegisterType kCpuRegisters[kMsrLast];
|
|
// Compute the change in values of the MSRs between current and previous,
|
// set the frequency in MHz of the cpu. If there is an error computing
|
// the delta, return false. Othewise, return true.
|
bool ComputeFrequency(CpuDataType *current, CpuDataType *previous,
|
int *frequency);
|
|
// Get the MSR values for this particular cpu and save them in data. If
|
// any error is encountered, returns false. Otherwise, returns true.
|
bool GetMsrs(int cpu, CpuDataType *data);
|
|
// Compute the difference between the currently read MSR values and the
|
// previously read values and store the results in delta. If any of the
|
// values did not increase, or the TSC value is too small, returns false.
|
// Otherwise, returns true.
|
bool ComputeDelta(CpuDataType *current, CpuDataType *previous,
|
CpuDataType *delta);
|
|
// The total number of cpus on the system.
|
int num_cpus_;
|
|
// The minimum frequency that each cpu must operate at (in MHz).
|
int freq_threshold_;
|
|
// The value to round the computed frequency to.
|
int round_;
|
|
// Precomputed value to add to the frequency to do the rounding.
|
double round_value_;
|
|
DISALLOW_COPY_AND_ASSIGN(CpuFreqThread);
|
};
|
|
#endif // STRESSAPPTEST_WORKER_H_
|