diff --git a/.devcontainer.json b/.devcontainer.json new file mode 100644 index 0000000..73c7dd8 --- /dev/null +++ b/.devcontainer.json @@ -0,0 +1,18 @@ +{ + "name": "Kernel Module Development", + "dockerFile": "Dockerfile.devcontainer", + "workspaceFolder": "/workspace", + "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached", + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.makefile-tools", + "ms-azuretools.vscode-docker", + "ms-vscode.cmake-tools" + ] + } + }, + "remoteUser": "root" +} + \ No newline at end of file diff --git a/.github/workflows/test-kernel-module.yaml b/.github/workflows/test-kernel-module.yaml new file mode 100644 index 0000000..e257712 --- /dev/null +++ b/.github/workflows/test-kernel-module.yaml @@ -0,0 +1,153 @@ +name: Test Kernel Module +on: workflow_dispatch # Manual trigger for testing + +permissions: + id-token: write # Required for requesting the JWT + +jobs: + start-runner: + name: Start EC2 runner + runs-on: ubuntu-latest + outputs: + label: ${{ steps.start-ec2-runner.outputs.label }} + ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }} + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + aws-region: ${{ secrets.AWS_REGION }} + role-session-name: github-runner-session + + - name: Start EC2 runner + id: start-ec2-runner + uses: machulav/ec2-github-runner@v2.3.8 + with: + mode: start + github-token: ${{ secrets.REPO_ADMIN_TOKEN }} + ec2-image-id: ami-0884d2865dbe9de4b # Ubuntu 22.04 LTS in us-east-2 + ec2-instance-type: t3.large + market-type: spot + subnet-id: ${{ secrets.AWS_SUBNET_ID }} + security-group-id: ${{ secrets.AWS_SECURITY_GROUP_ID }} + aws-resource-tags: > + [ + {"Key": "Name", "Value": "github-runner"}, + {"Key": "Repository", "Value": "${{ github.repository }}"}, + {"Key": "Workflow", "Value": "${{ github.workflow }}"}, + {"Key": "RunId", "Value": "${{ github.run_id }}"}, + {"Key": "RunNumber", "Value": "${{ github.run_number }}"}, + {"Key": "SHA", "Value": "${{ github.sha }}"}, + {"Key": "Branch", "Value": "${{ github.ref_name }}"}, + {"Key": "Actor", "Value": "${{ github.actor }}"} + ] + + test-module: + needs: start-runner + runs-on: ${{ needs.start-runner.outputs.label }} + timeout-minutes: 10 # Add timeout in case system hangs + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Disable IPv6 + run: | + # Disable IPv6 via sysctl + sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1 + sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1 + sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1 + + # Force apt to use IPv4 + echo 'Acquire::ForceIPv4 "true";' | sudo tee /etc/apt/apt.conf.d/99force-ipv4 + + - name: Configure apt to use HTTPS + run: | + # Update all archive URLs to use HTTPS + sudo sed -i 's/http:/https:/g' /etc/apt/sources.list + + # Install apt-transport-https (might fail initially, hence the || true) + sudo apt-get update || true + sudo apt-get install -y apt-transport-https ca-certificates + + # Update again with HTTPS now configured + sudo apt-get update + + - name: Install build dependencies + run: | + # Install base dependencies + sudo apt-get install -y build-essential linux-headers-$(uname -r) + + - name: Build kernel module + working-directory: module + run: | + # Try to compile and capture the warning message + make 2>&1 | tee compile_output.txt || true + + # Extract gcc version from the warning message + KERNEL_GCC_VERSION=$(grep "The kernel was built by:" compile_output.txt | grep -oP 'gcc-\K\d+' || echo "") + echo "Detected kernel compiler version: ${KERNEL_GCC_VERSION}" + + # Install specific gcc version if detected + if [ ! -z "$KERNEL_GCC_VERSION" ]; then + echo "Installing gcc-${KERNEL_GCC_VERSION}" + sudo apt-get install -y gcc-${KERNEL_GCC_VERSION} + + # Configure as default gcc + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${KERNEL_GCC_VERSION} 100 + sudo update-alternatives --set gcc /usr/bin/gcc-${KERNEL_GCC_VERSION} + else + echo "Warning: Could not detect kernel compiler version" + fi + + # Verify gcc version + gcc --version + + # Now try the actual build + make + + ls -l build/memory_collector.ko + + - name: Load and test module + working-directory: module + run: | + # Load module + sudo insmod build/memory_collector.ko + + # Verify module is loaded + lsmod | grep memory_collector + + # Check kernel logs for module initialization + dmesg | grep "Memory Collector" || true + + # Unload module + sudo rmmod memory_collector + + # Verify module unloaded successfully + if lsmod | grep -q memory_collector; then + echo "Error: Module still loaded" + exit 1 + fi + + # Check kernel logs for cleanup message + dmesg | grep "Memory Collector" || true + + stop-runner: + name: Stop EC2 runner + needs: [start-runner, test-module] + runs-on: ubuntu-latest + if: always() # Run even if previous jobs fail + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + aws-region: ${{ secrets.AWS_REGION }} + role-session-name: github-runner-session + + - name: Stop EC2 runner + uses: machulav/ec2-github-runner@v2.3.8 + with: + mode: stop + github-token: ${{ secrets.REPO_ADMIN_TOKEN }} + label: ${{ needs.start-runner.outputs.label }} + ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }} \ No newline at end of file diff --git a/Dockerfile.devcontainer b/Dockerfile.devcontainer new file mode 100644 index 0000000..9f9513c --- /dev/null +++ b/Dockerfile.devcontainer @@ -0,0 +1,21 @@ +FROM ubuntu:latest + +# Avoid prompts during package installation +ENV DEBIAN_FRONTEND=noninteractive + +# Update and install essential build tools and kernel headers +RUN apt-get update && apt-get install -y \ + build-essential \ + linux-headers-6.8.0-52-generic \ + linux-image-6.8.0-52-generic \ + git \ + vim \ + curl \ + kmod \ + && rm -rf /var/lib/apt/lists/* + +# Set the working directory +WORKDIR /workspace + +# Keep container running +CMD ["sleep", "infinity"] \ No newline at end of file diff --git a/module/Makefile b/module/Makefile new file mode 100644 index 0000000..8215291 --- /dev/null +++ b/module/Makefile @@ -0,0 +1,33 @@ +obj-m += memory_collector.o + +# Check if KVERSION is provided on command line +ifdef KVERSION + # Use provided version + KERNEL_VERSION := $(KVERSION) +else + # Check if we're in the dev container by looking for a specific file + # that would only exist in the container (like our Dockerfile) + ifneq (,$(wildcard /.dockerenv)) + # In container - use the version we installed + KERNEL_VERSION := 6.8.0-52-generic + else + # Not in container - use current kernel + KERNEL_VERSION := $(shell uname -r) + endif +endif + +KDIR := /lib/modules/$(KERNEL_VERSION)/build +BUILD_DIR := $(PWD)/build + +all: | $(BUILD_DIR) + @echo "Building for kernel version: $(KERNEL_VERSION)" + $(MAKE) -C $(KDIR) M=$(BUILD_DIR) src=$(PWD) modules + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +clean: + $(MAKE) -C $(KDIR) M=$(BUILD_DIR) src=$(PWD) clean + rm -rf $(BUILD_DIR) + +.PHONY: all clean \ No newline at end of file diff --git a/module/README.md b/module/README.md new file mode 100644 index 0000000..1adba79 --- /dev/null +++ b/module/README.md @@ -0,0 +1,78 @@ +# Memory Collector Kernel Module + +This kernel module provides low-level memory subsystem monitoring capabilities for the Memory Collector project. + +## Building the Module + +### Prerequisites + +- Linux kernel headers for your running kernel +- Build tools (gcc, make) + +On Ubuntu/Debian: +```bash +sudo apt-get install linux-headers-$(uname -r) build-essential +``` + +On RHEL/Fedora: +```bash +sudo dnf install kernel-devel kernel-headers gcc make +``` + +### Manual Build + +To build the module directly: + +```bash +make +``` + +This will create `memory_collector.ko` which can be loaded with: + +```bash +sudo insmod memory_collector.ko +``` + +To unload: +```bash +sudo rmmod memory_collector +``` + +### DKMS Installation + +For automatic rebuilding when the kernel updates: + +1. Install DKMS: +```bash +# Ubuntu/Debian +sudo apt-get install dkms + +# RHEL/Fedora +sudo dnf install dkms +``` + +2. Install the module through DKMS: +```bash +sudo dkms add . +sudo dkms install memory-collector/1.0 +``` + +The module will now automatically rebuild when your kernel is updated. + +To uninstall: +```bash +sudo dkms remove memory-collector/1.0 --all +``` + +## Verification + +After loading the module, verify it's running: + +```bash +lsmod | grep memory_collector +``` + +Check kernel logs for module status: +```bash +dmesg | tail +``` \ No newline at end of file diff --git a/module/dkms.conf b/module/dkms.conf new file mode 100644 index 0000000..1e4d68f --- /dev/null +++ b/module/dkms.conf @@ -0,0 +1,7 @@ +PACKAGE_NAME="memory-collector" +PACKAGE_VERSION="1.0" +BUILT_MODULE_NAME[0]="memory_collector" +DEST_MODULE_LOCATION[0]="/kernel/drivers/misc" +AUTOINSTALL="yes" +MAKE[0]="make" +CLEAN="make clean" \ No newline at end of file diff --git a/module/memory_collector.c b/module/memory_collector.c new file mode 100644 index 0000000..4d0fb02 --- /dev/null +++ b/module/memory_collector.c @@ -0,0 +1,22 @@ +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Memory Collector Project"); +MODULE_DESCRIPTION("Memory subsystem monitoring for Kubernetes"); +MODULE_VERSION("1.0"); + +static int __init memory_collector_init(void) +{ + printk(KERN_INFO "Memory Collector: module loaded\n"); + return 0; +} + +static void __exit memory_collector_exit(void) +{ + printk(KERN_INFO "Memory Collector: module unloaded\n"); +} + +module_init(memory_collector_init); +module_exit(memory_collector_exit); \ No newline at end of file