| /* |
| * 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); |