blob: 96a3f0959c93958072348e6731cf14b4a0630ebf [file] [log] [blame] [raw]
/*
* Copyright 2015-2025 Rivoreo
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/malloc.h>
#include <sys/fcntl.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/devicestat.h>
#include <sys/kthread.h>
#include <sys/unistd.h>
#include <sys/proc.h>
#include <sys/mutex.h>
#include <sys/lock.h>
#include <sys/condvar.h>
#include <sys/syscallsubr.h>
#include <sys/bio.h>
#include <geom/geom.h>
#include <geom/gate/g_gate.h>
#define CHECK_INTERVAL 20
#define INACTIVITY_MAX_COUNT 4
static SYSCTL_NODE(_kern, OID_AUTO, ggate_inactivity_expiration, CTLFLAG_RW, 0, "ggate_inactivity_expiration control");
static int ggate_inactivity_expiration_debug = 0;
SYSCTL_INT(_kern_ggate_inactivity_expiration, OID_AUTO, debug, CTLFLAG_RW, &ggate_inactivity_expiration_debug, 0, "Whether to print debug messages");
static struct mtx mtx;
static struct cv cv;
static int running;
static int still_running() {
mtx_lock(&mtx);
int r = running;
cv_broadcast(&cv);
mtx_unlock(&mtx);
return r;
}
static int destroy_ggate(int unit) {
struct thread *td = curthread;
int e = kern_openat(td, AT_FDCWD, "/dev/" G_GATE_CTL_NAME, UIO_SYSSPACE, O_RDWR, 0);
if(e) {
printf("Failed to open %s, error %d\n", G_GATE_CTL_NAME, e);
return e;
}
int fd = td->td_retval[0];
struct g_gate_ctl_destroy ggio = {
.gctl_version = G_GATE_VERSION, .gctl_unit = unit, .gctl_force = 1
};
e = kern_ioctl(td, fd, G_GATE_CMD_DESTROY, (caddr_t)&ggio);
if(e) printf("ioctl G_GATE_CMD_DESTROY error %d\n", e);
kern_close(td, fd);
return e;
}
static int inactivity_check_count;
static uint64_t read_operations, write_operations;
static uint64_t read_bytes, write_bytes;
static void ggate_inactivity_expiration_worker(void *a) {
while(still_running()) {
#if 1
tsleep(&running, 0, "w", CHECK_INTERVAL * hz);
#else
mtx_lock(&mtx);
mtx_sleep(&running, &mtx, 0, "w", CHECK_INTERVAL * hz);
mtx_unlock(&mtx);
#endif
g_topology_lock();
struct g_provider *ggate0 = g_provider_by_name("ggate0");
g_topology_unlock();
if(!ggate0) continue;
if(ggate_inactivity_expiration_debug) printf("ggate0->stat = %p\n", ggate0->stat);
if(!ggate0->stat) continue;
if(ggate_inactivity_expiration_debug) {
printf("ggate0:\n read operations %llu, write operations %llu\n read bytes %llu, write bytes %llu\n",
(unsigned long long int)ggate0->stat->operations[DEVSTAT_READ],
(unsigned long long int)ggate0->stat->operations[DEVSTAT_WRITE],
(unsigned long long int)ggate0->stat->bytes[DEVSTAT_READ],
(unsigned long long int)ggate0->stat->bytes[DEVSTAT_WRITE]);
}
if(read_operations == ggate0->stat->operations[DEVSTAT_READ] &&
write_operations == ggate0->stat->operations[DEVSTAT_WRITE] &&
read_bytes == ggate0->stat->bytes[DEVSTAT_READ] &&
write_bytes == ggate0->stat->bytes[DEVSTAT_WRITE]) {
if(++inactivity_check_count > INACTIVITY_MAX_COUNT) {
printf("ggate0 remains inactive for approximately %d sec, destroying\n",
CHECK_INTERVAL * inactivity_check_count);
int e = destroy_ggate(0);
if(!e) {
read_operations = 0;
write_operations = 0;
read_bytes = 0;
write_bytes = 0;
}
}
} else {
inactivity_check_count = 0;
read_operations = ggate0->stat->operations[DEVSTAT_READ];
write_operations = ggate0->stat->operations[DEVSTAT_WRITE];
read_bytes = ggate0->stat->bytes[DEVSTAT_READ];
write_bytes = ggate0->stat->bytes[DEVSTAT_WRITE];
}
}
if(ggate_inactivity_expiration_debug) printf("ggate_inactivity_expiration_worker exiting\n");
kthread_exit();
}
static int ggate_inactivity_expiration_reset_handler(SYSCTL_HANDLER_ARGS) {
unsigned int value = 0;
int e = sysctl_handle_int(oidp, &value, 0, req);
if(e) return e;
if(!req->newptr) return 0;
if(!value) return 0;
if(ggate_inactivity_expiration_debug) printf("Resetting ggate inactivity check counter\n");
inactivity_check_count = 0;
return 0;
}
SYSCTL_OID(_kern_ggate_inactivity_expiration, OID_AUTO, reset, CTLTYPE_UINT | CTLFLAG_WR, SYSCTL_NULL_INT_PTR, 0, ggate_inactivity_expiration_reset_handler, "I", "Reset inactivity check counter");
static struct thread *worker_thread;
static int
ggate_inactivity_expiration_modevent(module_t __unused mod, int type, void *__unused data)
{
switch(type) {
case MOD_LOAD:
running = 1;
mtx_init(&mtx, "ggate_inactivity_expiration_mutex", NULL, MTX_DEF);
cv_init(&cv, "ggate_inactivity_expiration_cv");
return kthread_add(ggate_inactivity_expiration_worker, NULL, NULL, &worker_thread, 0, 0, "ggate_inactivity_expiration_worker");
case MOD_SHUTDOWN:
break;
case MOD_UNLOAD:
mtx_lock(&mtx);
running = 0;
wakeup(&running);
cv_wait(&cv, &mtx);
mtx_unlock(&mtx);
mtx_destroy(&mtx);
cv_destroy(&cv);
break;
default:
return (EOPNOTSUPP);
}
return (0);
}
DEV_MODULE(ggate_inactivity_expiration, ggate_inactivity_expiration_modevent, NULL);