mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1362 lines
48 KiB
1362 lines
48 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
partctrl.cpp
|
|
|
|
Abstract:
|
|
|
|
SIS Groveler partition controller
|
|
|
|
Authors:
|
|
|
|
John Douceur, 1998
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "all.hxx"
|
|
|
|
PartitionController::PartitionController(
|
|
Groveler *groveler,
|
|
GrovelStatus groveler_status,
|
|
int target_entries_per_extraction,
|
|
int max_extraction_interval,
|
|
int base_grovel_interval,
|
|
int max_grovel_interval,
|
|
int low_confidence_grovel_interval,
|
|
int low_disk_space_grovel_interval,
|
|
int partition_info_update_interval,
|
|
int base_restart_extraction_interval,
|
|
int max_restart_extraction_interval,
|
|
int base_restart_groveling_interval,
|
|
int max_restart_groveling_interval,
|
|
int base_regrovel_interval,
|
|
int max_regrovel_interval,
|
|
int volscan_regrovel_threshold,
|
|
int partition_balance_time_constant,
|
|
int read_time_increase_history_size,
|
|
int read_time_decrease_history_size,
|
|
int file_size_history_size,
|
|
bool error_retry_log_extraction,
|
|
bool error_retry_groveling,
|
|
__int64 base_usn_log_size,
|
|
__int64 max_usn_log_size,
|
|
int sample_group_size,
|
|
double acceptance_p_value,
|
|
double rejection_p_value,
|
|
double base_use_multiplier,
|
|
double max_use_multiplier,
|
|
double peak_finder_accuracy,
|
|
double peak_finder_range,
|
|
double base_cpu_load_threshold,
|
|
double max_cpu_load_threshold,
|
|
double *hash_match_ratio,
|
|
double *compare_match_ratio,
|
|
double *dequeue_hash_ratio,
|
|
double *hash_read_time_estimate,
|
|
double *compare_read_time_estimate,
|
|
double *mean_file_size,
|
|
double *read_time_confidence,
|
|
int *volume_serial_number,
|
|
int partition_index,
|
|
double read_report_discard_threshold,
|
|
int min_file_size,
|
|
int min_file_age,
|
|
bool allow_compressed_files,
|
|
bool allow_encrypted_files,
|
|
bool allow_hidden_files,
|
|
bool allow_offline_files,
|
|
bool allow_temporary_files,
|
|
int num_excluded_paths,
|
|
const _TCHAR **excluded_paths)
|
|
: read_mean_comparator(2, sample_group_size,
|
|
acceptance_p_value, rejection_p_value, peak_finder_accuracy),
|
|
file_size_filter(file_size_history_size, *mean_file_size),
|
|
read_time_confidence_estimator(2, *read_time_confidence),
|
|
partition_grovel_accumulator(partition_balance_time_constant)
|
|
{
|
|
ASSERT(this != 0);
|
|
unsigned int current_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 1, (_T("time: %d\n"), current_time));
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPC -\tconstructing PartitionController for drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
ASSERT(groveler != 0);
|
|
this->groveler = groveler;
|
|
ASSERT(target_entries_per_extraction > 0);
|
|
this->target_entries_per_extraction = target_entries_per_extraction;
|
|
ASSERT(max_extraction_interval > 0);
|
|
this->max_extraction_interval = max_extraction_interval;
|
|
ASSERT(base_grovel_interval > 0);
|
|
this->base_grovel_interval = base_grovel_interval;
|
|
ASSERT(max_grovel_interval > 0);
|
|
ASSERT(max_grovel_interval >= base_grovel_interval);
|
|
this->max_grovel_interval = max_grovel_interval;
|
|
ASSERT(partition_info_update_interval > 0);
|
|
this->partition_info_update_interval = partition_info_update_interval;
|
|
ASSERT(base_restart_extraction_interval > 0);
|
|
this->base_restart_extraction_interval = base_restart_extraction_interval;
|
|
ASSERT(max_restart_extraction_interval > 0);
|
|
ASSERT(max_restart_extraction_interval >= base_restart_extraction_interval);
|
|
this->max_restart_extraction_interval = max_restart_extraction_interval;
|
|
ASSERT(base_restart_groveling_interval > 0);
|
|
this->base_restart_groveling_interval = base_restart_groveling_interval;
|
|
ASSERT(max_restart_groveling_interval > 0);
|
|
ASSERT(max_restart_groveling_interval >= base_restart_groveling_interval);
|
|
this->max_restart_groveling_interval = max_restart_groveling_interval;
|
|
this->error_retry_log_extraction = error_retry_log_extraction;
|
|
this->error_retry_groveling = error_retry_groveling;
|
|
ASSERT(base_usn_log_size > 0);
|
|
this->base_usn_log_size = base_usn_log_size;
|
|
ASSERT(max_usn_log_size > 0);
|
|
ASSERT(max_usn_log_size >= base_usn_log_size);
|
|
this->max_usn_log_size = max_usn_log_size;
|
|
ASSERT(hash_match_ratio != 0);
|
|
ASSERT(*hash_match_ratio >= 0.0);
|
|
ASSERT(*hash_match_ratio <= 1.0);
|
|
this->hash_match_ratio = hash_match_ratio;
|
|
ASSERT(compare_match_ratio != 0);
|
|
ASSERT(*compare_match_ratio >= 0.0);
|
|
ASSERT(*compare_match_ratio <= 1.0);
|
|
this->compare_match_ratio = compare_match_ratio;
|
|
ASSERT(dequeue_hash_ratio != 0);
|
|
ASSERT(*dequeue_hash_ratio >= 0.0);
|
|
ASSERT(*dequeue_hash_ratio <= 1.0);
|
|
this->dequeue_hash_ratio = dequeue_hash_ratio;
|
|
ASSERT(mean_file_size != 0);
|
|
ASSERT(*mean_file_size >= 0.0);
|
|
this->mean_file_size = mean_file_size;
|
|
ASSERT(read_time_confidence != 0);
|
|
ASSERT(*read_time_confidence >= 0.0);
|
|
ASSERT(*read_time_confidence <= 1.0);
|
|
this->read_time_confidence = read_time_confidence;
|
|
ASSERT(base_use_multiplier > 0.0);
|
|
this->base_use_multiplier = base_use_multiplier;
|
|
ASSERT(partition_index >= 0);
|
|
this->partition_index = partition_index;
|
|
ASSERT(base_regrovel_interval > 0);
|
|
this->base_regrovel_interval = base_regrovel_interval;
|
|
ASSERT(max_regrovel_interval >= base_regrovel_interval);
|
|
this->max_regrovel_interval = max_regrovel_interval;
|
|
ASSERT(volscan_regrovel_threshold >= base_regrovel_interval);
|
|
ASSERT(volscan_regrovel_threshold <= max_regrovel_interval);
|
|
this->volscan_regrovel_threshold = volscan_regrovel_threshold;
|
|
ASSERT(read_report_discard_threshold >= 0.0);
|
|
ASSERT(read_report_discard_threshold <= 1.0);
|
|
this->read_report_discard_threshold = read_report_discard_threshold;
|
|
ASSERT(min_file_size >= 0);
|
|
this->min_file_size = min_file_size;
|
|
ASSERT(min_file_age >= 0);
|
|
this->min_file_age = min_file_age;
|
|
this->allow_compressed_files = allow_compressed_files;
|
|
this->allow_encrypted_files = allow_encrypted_files;
|
|
this->allow_hidden_files = allow_hidden_files;
|
|
this->allow_offline_files = allow_offline_files;
|
|
this->allow_temporary_files = allow_temporary_files;
|
|
ASSERT(num_excluded_paths >= 0);
|
|
this->num_excluded_paths = num_excluded_paths;
|
|
ASSERT(excluded_paths != 0);
|
|
this->excluded_paths = excluded_paths;
|
|
ASSERT(peak_finder_accuracy > 0.0);
|
|
ASSERT(peak_finder_accuracy <= 1.0);
|
|
this->peak_finder_accuracy = peak_finder_accuracy;
|
|
ASSERT(peak_finder_range >= 1.0);
|
|
read_peak_finder[RT_hash] =
|
|
new PeakFinder(peak_finder_accuracy, peak_finder_range);
|
|
read_peak_finder[RT_compare] =
|
|
new PeakFinder(peak_finder_accuracy, peak_finder_range);
|
|
ASSERT(base_cpu_load_threshold >= 0.0);
|
|
this->base_cpu_load_threshold = base_cpu_load_threshold;
|
|
ASSERT(read_time_increase_history_size > 0);
|
|
ASSERT(read_time_decrease_history_size > 0);
|
|
ASSERT(hash_read_time_estimate != 0);
|
|
ASSERT(*hash_read_time_estimate >= 0.0);
|
|
read_time_filter[RT_hash] =
|
|
new DirectedIncidentFilter(read_time_increase_history_size,
|
|
read_time_decrease_history_size, *hash_read_time_estimate);
|
|
ASSERT(compare_read_time_estimate != 0);
|
|
ASSERT(*compare_read_time_estimate >= 0.0);
|
|
read_time_filter[RT_compare] =
|
|
new DirectedIncidentFilter(read_time_increase_history_size,
|
|
read_time_decrease_history_size, *compare_read_time_estimate);
|
|
read_time_estimate[RT_hash] = hash_read_time_estimate;
|
|
read_time_estimate[RT_compare] = compare_read_time_estimate;
|
|
log_max_grovel_interval = log(double(max_grovel_interval));
|
|
ASSERT(low_confidence_grovel_interval > 0);
|
|
log_low_confidence_slope =
|
|
log_max_grovel_interval - log(double(low_confidence_grovel_interval));
|
|
if (log_low_confidence_slope < 0.0)
|
|
{
|
|
log_low_confidence_slope = 0.0;
|
|
}
|
|
ASSERT(low_disk_space_grovel_interval > 0);
|
|
low_disk_space_slope = max_grovel_interval - low_disk_space_grovel_interval;
|
|
if (low_disk_space_slope < 0.0)
|
|
{
|
|
low_disk_space_slope = 0.0;
|
|
}
|
|
ASSERT(max_use_multiplier >= base_use_multiplier);
|
|
use_multiplier_slope = max_use_multiplier - base_use_multiplier;
|
|
ASSERT(max_cpu_load_threshold <= 1.0);
|
|
ASSERT(max_cpu_load_threshold >= base_cpu_load_threshold);
|
|
cpu_load_threshold_slope = max_cpu_load_threshold - base_cpu_load_threshold;
|
|
ASSERT(volume_serial_number != 0);
|
|
this->volume_serial_number = volume_serial_number;
|
|
update_partition_info((void *)this);
|
|
ASSERT(volume_total_bytes > 0.0);
|
|
ASSERT(volume_free_bytes >= 0.0);
|
|
current_usn_log_size = base_usn_log_size;
|
|
restart_groveling_interval = base_restart_groveling_interval;
|
|
remaining_grovel_interval = 0;
|
|
restart_volume_scan = false;
|
|
extended_restart_in_progress = false;
|
|
initialize_groveling(groveler_status);
|
|
log_extractor_dead = true;
|
|
restart_extraction_interval = base_restart_extraction_interval;
|
|
remaining_restart_extraction_interval = 0;
|
|
restart_extraction((void *)this);
|
|
}
|
|
|
|
PartitionController::~PartitionController()
|
|
{
|
|
ASSERT(read_peak_finder[RT_hash] != 0);
|
|
delete read_peak_finder[RT_hash];
|
|
read_peak_finder[RT_hash] = 0;
|
|
ASSERT(read_peak_finder[RT_compare] != 0);
|
|
delete read_peak_finder[RT_compare];
|
|
read_peak_finder[RT_hash] = 0;
|
|
ASSERT(read_time_filter[RT_hash] != 0);
|
|
delete read_time_filter[RT_hash];
|
|
read_time_filter[RT_hash] = 0;
|
|
ASSERT(read_time_filter[RT_compare] != 0);
|
|
delete read_time_filter[RT_compare];
|
|
read_time_filter[RT_compare] = 0;
|
|
}
|
|
|
|
bool
|
|
PartitionController::control_operation(
|
|
DWORD grovel_duration,
|
|
DWORD *count_of_files_hashed,
|
|
DWORDLONG *bytes_of_files_hashed,
|
|
DWORD *count_of_files_matching,
|
|
DWORDLONG *bytes_of_files_matching,
|
|
DWORD *count_of_files_compared,
|
|
DWORDLONG *bytes_of_files_compared,
|
|
DWORD *count_of_files_merged,
|
|
DWORDLONG *bytes_of_files_merged,
|
|
DWORD *count_of_files_enqueued,
|
|
DWORD *count_of_files_dequeued,
|
|
double cpu_load)
|
|
{
|
|
ASSERT(this != 0);
|
|
ASSERT(grovel_duration > 0);
|
|
ASSERT(count_of_files_hashed != 0);
|
|
ASSERT(bytes_of_files_hashed != 0);
|
|
ASSERT(count_of_files_matching != 0);
|
|
ASSERT(bytes_of_files_matching != 0);
|
|
ASSERT(count_of_files_compared != 0);
|
|
ASSERT(bytes_of_files_compared != 0);
|
|
ASSERT(count_of_files_merged != 0);
|
|
ASSERT(bytes_of_files_merged != 0);
|
|
ASSERT(count_of_files_enqueued != 0);
|
|
ASSERT(count_of_files_dequeued != 0);
|
|
ASSERT(cpu_load >= 0.0);
|
|
ASSERT(cpu_load <= 1.0);
|
|
ASSERT(!groveler_dead);
|
|
unsigned int current_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 3, (_T("time: %d\n"), current_time));
|
|
TRACE_PRINTF(TC_partctrl, 3, (_T("\tPCco -\toperating on drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
int files_to_compare = groveler->count_of_files_to_compare();
|
|
ASSERT(files_to_compare >= 0);
|
|
int files_in_queue = groveler->count_of_files_in_queue();
|
|
ASSERT(files_in_queue >= 0);
|
|
int ready_time = groveler->time_to_first_file_ready();
|
|
ASSERT(files_in_queue == 0 || ready_time >= 0);
|
|
bool more_work_to_do = files_to_compare > 0 ||
|
|
files_in_queue > 0 && ready_time < volscan_regrovel_threshold;
|
|
if (log_extractor_dead && !performing_full_volume_scan && !more_work_to_do)
|
|
{
|
|
initiate_full_volume_scan = true;
|
|
}
|
|
partition_grovel_accumulator.increment();
|
|
ASSERT(groveler != 0);
|
|
bool ok;
|
|
if (initiate_full_volume_scan ||
|
|
performing_full_volume_scan && !more_work_to_do)
|
|
{
|
|
if (initiate_full_volume_scan)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCco -\tinitiating full volume scan\n")));
|
|
initiate_full_volume_scan = false;
|
|
performing_full_volume_scan = true;
|
|
restart_volume_scan = true;
|
|
}
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCco -\tperforming full volume scan\n")));
|
|
ok = control_volume_scan(grovel_duration, count_of_files_enqueued);
|
|
*count_of_files_hashed = 0;
|
|
*bytes_of_files_hashed = 0;
|
|
*count_of_files_matching = 0;
|
|
*bytes_of_files_matching = 0;
|
|
*count_of_files_compared = 0;
|
|
*bytes_of_files_compared = 0;
|
|
*count_of_files_merged = 0;
|
|
*bytes_of_files_merged = 0;
|
|
*count_of_files_dequeued = 0;
|
|
}
|
|
else
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("\tPCco -\tgroveling\n")));
|
|
ok = control_groveling(grovel_duration,
|
|
count_of_files_hashed, bytes_of_files_hashed,
|
|
count_of_files_matching, bytes_of_files_matching,
|
|
count_of_files_compared, bytes_of_files_compared,
|
|
count_of_files_merged, bytes_of_files_merged,
|
|
count_of_files_enqueued, count_of_files_dequeued,
|
|
cpu_load);
|
|
}
|
|
if (groveler_dead)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCco -\tconcluding foreground batch for drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
SERVICE_SET_FOREGROUND_BATCH_IN_PROGRESS(partition_index, false);
|
|
}
|
|
else
|
|
{
|
|
files_to_compare = groveler->count_of_files_to_compare();
|
|
ASSERT(files_to_compare >= 0);
|
|
files_in_queue = groveler->count_of_files_in_queue();
|
|
ASSERT(files_in_queue >= 0);
|
|
ready_time = groveler->time_to_first_file_ready();
|
|
ASSERT(files_in_queue == 0 || ready_time >= 0);
|
|
more_work_to_do = files_to_compare > 0 ||
|
|
files_in_queue > 0 && ready_time < volscan_regrovel_threshold;
|
|
if (!performing_full_volume_scan && !more_work_to_do)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCco -\tconcluding foreground batch for drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
SERVICE_SET_FOREGROUND_BATCH_IN_PROGRESS(partition_index, false);
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
void
|
|
PartitionController::advance(
|
|
int time_delta)
|
|
{
|
|
ASSERT(this != 0);
|
|
ASSERT(time_delta >= 0);
|
|
unsigned int current_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("time: %d\n"), current_time));
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCa -\tadvancing time for drive %s by %d\n"),
|
|
sis_drives.partition_mount_name(partition_index), time_delta));
|
|
if (groveler_dead)
|
|
{
|
|
return;
|
|
}
|
|
ASSERT(remaining_grovel_interval >= 0);
|
|
remaining_grovel_interval -= time_delta;
|
|
if (remaining_grovel_interval < 0)
|
|
{
|
|
remaining_grovel_interval = 0;
|
|
}
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCa -\tremaining grovel interval = %d\n"),
|
|
remaining_grovel_interval));
|
|
}
|
|
|
|
double
|
|
PartitionController::priority() const
|
|
{
|
|
ASSERT(this != 0);
|
|
unsigned int current_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("time: %d\n"), current_time));
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCp -\tcalculating priority on drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
if (groveler_dead)
|
|
{
|
|
return DBL_MAX;
|
|
}
|
|
double accumulated_groveling =
|
|
partition_grovel_accumulator.retrieve_value();
|
|
ASSERT(accumulated_groveling >= 0.0);
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCp -\taccumulated groveling = %f\n"), accumulated_groveling));
|
|
double calculated_priority =
|
|
(1.0 + accumulated_groveling) * (1.0 + volume_free_bytes);
|
|
ASSERT(calculated_priority > 1.0);
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCp -\tcalculated priority = %f\n"), calculated_priority));
|
|
return calculated_priority;
|
|
}
|
|
|
|
int
|
|
PartitionController::wait() const
|
|
{
|
|
ASSERT(this != 0);
|
|
ASSERT(groveler != 0);
|
|
unsigned int current_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("time: %d\n"), current_time));
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCw -\tcalculating wait time for drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
int time_until_groveler_ready = max_grovel_interval;
|
|
if (!groveler_dead)
|
|
{
|
|
int files_to_compare = groveler->count_of_files_to_compare();
|
|
ASSERT(files_to_compare >= 0);
|
|
int files_in_queue = groveler->count_of_files_in_queue();
|
|
ASSERT(files_in_queue >= 0);
|
|
int ready_time = groveler->time_to_first_file_ready();
|
|
ASSERT(files_in_queue == 0 || ready_time >= 0);
|
|
bool more_work_to_do = files_to_compare > 0 ||
|
|
files_in_queue > 0 && ready_time < volscan_regrovel_threshold;
|
|
if (files_to_compare > 0 ||
|
|
initiate_full_volume_scan && !more_work_to_do ||
|
|
performing_full_volume_scan && !more_work_to_do ||
|
|
log_extractor_dead && !more_work_to_do)
|
|
{
|
|
time_until_groveler_ready = 0;
|
|
TRACE_PRINTF(TC_partctrl, 5, (_T("\tPCw -\tgroveler ready now\n")));
|
|
}
|
|
else if (files_in_queue > 0)
|
|
{
|
|
time_until_groveler_ready = ready_time;
|
|
ASSERT(time_until_groveler_ready >= 0);
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCw -\ttime until groveler ready = %d\n"),
|
|
time_until_groveler_ready));
|
|
}
|
|
}
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCw -\tremaining grovel interval = %d\n"),
|
|
remaining_grovel_interval));
|
|
int wait_time = __max(remaining_grovel_interval, time_until_groveler_ready);
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("\tPCw -\twait time = %d\n"), wait_time));
|
|
return wait_time;
|
|
}
|
|
|
|
void
|
|
PartitionController::demarcate_foreground_batch()
|
|
{
|
|
ASSERT(this != 0);
|
|
unsigned int current_time = GET_TICK_COUNT();
|
|
if (!groveler_dead)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 1, (_T("time: %d\n"), current_time));
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCdfb -\tdemarcating foreground batch for drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
SERVICE_SET_FOREGROUND_BATCH_IN_PROGRESS(partition_index, true);
|
|
}
|
|
}
|
|
|
|
void
|
|
PartitionController::command_full_volume_scan()
|
|
{
|
|
ASSERT(this != 0);
|
|
unsigned int current_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 1, (_T("time: %d\n"), current_time));
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCcfvs -\tcommanding full volume scan for drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
initiate_full_volume_scan = true;
|
|
}
|
|
|
|
void
|
|
PartitionController::control_extraction(
|
|
void *context)
|
|
{
|
|
ASSERT(context != 0);
|
|
unsigned int invokation_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 3, (_T("time: %d\n"), invokation_time));
|
|
PartitionController *me = (PartitionController *)context;
|
|
TRACE_PRINTF(TC_partctrl, 3, (_T("\tPCce -\textracting log on drive %s\n"),
|
|
sis_drives.partition_mount_name(me->partition_index)));
|
|
ASSERT(!me->log_extractor_dead);
|
|
if (me->groveler_dead || me->restart_extraction_required)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("\tPCce -\trestarting extraction\n")));
|
|
me->log_extractor_dead = true;
|
|
me->restart_extraction_interval = me->base_restart_extraction_interval;
|
|
me->remaining_restart_extraction_interval = 0;
|
|
restart_extraction(context);
|
|
return;
|
|
}
|
|
DWORD num_entries_extracted;
|
|
DWORDLONG num_bytes_extracted;
|
|
DWORDLONG num_bytes_skipped;
|
|
DWORD num_files_enqueued;
|
|
DWORD num_files_dequeued;
|
|
GrovelStatus status =
|
|
me->groveler->extract_log(&num_entries_extracted, &num_bytes_extracted,
|
|
&num_bytes_skipped, &num_files_enqueued, &num_files_dequeued);
|
|
unsigned int completion_time = GET_TICK_COUNT();
|
|
if (status == Grovel_overrun)
|
|
{
|
|
ASSERT(signed(num_entries_extracted) >= 0);
|
|
ASSERT(signed(num_bytes_extracted) >= 0);
|
|
ASSERT(signed(num_bytes_skipped) >= 0);
|
|
ASSERT(signed(num_files_enqueued) >= 0);
|
|
ASSERT(signed(num_files_dequeued) >= 0);
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCce -\textract_log() returned Grovel_overrun\n")));
|
|
me->initiate_full_volume_scan = true;
|
|
eventlog.report_event(GROVMSG_USN_LOG_OVERRUN, 1,
|
|
sis_drives.partition_mount_name(me->partition_index));
|
|
if (!me->first_extraction)
|
|
{
|
|
__int64 usn_log_size = num_bytes_extracted + num_bytes_skipped;
|
|
ASSERT(me->base_usn_log_size > 0);
|
|
ASSERT(me->max_usn_log_size > 0);
|
|
ASSERT(me->current_usn_log_size >= me->base_usn_log_size);
|
|
ASSERT(me->current_usn_log_size <= me->max_usn_log_size);
|
|
if (usn_log_size > me->current_usn_log_size &&
|
|
me->current_usn_log_size < me->max_usn_log_size)
|
|
{
|
|
if (usn_log_size > me->max_usn_log_size)
|
|
{
|
|
usn_log_size = me->max_usn_log_size;
|
|
}
|
|
_TCHAR size_string[32];
|
|
_stprintf(size_string, _T("%d"), usn_log_size);
|
|
eventlog.report_event(GROVMSG_SET_USN_LOG_SIZE, 2,
|
|
sis_drives.partition_mount_name(me->partition_index),
|
|
size_string);
|
|
TRACE_PRINTF(TC_partctrl, 2,
|
|
(_T("\tPCce -\tincreasing USN log size from %d to %d\n"),
|
|
me->current_usn_log_size, usn_log_size));
|
|
me->current_usn_log_size = usn_log_size;
|
|
GrovelStatus status = me->groveler->set_usn_log_size(usn_log_size);
|
|
if (status == Grovel_error)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCce -\tset_usn_log_size() returned error\n")));
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\t\tsuspending control_extraction()\n")));
|
|
eventlog.report_event(GROVMSG_LOG_EXTRACTOR_DEAD, 1,
|
|
sis_drives.partition_mount_name(me->partition_index));
|
|
me->log_extractor_dead = true;
|
|
me->restart_extraction_interval =
|
|
me->base_restart_extraction_interval;
|
|
me->remaining_restart_extraction_interval =
|
|
me->restart_extraction_interval;
|
|
event_timer.schedule(
|
|
completion_time + me->max_extraction_interval,
|
|
context, restart_extraction);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (status != Grovel_ok)
|
|
{
|
|
ASSERT(status == Grovel_error);
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCce -\textract_log() returned error\n")));
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\t\tsuspending control_extraction()\n")));
|
|
eventlog.report_event(GROVMSG_LOG_EXTRACTOR_DEAD, 1,
|
|
sis_drives.partition_mount_name(me->partition_index));
|
|
me->log_extractor_dead = true;
|
|
me->restart_extraction_interval = me->base_restart_extraction_interval;
|
|
me->remaining_restart_extraction_interval =
|
|
me->restart_extraction_interval;
|
|
event_timer.schedule(completion_time + me->max_extraction_interval,
|
|
context, restart_extraction);
|
|
return;
|
|
}
|
|
unsigned int extraction_time = completion_time - invokation_time;
|
|
ASSERT(signed(extraction_time) >= 0);
|
|
shared_data->increment_value(me->partition_index,
|
|
SDF_extract_time, extraction_time);
|
|
shared_data->increment_value(me->partition_index,
|
|
SDF_working_time, extraction_time);
|
|
int queue_length = me->groveler->count_of_files_in_queue();
|
|
ASSERT(queue_length >= 0);
|
|
shared_data->set_value(me->partition_index, SDF_queue_length, queue_length);
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCce -\tnum entries extracted = %d\n"), num_entries_extracted));
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCce -\tnum files enqueued = %d\n"), num_files_enqueued));
|
|
ASSERT(signed(num_entries_extracted) >= 0);
|
|
ASSERT(signed(num_bytes_extracted) >= 0);
|
|
ASSERT(signed(num_bytes_skipped) >= 0);
|
|
ASSERT(signed(num_files_enqueued) >= 0);
|
|
ASSERT(signed(num_files_dequeued) >= 0);
|
|
ASSERT(num_bytes_extracted >= num_entries_extracted);
|
|
ASSERT(num_files_enqueued <= num_entries_extracted);
|
|
ASSERT(status == Grovel_overrun || num_bytes_skipped == 0);
|
|
me->first_extraction = false;
|
|
if (num_entries_extracted < 1)
|
|
{
|
|
num_entries_extracted = 1;
|
|
}
|
|
ASSERT(me->extraction_interval > 0);
|
|
ASSERT(me->target_entries_per_extraction > 0);
|
|
int ideal_extraction_interval = me->extraction_interval *
|
|
me->target_entries_per_extraction / num_entries_extracted + 1;
|
|
ASSERT(ideal_extraction_interval > 0);
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCce -\tideal extraction interval = %d\n"),
|
|
ideal_extraction_interval));
|
|
if (ideal_extraction_interval < me->extraction_interval)
|
|
{
|
|
me->extraction_interval = ideal_extraction_interval;
|
|
}
|
|
else
|
|
{
|
|
me->extraction_interval = int(sqrt(double(me->extraction_interval)
|
|
* double(ideal_extraction_interval)));
|
|
ASSERT(me->max_extraction_interval > 0);
|
|
if (me->extraction_interval > me->max_extraction_interval)
|
|
{
|
|
me->extraction_interval = me->max_extraction_interval;
|
|
}
|
|
}
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCce -\textraction interval = %d\n"), me->extraction_interval));
|
|
ASSERT(me->extraction_interval > 0);
|
|
ASSERT(me->extraction_interval <= me->max_extraction_interval);
|
|
event_timer.schedule(invokation_time + me->extraction_interval,
|
|
context, control_extraction);
|
|
}
|
|
|
|
void
|
|
PartitionController::restart_extraction(
|
|
void *context)
|
|
{
|
|
ASSERT(context != 0);
|
|
unsigned int invokation_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 3, (_T("time: %d\n"), invokation_time));
|
|
PartitionController *me = (PartitionController *)context;
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCre -\tconsidering restart extraction on drive %s\n"),
|
|
sis_drives.partition_mount_name(me->partition_index)));
|
|
ASSERT(me->log_extractor_dead);
|
|
me->remaining_restart_extraction_interval -=
|
|
__min(me->remaining_restart_extraction_interval,
|
|
me->max_extraction_interval);
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("\tPCre -\tremaining restart extraction interval = %d\n"),
|
|
me->remaining_restart_extraction_interval));
|
|
if (!me->groveler_dead && me->remaining_restart_extraction_interval == 0)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 2, (_T("time: %d\n"), invokation_time));
|
|
TRACE_PRINTF(TC_partctrl, 2,
|
|
(_T("\tPCre -\tattempting restart extraction on drive %s\n"),
|
|
sis_drives.partition_mount_name(me->partition_index)));
|
|
eventlog.report_event(GROVMSG_USN_LOG_RETRY, 1,
|
|
sis_drives.partition_mount_name(me->partition_index));
|
|
GrovelStatus status =
|
|
me->groveler->set_usn_log_size(me->current_usn_log_size);
|
|
if (status == Grovel_ok || status == Grovel_new)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 2,
|
|
(_T("\tPCre -\tset_usn_log_size() returned success\n")));
|
|
_TCHAR size_string[32];
|
|
_stprintf(size_string, _T("%d"), me->current_usn_log_size);
|
|
eventlog.report_event(GROVMSG_INIT_USN_LOG, 2,
|
|
sis_drives.partition_mount_name(me->partition_index),
|
|
size_string);
|
|
me->restart_extraction_required = false;
|
|
me->log_extractor_dead = false;
|
|
me->first_extraction = true;
|
|
control_extraction(context);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCre -\tset_usn_log_size() returned error\n")));
|
|
ASSERT(status == Grovel_error);
|
|
me->remaining_restart_extraction_interval =
|
|
me->restart_extraction_interval;
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCre -\tremaining restart extraction interval = %d\n"),
|
|
me->remaining_restart_extraction_interval));
|
|
me->restart_extraction_interval *= 2;
|
|
if (me->restart_extraction_interval >
|
|
me->max_restart_extraction_interval)
|
|
{
|
|
me->restart_extraction_interval =
|
|
me->max_restart_extraction_interval;
|
|
}
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCre -\tnext restart extraction interval = %d\n"),
|
|
me->restart_extraction_interval));
|
|
}
|
|
}
|
|
event_timer.schedule(invokation_time + me->max_extraction_interval,
|
|
context, restart_extraction);
|
|
}
|
|
|
|
void
|
|
PartitionController::restart_groveling(
|
|
void *context)
|
|
{
|
|
ASSERT(context != 0);
|
|
unsigned int invokation_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 2, (_T("time: %d\n"), invokation_time));
|
|
PartitionController *me = (PartitionController *)context;
|
|
TRACE_PRINTF(TC_partctrl, 2,
|
|
(_T("\tPCrg -\tattempting to restart groveler on drive %s\n"),
|
|
sis_drives.partition_mount_name(me->partition_index)));
|
|
ASSERT(me->groveler_dead);
|
|
me->groveler->close();
|
|
eventlog.report_event(GROVMSG_GROVELER_RETRY, 1,
|
|
sis_drives.partition_mount_name(me->partition_index));
|
|
GrovelStatus status = me->groveler->open(
|
|
sis_drives.partition_guid_name(me->partition_index),
|
|
sis_drives.partition_mount_name(me->partition_index),
|
|
me->partition_index == log_drive->drive_index(),
|
|
me->read_report_discard_threshold,
|
|
me->min_file_size,
|
|
me->min_file_age,
|
|
me->allow_compressed_files,
|
|
me->allow_encrypted_files,
|
|
me->allow_hidden_files,
|
|
me->allow_offline_files,
|
|
me->allow_temporary_files,
|
|
me->num_excluded_paths,
|
|
me->excluded_paths,
|
|
me->base_regrovel_interval,
|
|
me->max_regrovel_interval);
|
|
if (status == Grovel_ok)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 2,
|
|
(_T("\tPCrg -\topen() returned ok\n")));
|
|
log_drive->partition_initialized(me->partition_index);
|
|
eventlog.report_event(GROVMSG_GROVELER_STARTED, 1,
|
|
sis_drives.partition_mount_name(me->partition_index));
|
|
}
|
|
else if (status == Grovel_new)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 2,
|
|
(_T("\tPCrg -\topen() returned new\n")));
|
|
}
|
|
else
|
|
{
|
|
ASSERT(status == Grovel_error);
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCrg -\topen() returned error\n")));
|
|
}
|
|
me->restart_volume_scan = true;
|
|
me->initialize_groveling(status);
|
|
}
|
|
|
|
void
|
|
PartitionController::update_partition_info(
|
|
void *context)
|
|
{
|
|
ASSERT(context != 0);
|
|
unsigned int invokation_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 3, (_T("time: %d\n"), invokation_time));
|
|
PartitionController *me = (PartitionController *)context;
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCupi -\tupdating partition info for drive %s\n"),
|
|
sis_drives.partition_mount_name(me->partition_index)));
|
|
ULARGE_INTEGER my_free_bytes;
|
|
ULARGE_INTEGER total_bytes;
|
|
ULARGE_INTEGER free_bytes;
|
|
int ok = GetDiskFreeSpaceEx(
|
|
sis_drives.partition_guid_name(me->partition_index),
|
|
&my_free_bytes, &total_bytes, &free_bytes);
|
|
if (ok)
|
|
{
|
|
me->volume_total_bytes = double(__int64(total_bytes.QuadPart));
|
|
me->volume_free_bytes = double(__int64(free_bytes.QuadPart));
|
|
ASSERT(me->volume_total_bytes > 0.0);
|
|
ASSERT(me->volume_free_bytes >= 0.0);
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("\tPCupi -\tvolume total bytes = %f\n"),
|
|
me->volume_total_bytes));
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("\tPCupi -\tvolume free bytes = %f\n"),
|
|
me->volume_free_bytes));
|
|
}
|
|
else
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 3, (_T("\tPCupi -\tGetDiskFreeSpaceEx() returned error.\n")));
|
|
DWORD err = GetLastError();
|
|
PRINT_DEBUG_MSG((_T("GROVELER: GetDiskFreeSpaceEx() failed with error %d\n"),
|
|
err));
|
|
}
|
|
ASSERT(me->partition_info_update_interval > 0);
|
|
event_timer.schedule(invokation_time + me->partition_info_update_interval,
|
|
context, update_partition_info);
|
|
}
|
|
|
|
void
|
|
PartitionController::initialize_groveling(
|
|
GrovelStatus groveler_status)
|
|
{
|
|
ASSERT(this != 0);
|
|
unsigned int invokation_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 3, (_T("time: %d\n"), invokation_time));
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCig -\tinitializing groveling for drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
if (groveler_status == Grovel_ok || groveler_status == Grovel_new)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCig -\tgroveler_status indicates success\n")));
|
|
groveler_dead = false;
|
|
DWORD serial_number;
|
|
int ok = GetVolumeInformation(
|
|
sis_drives.partition_guid_name(partition_index),
|
|
0, 0, &serial_number, 0, 0, 0, 0);
|
|
if (!ok)
|
|
{
|
|
DWORD err = GetLastError();
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCig -\tGetVolumeInformation() returned error %d\n"),
|
|
err));
|
|
PRINT_DEBUG_MSG((_T("GROVELER: GetVolumeInformation() failed with error %d\n"),
|
|
err));
|
|
}
|
|
else
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCig -\tGetVolumeInformation() returned ")
|
|
_T("serial number %d\n"), serial_number));
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCig -\trecorded serial number is %d\n"),
|
|
*volume_serial_number));
|
|
if (volume_serial_number == 0)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCig -\tGetVolumeInformation() returned ")
|
|
_T("serial number 0\n")));
|
|
PRINT_DEBUG_MSG((_T("GROVELER: GetVolumeInformation() returned ")
|
|
_T("volume serial number 0\n")));
|
|
}
|
|
}
|
|
if (ok && int(serial_number) != *volume_serial_number)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCig -\tresetting read time filters\n")));
|
|
read_time_filter[RT_hash]->reset();
|
|
read_time_filter[RT_compare]->reset();
|
|
read_time_confidence_estimator.reset();
|
|
*read_time_estimate[RT_hash] =
|
|
read_time_filter[RT_hash]->retrieve_value();
|
|
ASSERT(*read_time_estimate[RT_hash] == 0.0);
|
|
*read_time_estimate[RT_compare] =
|
|
read_time_filter[RT_compare]->retrieve_value();
|
|
ASSERT(*read_time_estimate[RT_compare] == 0.0);
|
|
*read_time_confidence = read_time_confidence_estimator.confidence();
|
|
ASSERT(*read_time_confidence == 0.0);
|
|
*volume_serial_number = serial_number;
|
|
}
|
|
extraction_interval = max_extraction_interval;
|
|
free_space_ratio = 0.0;
|
|
calculate_effective_max_grovel_interval();
|
|
ASSERT(effective_max_grovel_interval > 0);
|
|
ASSERT(effective_max_grovel_interval <= max_grovel_interval);
|
|
grovel_interval = base_grovel_interval;
|
|
remaining_grovel_interval = grovel_interval;
|
|
ok_to_record_measurement = true;
|
|
next_untrusted_measurement_time = invokation_time;
|
|
restart_extraction_required = true;
|
|
if (groveler_status == Grovel_new)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCig -\tinitiating full volume scan\n")));
|
|
initiate_full_volume_scan = true;
|
|
performing_full_volume_scan = false;
|
|
extended_restart_in_progress = true;
|
|
}
|
|
else
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCig -\tcontinuing full volume scan\n")));
|
|
initiate_full_volume_scan = false;
|
|
performing_full_volume_scan = true;
|
|
extended_restart_in_progress = false;
|
|
restart_groveling_interval = base_restart_groveling_interval;
|
|
}
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCig -\trestart groveling interval = %d\n"),
|
|
restart_groveling_interval));
|
|
}
|
|
else
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCig -\tgroveler_status indicates error or disable\n")));
|
|
ASSERT(groveler_status == Grovel_error
|
|
|| groveler_status == Grovel_disable);
|
|
groveler_dead = true;
|
|
if (groveler_status != Grovel_disable && error_retry_groveling)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCig -\tscheduling restart groveling\n")));
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCig -\trestart groveling interval = %d\n"),
|
|
restart_groveling_interval));
|
|
event_timer.schedule(invokation_time + restart_groveling_interval,
|
|
(void *)this, restart_groveling);
|
|
}
|
|
restart_groveling_interval *= 2;
|
|
if (restart_groveling_interval > max_restart_groveling_interval)
|
|
{
|
|
restart_groveling_interval = max_restart_groveling_interval;
|
|
}
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCig -\tnext restart groveling interval = %d\n"),
|
|
restart_groveling_interval));
|
|
}
|
|
}
|
|
|
|
bool
|
|
PartitionController::control_groveling(
|
|
DWORD grovel_duration,
|
|
DWORD *count_of_files_hashed,
|
|
DWORDLONG *bytes_of_files_hashed,
|
|
DWORD *count_of_files_matching,
|
|
DWORDLONG *bytes_of_files_matching,
|
|
DWORD *count_of_files_compared,
|
|
DWORDLONG *bytes_of_files_compared,
|
|
DWORD *count_of_files_merged,
|
|
DWORDLONG *bytes_of_files_merged,
|
|
DWORD *count_of_files_enqueued,
|
|
DWORD *count_of_files_dequeued,
|
|
double cpu_load)
|
|
{
|
|
ASSERT(this != 0);
|
|
ASSERT(grovel_duration > 0);
|
|
ASSERT(count_of_files_hashed != 0);
|
|
ASSERT(bytes_of_files_hashed != 0);
|
|
ASSERT(count_of_files_matching != 0);
|
|
ASSERT(bytes_of_files_matching != 0);
|
|
ASSERT(count_of_files_compared != 0);
|
|
ASSERT(bytes_of_files_compared != 0);
|
|
ASSERT(count_of_files_merged != 0);
|
|
ASSERT(bytes_of_files_merged != 0);
|
|
ASSERT(count_of_files_enqueued != 0);
|
|
ASSERT(count_of_files_dequeued != 0);
|
|
ASSERT(cpu_load >= 0.0);
|
|
ASSERT(cpu_load <= 1.0);
|
|
ASSERT(!groveler_dead);
|
|
unsigned int invokation_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 2,
|
|
(_T("time: %d\n"), invokation_time));
|
|
TRACE_PRINTF(TC_partctrl, 2, (_T("\tPCcg -\tgroveling on drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
DWORD hash_read_ops;
|
|
DWORD hash_read_time;
|
|
DWORD compare_read_ops;
|
|
DWORD compare_read_time;
|
|
DWORD merge_time;
|
|
GrovelStatus status = groveler->grovel(grovel_duration,
|
|
&hash_read_ops, &hash_read_time,
|
|
count_of_files_hashed, bytes_of_files_hashed,
|
|
&compare_read_ops, &compare_read_time,
|
|
count_of_files_compared, bytes_of_files_compared,
|
|
count_of_files_matching, bytes_of_files_matching,
|
|
&merge_time,
|
|
count_of_files_merged, bytes_of_files_merged,
|
|
count_of_files_enqueued, count_of_files_dequeued);
|
|
unsigned int completion_time = GET_TICK_COUNT();
|
|
if (status != Grovel_ok && status != Grovel_pending)
|
|
{
|
|
ASSERT(status == Grovel_error);
|
|
*count_of_files_hashed = 0;
|
|
*bytes_of_files_hashed = 0;
|
|
*count_of_files_matching = 0;
|
|
*bytes_of_files_matching = 0;
|
|
*count_of_files_compared = 0;
|
|
*bytes_of_files_compared = 0;
|
|
*count_of_files_merged = 0;
|
|
*bytes_of_files_merged = 0;
|
|
*count_of_files_enqueued = 0;
|
|
*count_of_files_dequeued = 0;
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCcg -\tgrovel() returned error -- groveler dead\n")));
|
|
eventlog.report_event(GROVMSG_GROVELER_DEAD, 1,
|
|
sis_drives.partition_mount_name(partition_index));
|
|
groveler_dead = true;
|
|
if (error_retry_groveling)
|
|
{
|
|
restart_groveling_interval = base_restart_groveling_interval;
|
|
event_timer.schedule(invokation_time + restart_groveling_interval,
|
|
(void *)this, restart_groveling);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
ASSERT(hash_read_ops > 0 || hash_read_time == 0);
|
|
ASSERT(compare_read_ops > 0 || compare_read_time == 0);
|
|
ASSERT(*bytes_of_files_hashed >= *count_of_files_hashed);
|
|
ASSERT(*bytes_of_files_matching >= *count_of_files_matching);
|
|
ASSERT(*bytes_of_files_compared >= *count_of_files_compared);
|
|
ASSERT(*bytes_of_files_merged >= *count_of_files_merged);
|
|
ASSERT(*count_of_files_hashed >= *count_of_files_matching);
|
|
ASSERT(*bytes_of_files_hashed >= *bytes_of_files_matching);
|
|
ASSERT(*count_of_files_compared >= *count_of_files_merged);
|
|
ASSERT(*bytes_of_files_compared >= *bytes_of_files_merged);
|
|
ASSERT(*count_of_files_dequeued >= *count_of_files_hashed);
|
|
unsigned int grovel_time = completion_time - invokation_time;
|
|
ASSERT(signed(grovel_time) >= 0);
|
|
shared_data->increment_value(partition_index,
|
|
SDF_grovel_time, grovel_time);
|
|
shared_data->increment_value(partition_index,
|
|
SDF_working_time, grovel_time);
|
|
shared_data->increment_value(partition_index,
|
|
SDF_files_hashed, *count_of_files_hashed);
|
|
shared_data->increment_value(partition_index,
|
|
SDF_files_compared, *count_of_files_compared);
|
|
shared_data->increment_value(partition_index,
|
|
SDF_files_merged, *count_of_files_merged);
|
|
int files_in_queue = groveler->count_of_files_in_queue();
|
|
ASSERT(files_in_queue >= 0);
|
|
int files_to_compare = groveler->count_of_files_to_compare();
|
|
ASSERT(files_to_compare >= 0);
|
|
shared_data->set_value(partition_index, SDF_queue_length, files_in_queue);
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCcg -\thash read ops = %d\n"), hash_read_ops));
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCcg -\thash read time = %d\n"), hash_read_time));
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCcg -\tcompare read ops = %d\n"), compare_read_ops));
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCcg -\tcompare read time = %d\n"), compare_read_time));
|
|
shared_data->increment_value(partition_index,
|
|
SDF_hash_read_time, hash_read_time);
|
|
shared_data->increment_value(partition_index,
|
|
SDF_hash_read_ops, hash_read_ops);
|
|
shared_data->increment_value(partition_index,
|
|
SDF_compare_read_time, compare_read_time);
|
|
shared_data->increment_value(partition_index,
|
|
SDF_compare_read_ops, compare_read_ops);
|
|
update_peak_finder(RT_hash, hash_read_time, hash_read_ops);
|
|
update_peak_finder(RT_compare, compare_read_time, compare_read_ops);
|
|
shared_data->set_value(partition_index,
|
|
SDF_hash_read_estimate, __int64(*read_time_estimate[RT_hash]));
|
|
shared_data->set_value(partition_index,
|
|
SDF_compare_read_estimate, __int64(*read_time_estimate[RT_compare]));
|
|
int count_of_files_groveled =
|
|
*count_of_files_hashed + *count_of_files_compared;
|
|
ASSERT(mean_file_size != 0);
|
|
if (count_of_files_groveled > 0)
|
|
{
|
|
__int64 bytes_of_files_groveled =
|
|
*bytes_of_files_hashed + *bytes_of_files_compared;
|
|
double sample_mean_file_size =
|
|
double(bytes_of_files_groveled) / double(count_of_files_groveled);
|
|
ASSERT(sample_mean_file_size > 0.0);
|
|
file_size_filter.update_value(sample_mean_file_size,
|
|
count_of_files_groveled);
|
|
*mean_file_size = file_size_filter.retrieve_value();
|
|
ASSERT(*mean_file_size > 0.0);
|
|
}
|
|
ASSERT(dequeue_hash_ratio != 0);
|
|
ASSERT(*dequeue_hash_ratio >= 0.0);
|
|
ASSERT(*dequeue_hash_ratio <= 1.0);
|
|
double files_to_hash = *dequeue_hash_ratio * double(files_in_queue);
|
|
ASSERT(*mean_file_size >= 0.0);
|
|
double bytes_to_hash = *mean_file_size * files_to_hash;
|
|
double bytes_to_compare = *mean_file_size * double(files_to_compare);
|
|
double expected_bytes_to_free = *compare_match_ratio *
|
|
(bytes_to_compare + *hash_match_ratio * bytes_to_hash);
|
|
double expected_free_bytes = volume_free_bytes + expected_bytes_to_free;
|
|
free_space_ratio = 0;
|
|
if (expected_free_bytes > 0)
|
|
{
|
|
free_space_ratio = expected_bytes_to_free / expected_free_bytes;
|
|
}
|
|
ASSERT(free_space_ratio >= 0.0);
|
|
ASSERT(free_space_ratio <= 1.0);
|
|
calculate_effective_max_grovel_interval();
|
|
ASSERT(effective_max_grovel_interval > 0);
|
|
ASSERT(effective_max_grovel_interval <= max_grovel_interval);
|
|
double use_multiplier =
|
|
base_use_multiplier + use_multiplier_slope * free_space_ratio;
|
|
ASSERT(use_multiplier >= 0.0);
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCcg -\tuse multiplier = %f\n"), use_multiplier));
|
|
double hash_comparison_time = use_multiplier * *read_time_estimate[RT_hash];
|
|
ASSERT(hash_comparison_time >= 0.0);
|
|
double compare_comparison_time =
|
|
use_multiplier * *read_time_estimate[RT_compare];
|
|
ASSERT(compare_comparison_time >= 0.0);
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCcg -\thash comparison time = %f\n"), hash_comparison_time));
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCcg -\tcompare comparison time = %f\n"),
|
|
compare_comparison_time));
|
|
TRACE_PRINTF(TC_partctrl, 5, (_T("\tPCcg -\tcpu load = %f\n"), cpu_load));
|
|
double cpu_load_threshold = base_cpu_load_threshold +
|
|
cpu_load_threshold_slope * free_space_ratio;
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCcg -\tcpu load threshold = %f\n"), cpu_load_threshold));
|
|
ASSERT(cpu_load_threshold >= 0.0);
|
|
ASSERT(cpu_load_threshold <= 1.0);
|
|
if (cpu_load > cpu_load_threshold || read_mean_comparator.exceeds(
|
|
hash_comparison_time, compare_comparison_time))
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCcg -\tread time exceeds acceptable bounds\n")));
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\t\tor CPU load exceeds threshold\n")));
|
|
ASSERT(grovel_interval > 0);
|
|
remaining_grovel_interval = grovel_interval;
|
|
read_mean_comparator.reset();
|
|
grovel_interval *= 2;
|
|
ok_to_record_measurement = false;
|
|
}
|
|
else if (read_mean_comparator.within(hash_comparison_time,
|
|
compare_comparison_time))
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCcg -\tread time within acceptable bounds\n")));
|
|
ASSERT(base_grovel_interval > 0);
|
|
grovel_interval = base_grovel_interval;
|
|
ok_to_record_measurement = true;
|
|
}
|
|
if (grovel_interval > effective_max_grovel_interval)
|
|
{
|
|
ASSERT(effective_max_grovel_interval > 0);
|
|
grovel_interval = effective_max_grovel_interval;
|
|
}
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("\tPCcg -\tgrovel interval = %d\n"), grovel_interval));
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("\tPCcg -\tremaining grovel interval = %d\n"),
|
|
remaining_grovel_interval));
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
PartitionController::control_volume_scan(
|
|
int scan_duration,
|
|
DWORD *count_of_files_enqueued)
|
|
{
|
|
ASSERT(this != 0);
|
|
ASSERT(scan_duration > 0);
|
|
ASSERT(count_of_files_enqueued != 0);
|
|
ASSERT(!groveler_dead);
|
|
unsigned int invokation_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 2, (_T("time: %d\n"), invokation_time));
|
|
TRACE_PRINTF(TC_partctrl, 2,
|
|
(_T("\tPCcvs -\tscanning volume on drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
DWORD time_consumed;
|
|
DWORD findfirst_count;
|
|
DWORD findnext_count;
|
|
GrovelStatus status = groveler->scan_volume(scan_duration,
|
|
restart_volume_scan, &time_consumed, &findfirst_count, &findnext_count,
|
|
count_of_files_enqueued);
|
|
unsigned int completion_time = GET_TICK_COUNT();
|
|
if (status == Grovel_ok || status == Grovel_pending)
|
|
{
|
|
if (extended_restart_in_progress)
|
|
{
|
|
log_drive->partition_initialized(partition_index);
|
|
eventlog.report_event(GROVMSG_GROVELER_STARTED, 1,
|
|
sis_drives.partition_mount_name(partition_index));
|
|
extended_restart_in_progress = false;
|
|
}
|
|
if (status == Grovel_ok)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCcvs -\tcompleted volume scan\n")));
|
|
performing_full_volume_scan = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(status == Grovel_error);
|
|
*count_of_files_enqueued = 0;
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCcvs -\tscan_volume() returned error -- groveler dead\n")));
|
|
if (!extended_restart_in_progress)
|
|
{
|
|
eventlog.report_event(GROVMSG_GROVELER_DEAD, 1,
|
|
sis_drives.partition_mount_name(partition_index));
|
|
}
|
|
groveler_dead = true;
|
|
if (error_retry_groveling)
|
|
{
|
|
if (!extended_restart_in_progress)
|
|
{
|
|
restart_groveling_interval = base_restart_groveling_interval;
|
|
}
|
|
event_timer.schedule(invokation_time + restart_groveling_interval,
|
|
(void *)this, restart_groveling);
|
|
if (extended_restart_in_progress)
|
|
{
|
|
restart_groveling_interval *= 2;
|
|
if (restart_groveling_interval > max_restart_groveling_interval)
|
|
{
|
|
restart_groveling_interval = max_restart_groveling_interval;
|
|
}
|
|
extended_restart_in_progress = false;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
extended_restart_in_progress = false;
|
|
return false;
|
|
}
|
|
}
|
|
ASSERT(signed(time_consumed) >= 0);
|
|
ASSERT(signed(findfirst_count) >= 0);
|
|
ASSERT(signed(findnext_count) >= 0);
|
|
ASSERT(signed(*count_of_files_enqueued) >= 0);
|
|
unsigned int scan_time = completion_time - invokation_time;
|
|
ASSERT(signed(scan_time) >= 0);
|
|
shared_data->increment_value(partition_index, SDF_volscan_time, scan_time);
|
|
shared_data->increment_value(partition_index, SDF_working_time, scan_time);
|
|
shared_data->increment_value(partition_index,
|
|
SDF_files_scanned, findfirst_count + findnext_count);
|
|
int queue_length = groveler->count_of_files_in_queue();
|
|
ASSERT(queue_length >= 0);
|
|
shared_data->set_value(partition_index, SDF_queue_length, queue_length);
|
|
restart_volume_scan = false;
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCcvs -\ttime consumed = %d\n"), time_consumed));
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCcvs -\tfindfirst count = %d\n"), findfirst_count));
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCcvs -\tfindnext count = %d\n"), findnext_count));
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCcvs -\tcount of files enqueued = %d\n"),
|
|
*count_of_files_enqueued));
|
|
return true;
|
|
}
|
|
|
|
void
|
|
PartitionController::update_peak_finder(
|
|
ReadType read_type,
|
|
DWORD read_time,
|
|
DWORD read_ops)
|
|
{
|
|
ASSERT(this != 0);
|
|
unsigned int invokation_time = GET_TICK_COUNT();
|
|
TRACE_PRINTF(TC_partctrl, 3, (_T("time: %d\n"), invokation_time));
|
|
TRACE_PRINTF(TC_partctrl, 3,
|
|
(_T("\tPCupf -\tupdating peak finder for drive %s\n"),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
ASSERT(read_type == RT_hash || read_type == RT_compare);
|
|
ASSERT(signed(read_time) >= 0);
|
|
ASSERT(signed(read_ops) >= 0);
|
|
if (read_ops > 0)
|
|
{
|
|
double time_per_read = double(read_time)/double(read_ops);
|
|
ASSERT(time_per_read >= 0.0);
|
|
TRACE_PRINTF(TC_partctrl, 4, (_T("\tPCupf -\ttime per %s read = %f\n"),
|
|
(read_type == RT_hash ? _T("hash") : _T("compare")),
|
|
time_per_read));
|
|
read_mean_comparator.sample(read_type, time_per_read);
|
|
ASSERT(read_peak_finder[read_type] != 0);
|
|
ASSERT(read_time_filter[read_type] != 0);
|
|
ASSERT(read_time_estimate[read_type] != 0);
|
|
ASSERT(read_time_confidence != 0);
|
|
if (ok_to_record_measurement ||
|
|
signed(invokation_time - next_untrusted_measurement_time) >= 0)
|
|
{
|
|
TRACE_PRINTF(TC_partctrl, 2,
|
|
(_T("\tPCupf -\trecording %s measurement for drive %s\n"),
|
|
(read_type == RT_hash ? _T("hash") : _T("compare")),
|
|
sis_drives.partition_mount_name(partition_index)));
|
|
read_peak_finder[read_type]->sample(time_per_read, read_ops);
|
|
ASSERT(untrusted_measurement_interval > 0);
|
|
ok_to_record_measurement = true;
|
|
next_untrusted_measurement_time =
|
|
invokation_time + untrusted_measurement_interval;
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCupf -\tnext untrusted measurement time = %d\n"),
|
|
next_untrusted_measurement_time));
|
|
}
|
|
if (read_peak_finder[read_type]->found())
|
|
{
|
|
double peak = read_peak_finder[read_type]->median();
|
|
TRACE_PRINTF(TC_partctrl, 1,
|
|
(_T("\tPCupf -\t%s read peak found: %f\n"),
|
|
(read_type == RT_hash ? _T("hash") : _T("compare")), peak));
|
|
if (peak > 0.0)
|
|
{
|
|
read_time_filter[read_type]->update_value(peak);
|
|
}
|
|
else
|
|
{
|
|
PRINT_DEBUG_MSG((_T("GROVELER: update_peak_finder() peak finder returned peak of zero\n")));
|
|
}
|
|
read_peak_finder[read_type]->reset();
|
|
*read_time_estimate[read_type] =
|
|
read_time_filter[read_type]->retrieve_value();
|
|
ASSERT(*read_time_estimate[read_type] > 0.0);
|
|
TRACE_PRINTF(TC_partctrl, 2,
|
|
(_T("\tPCupf -\t%s read time estimate = %f\n"),
|
|
(read_type == RT_hash ? _T("hash") : _T("compare")),
|
|
*read_time_estimate[read_type]));
|
|
double sample_peak_ratio = *read_time_estimate[read_type] / peak;
|
|
double sample_read_time_confidence =
|
|
(sample_peak_ratio + peak_finder_accuracy - 1.0) /
|
|
peak_finder_accuracy;
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCupf -\tsample read time confidence = %f\n"),
|
|
sample_read_time_confidence));
|
|
read_time_confidence_estimator.update(read_type,
|
|
sample_read_time_confidence);
|
|
*read_time_confidence = read_time_confidence_estimator.confidence();
|
|
ASSERT(*read_time_confidence >= 0.0);
|
|
ASSERT(*read_time_confidence <= 1.0);
|
|
TRACE_PRINTF(TC_partctrl, 2,
|
|
(_T("\tPCupf -\tread time confidence = %f\n"),
|
|
*read_time_confidence));
|
|
calculate_effective_max_grovel_interval();
|
|
ASSERT(effective_max_grovel_interval > 0);
|
|
ASSERT(effective_max_grovel_interval <= max_grovel_interval);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PartitionController::calculate_effective_max_grovel_interval()
|
|
{
|
|
ASSERT(this != 0);
|
|
ASSERT(read_time_confidence != 0);
|
|
ASSERT(*read_time_confidence >= 0.0);
|
|
ASSERT(*read_time_confidence <= 1.0);
|
|
TRACE_PRINTF(TC_partctrl, 4,
|
|
(_T("\tPCcemgi -\tread time confidence = %f\n"),
|
|
*read_time_confidence));
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCcemgi -\tfree space ratio = %f\n"), free_space_ratio));
|
|
double log_untrusted_measurement_interval = log_max_grovel_interval -
|
|
(1.0 - *read_time_confidence) * log_low_confidence_slope;
|
|
untrusted_measurement_interval =
|
|
int(exp(log_untrusted_measurement_interval));
|
|
ASSERT(untrusted_measurement_interval > 0);
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCcemgi -\tuntrusted measurement interval = %d\n"),
|
|
untrusted_measurement_interval));
|
|
int low_disk_space_interval = max_grovel_interval -
|
|
int(free_space_ratio * low_disk_space_slope);
|
|
ASSERT(low_disk_space_interval > 0);
|
|
effective_max_grovel_interval =
|
|
__min(untrusted_measurement_interval, low_disk_space_interval);
|
|
ASSERT(effective_max_grovel_interval > 0);
|
|
ASSERT(effective_max_grovel_interval <= max_grovel_interval);
|
|
TRACE_PRINTF(TC_partctrl, 5,
|
|
(_T("\tPCcemgi -\teffective max grovel interval = %d\n"),
|
|
effective_max_grovel_interval));
|
|
}
|