cfg->dvr_warm_time = 30;
cfg->dvr_update_window = 24 * 3600;
cfg->dvr_pathname = strdup("$t$n.$x");
- cfg->dvr_cleanup_threshold = 2000;
+ cfg->dvr_cleanup_threshold_low = 200;
+ cfg->dvr_cleanup_threshold_high = 2000;
/* Muxer config */
cfg->dvr_muxcnf.m_cache = MC_CACHE_DONTKEEP;
lock_assert(&global_lock);
dvr_config_storage_check(cfg);
- if (cfg->dvr_cleanup_threshold < 100)
- cfg->dvr_cleanup_threshold = 100; // as checking is only periodically, lower is not save
+ if (cfg->dvr_cleanup_threshold_low < 50)
+ cfg->dvr_cleanup_threshold_low = 50; // as checking is only periodically, lower is not save
+ if (cfg->dvr_cleanup_threshold_high < cfg->dvr_cleanup_threshold_high)
+ cfg->dvr_cleanup_threshold_high = cfg->dvr_cleanup_threshold_low + 50;
if (cfg->dvr_removal_days != DVR_RET_FOREVER &&
cfg->dvr_removal_days > cfg->dvr_retention_days)
cfg->dvr_retention_days = DVR_RET_ONREMOVE;
{ N_("3 months"), DVR_RET_3MONTH },
{ N_("6 months"), DVR_RET_6MONTH },
{ N_("1 year"), DVR_RET_1YEAR },
- { N_("Until space needed"), DVR_RET_SPACENEED },
+ { N_("Maintained space"), DVR_RET_SPACE },
{ N_("Forever"), DVR_RET_FOREVER },
};
return strtab2htsmsg_u32(tab, 1, lang);
{ N_("3 months"), DVR_RET_3MONTH },
{ N_("6 months"), DVR_RET_6MONTH },
{ N_("1 year"), DVR_RET_1YEAR },
+ { N_("2 years"), DVR_RET_2YEARS },
+ { N_("3 years"), DVR_RET_3YEARS },
{ N_("On file removal"), DVR_RET_ONREMOVE },
{ N_("Forever"), DVR_RET_FOREVER },
};
},
{
.type = PT_U32,
- .id = "storage-cleanup",
- .name = N_("\"Until space needed\" cleanup below (MB)"),
- .off = offsetof(dvr_config_t, dvr_cleanup_threshold),
- .def.i = 2000, //2000 MB
+ .id = "storage-cleanup-low",
+ .name = N_("Maintain free storage space (MiB)"),
+ .off = offsetof(dvr_config_t, dvr_cleanup_threshold_low),
+ .def.i = 200,
+ .group = 2,
+ },
+ {
+ .type = PT_U32,
+ .id = "storage-cleanup-high",
+ .name = N_("Maintain used storage space (MiB)"),
+ .off = offsetof(dvr_config_t, dvr_cleanup_threshold_high),
+ .def.i = 2000,
.group = 2,
},
{
char buf[24];
uint32_t removal = dvr_entry_get_removal_days(de);
- if (removal < DVR_RET_SPACENEED)
+ if (removal < DVR_RET_SPACE)
snprintf(buf, sizeof(buf), "%i days", removal);
- else if (removal == DVR_RET_SPACENEED)
+ else if (removal == DVR_RET_SPACE)
return strdup("Until space needed");
else
return strdup("Forever");
uint32_t retention = dvr_entry_get_retention_days(de);
stop = de->de_stop + removal * (time_t)86400;
- if ((removal > 0 || retention == 0) && removal < DVR_RET_SPACENEED) {
+ if ((removal > 0 || retention == 0) && removal < DVR_RET_SPACE) {
if (stop > dispatch_clock) {
dvr_entry_retention_arm(de, dvr_timer_remove_files, stop);
return;
{ N_("3 months"), DVR_RET_3MONTH },
{ N_("6 months"), DVR_RET_6MONTH },
{ N_("1 year"), DVR_RET_1YEAR },
- { N_("Until space needed"), DVR_RET_SPACENEED },
+ { N_("Maintained space"), DVR_RET_SPACE },
{ N_("Forever"), DVR_RET_FOREVER },
};
return strtab2htsmsg_u32(tab, 1, lang);
#include <sys/statvfs.h>
#endif
+#define MIB(v) ((int64_t)v*((int64_t)1024*1024))
+#define TOMIB(v) (v/((int64_t)1024*1024))
+
static int dvr_disk_space_config_idx;
static int dvr_disk_space_config_size;
static time_t dvr_disk_space_config_lastdelete;
{
dvr_entry_t *de, *oldest;
time_t stoptime;
- int64_t requiredBytes, availBytes;
+ int64_t requiredBytes, maximalBytes, availBytes, usedBytes, diskBytes;
int64_t clearedBytes = 0, fileSize;
unsigned long int filesystemId;
struct statvfs diskdata;
filesystemId = diskdata.f_fsid;
availBytes = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
- requiredBytes = (int64_t)cfg->dvr_cleanup_threshold*(int64_t)1024*(int64_t)1024;
+ requiredBytes = MIB(cfg->dvr_cleanup_threshold_low);
+ diskBytes = diskdata.f_bsize * (int64_t)diskdata.f_blocks;
+ usedBytes = diskBytes - availBytes;
+ maximalBytes = MIB(cfg->dvr_cleanup_threshold_high);
configName = cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile";
/* When deleting a file from the disk, the system needs some time to actually do this */
return -1;
}
- if (diskdata.f_bsize * (int64_t)diskdata.f_blocks < requiredBytes) {
- tvhlog(LOG_WARNING, "dvr","disk space cleanup for config \"%s\", required free space \"%ld MB\" is smaller than the total disk size!",
+ if (diskBytes < requiredBytes) {
+ tvhlog(LOG_WARNING, "dvr","disk space cleanup for config \"%s\", required free space \"%ld MiB\" is smaller than the total disk size!",
configName, requiredBytes/(int64_t)1024/(int64_t)1024);
- return -1;
+ if (maximalBytes >= usedBytes)
+ return -1;
}
- tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", required free space \"%ld MB\", current free space \"%ld MB\"",
- configName, requiredBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
+ tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", required/current free space \"%ld/%ld MiB\", required/current used space \"%ld/%ld MB\"",
+ configName, TOMIB(requiredBytes), TOMIB(availBytes), TOMIB(maximalBytes), TOMIB(usedBytes));
- while (availBytes < requiredBytes) {
+ while (availBytes < requiredBytes || maximalBytes < usedBytes) {
oldest = NULL;
stoptime = dispatch_clock;
if (dvr_entry_get_stop_time(de) > stoptime)
continue;
- if (dvr_entry_get_removal_days(de) != DVR_RET_SPACENEED) // only remove the allowed ones
+ if (dvr_entry_get_removal_days(de) != DVR_RET_SPACE) // only remove the allowed ones
continue;
if (dvr_get_filename(de) == NULL || dvr_get_filesize(de) <= 0)
fileSize = dvr_get_filesize(oldest);
availBytes += fileSize;
clearedBytes += fileSize;
+ usedBytes -= fileSize;
localtime_r(&stoptime, &tm);
if (strftime(tbuf, sizeof(tbuf), "%F %T", &tm) <= 0)
}
finish:
- tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", cleared \"%ld MB\" of disk space, new free disk space \"%ld MB\"",
- configName, clearedBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
+ tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", cleared \"%ld MB\" of disk space, new free disk space \"%ld MiB\", new used disk space \"%ld MiB\"",
+ configName, TOMIB(clearedBytes), TOMIB(availBytes), TOMIB(usedBytes));
return clearedBytes;
}
dvr_config_t *cfg;
dvr_entry_t *de;
struct statvfs diskdata;
- int64_t requiredBytes, availBytes;
+ int64_t requiredBytes, maximalBytes, availBytes, usedBytes;
int idx = 0, cleanupDone = 0;
pthread_mutex_lock(&global_lock);
statvfs(cfg->dvr_storage, &diskdata) != -1)
{
availBytes = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
- requiredBytes = (int64_t)cfg->dvr_cleanup_threshold*(int64_t)1024*(int64_t)1024;
+ usedBytes = (diskdata.f_bsize * (int64_t)diskdata.f_blocks) - availBytes;
+ requiredBytes = MIB(cfg->dvr_cleanup_threshold_low);
+ maximalBytes = MIB(cfg->dvr_cleanup_threshold_high);
- if (availBytes < requiredBytes) {
+ if (availBytes < requiredBytes || maximalBytes > usedBytes) {
LIST_FOREACH(de, &dvrentries, de_global_link) {
/* only start cleanup if we are actually writing files right now */
if (de->de_sched_state != DVR_RECORDING || !de->de_config || de->de_config != cfg)
- continue;
-
- tvhlog(LOG_WARNING, "dvr","running out of free disk space for dvr config \"%s\", required free space \"%ld MB\", current free space \"%ld MB\"",
- cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile",
- requiredBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);
+ goto checking;
+
+ if (availBytes < requiredBytes) {
+ tvhlog(LOG_WARNING, "dvr","running out of free disk space for dvr config \"%s\", required free space \"%ld MiB\", current free space \"%ld MiB\"",
+ cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile",
+ TOMIB(requiredBytes), TOMIB(availBytes));
+ } else {
+ tvhlog(LOG_WARNING, "dvr","running out of used disk space for dvr config \"%s\", required used space \"%ld MiB\", current used space \"%ld MiB\"",
+ cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile",
+ TOMIB(maximalBytes), TOMIB(usedBytes));
+ }
/* only cleanup one directory at the time as the system needs time to delete the actual files */
dvr_disk_space_cleanup(de->de_config);
dvr_disk_space_config_idx = idx + 1;
break;
}
- }
- else {
- tvhlog(LOG_DEBUG, "dvr","checking free disk space for config \"%s\" : OK",
- cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile");
+ } else {
+checking:
+ tvhlog(LOG_DEBUG, "dvr","checking free and used disk space for config \"%s\" : OK",
+ cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile");
}
}
}