From nobody Mon Feb 9 16:33:43 2026 Received: from mail-yw1-f174.google.com (mail-yw1-f174.google.com [209.85.128.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E81ED42EEA0 for ; Fri, 6 Feb 2026 19:13:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770405193; cv=none; b=AaKeZwqvZOGa1X2iBmRVJfgx7P690VgnCRbhW6snJ+rxp8+2+f7IBNZHeeS3L0wmngsqLgeFf3mr5+Lfj8xY7/bpJmNvHwFz6mIcOtwk1rxpVxxrB+xbrZriZeYC8Kpo2akm+5lUdurNCKj2mUJOWATHsMChJV5mqkIDeIch9Cg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770405193; c=relaxed/simple; bh=8DOELj3d1YF5vnkKTf0ls4Z7dKSAOmVY1KhPtSJlYno=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=G//phFgPa2Th5Vr4WzjKmdBQk0OD72GLjx/wFWOYIQWgkVXsNaTYpcgjrzpAVzlJWQKaMLqeB7AbWSRONu5rHEDXcRgK3jZQnoqbPS8RvrhvGOHn9uTc5h96xS916aukKLQkf2xQ+k0a5VZr4aSFoeqxjmLHI6yIRH9nPqfq9Zo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=dubeyko.com; spf=pass smtp.mailfrom=dubeyko.com; dkim=pass (2048-bit key) header.d=dubeyko-com.20230601.gappssmtp.com header.i=@dubeyko-com.20230601.gappssmtp.com header.b=qMUZ3y7v; arc=none smtp.client-ip=209.85.128.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=dubeyko.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=dubeyko.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=dubeyko-com.20230601.gappssmtp.com header.i=@dubeyko-com.20230601.gappssmtp.com header.b="qMUZ3y7v" Received: by mail-yw1-f174.google.com with SMTP id 00721157ae682-7950881727cso18585517b3.3 for ; Fri, 06 Feb 2026 11:13:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dubeyko-com.20230601.gappssmtp.com; s=20230601; t=1770405192; x=1771009992; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=YEXgdq0izXJsGpGxsVLN8S/Y6PSgZJ5yVJf6Tpti494=; b=qMUZ3y7v4CUyHnCq9dui3zjSqmd7Vbk3gInTWAmUREBPnJnHU+Uugjs6XhxEbztUJY 6MIAuVy7i4q17GZzyT3vqqUbzI0iZbrPR11PXUvNUqkUgSZaQmoL3dQJxn7ON80uRcCB 0MdK7XDrS0SvPeodUezrKPUZAknAXJ2383hc+mACzyvortOxi+JlfE6qcv50cH/qeGho T7WRppCGoFPndU7EJTf65pl9ipgGgsXFcIJ/XbfLbplNKBuS3zpJWq9Oj+DORvN4gUTM ABKB4YRV+55SHIx4WiiiCLtC8DkzZAIucRHCjxkTx+AH5gW3EFE+GodfGEj+IwWc30qr rCvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770405192; x=1771009992; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=YEXgdq0izXJsGpGxsVLN8S/Y6PSgZJ5yVJf6Tpti494=; b=n+hSZR8OULAozmOgwxG1W58pP++2Z7b68kqEp6gHYgCrx8kfIJ5BlrsspR7rO6wFNI UnKDg0Ym8HY5QcTcICLYc2uiJg2qTopTnc7fLA3Rvkbz7qCTqvAq5Kb6vFAsK3WoPMuM H9IOZ56NjDtjCtHrMbpPSiLjKaNGYVuJGCCsMEryQ6BJftN07yIvGYy8/ui8/KAb71Ii p6mYAkw7rUvZKZTWm0dH82899zwjhYpUS80ZcO9V2xCQW+71rdpq00EAAk5+awAxMQQ5 GM61BWnHu6dkCcFKCQGO5dm4Yl7SA6vEvM49CdEAhXZPoUb330PtC4/w/TeDJARgFDyx a5dg== X-Forwarded-Encrypted: i=1; AJvYcCVTJf8Cx33WXB0TP49N8ly1U7Vbs37S+5MQhbJxNJghb5rqVvWguSdRlUfRkLinlWLMve/bWntHB+xCOvg=@vger.kernel.org X-Gm-Message-State: AOJu0YyFtns3NEMjXW+7v/0yKKtA7IFDcqVbFv7XNfq7iksYI1xD03XR vdBBQXWCh242eJr//BxH6grjEaGyrYn/3Qhh01iJk+lfK5AlWvWD0rCOlaj9jPTDH96RuF2sQDt XpbbKr6s= X-Gm-Gg: AZuq6aKeNKJ32ztS8rGqzXoeWYgQrjv9dLP2N4Z277EwvdRtTLPK4NyKwQ2Phpp6ZTG RZvKWYo0tbc8RZAUOGT3GPgs/Sm+ouYvfsMtSJda2t0L4KPOJfpuqfDyLeVMBKCn5cJhoN/o0fU f1fQM12QAlfptWcfXgKZmGfgcAv3x+RwjjT9ExtPlUnjzaLdCFxb+PsuBZz/9fPOxpfqN+BSF86 Y6XmMw1UFKqewX8V1WnWkzqZXif+OGsfDTKrgphNR/aRu7/RzWHCwGxFyF+Pt4X4DJdDkUA4jh9 iKKLh4hUXuGbNvU/Z9dIr3J/2OaBHQyp6DHqxgi+sTTPB9rziQguxCiAjw8if/sjtEhmmnbVVsF WbHa8kR3AHV1umDFSWEieY6ljYeoOAzT5d+5IblXCHjU1ITCjE6aMLKVG/V5xShvRBF3AKrBwqM ngmL4htZN9+wrEwOpQfw5Rgc6Zy7SYZImO/cTEtT1z5A3qXUBEBp3nQ3SqN6Osqr4fVBsU7nVqZ jbe5606Jhys X-Received: by 2002:a05:690c:9c04:b0:796:1eee:b8f7 with SMTP id 00721157ae682-7961eeebef9mr45307667b3.58.1770405191900; Fri, 06 Feb 2026 11:13:11 -0800 (PST) Received: from pop-os.attlocal.net ([2600:1700:6476:1430:9fc0:ed7d:72bd:ecd1]) by smtp.gmail.com with ESMTPSA id 00721157ae682-7952a28697fsm29051277b3.50.2026.02.06.11.13.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Feb 2026 11:13:11 -0800 (PST) From: Viacheslav Dubeyko To: linux-fsdevel@vger.kernel.org, linux-mm@kvack.org, bpf@vger.kernel.org Cc: Slava.Dubeyko@ibm.com, slava@dubeyko.com, linux-kernel@vger.kernel.org Subject: [RFC PATCH v1 3/4] ml-lib: Implement simple testing character device driver Date: Fri, 6 Feb 2026 11:11:35 -0800 Message-Id: <20260206191136.2609767-4-slava@dubeyko.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260206191136.2609767-1-slava@dubeyko.com> References: <20260206191136.2609767-1-slava@dubeyko.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Implement simple testing character device driver Signed-off-by: Viacheslav Dubeyko --- lib/ml-lib/test_driver/Kconfig | 22 + lib/ml-lib/test_driver/Makefile | 5 + lib/ml-lib/test_driver/README.md | 233 ++++++++++ lib/ml-lib/test_driver/ml_lib_char_dev.c | 530 +++++++++++++++++++++++ 4 files changed, 790 insertions(+) create mode 100644 lib/ml-lib/test_driver/Kconfig create mode 100644 lib/ml-lib/test_driver/Makefile create mode 100644 lib/ml-lib/test_driver/README.md create mode 100644 lib/ml-lib/test_driver/ml_lib_char_dev.c diff --git a/lib/ml-lib/test_driver/Kconfig b/lib/ml-lib/test_driver/Kconfig new file mode 100644 index 000000000000..183fc1de57a8 --- /dev/null +++ b/lib/ml-lib/test_driver/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 + +config ML_LIB_TEST_DRIVER + tristate "ML library testing character device driver" + depends on ML_LIB + default n + help + This is a ML library testing character device driver for + testing generic ML library functionality. It provides: + + - Basic read/write operations + - IOCTL interface for device control + - Sysfs attributes for runtime information + - Procfs entry for debugging + + The driver creates a /dev/mllibdev device node that can be + used to read and write data to a kernel buffer. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mllibdev. diff --git a/lib/ml-lib/test_driver/Makefile b/lib/ml-lib/test_driver/Makef= ile new file mode 100644 index 000000000000..6444bcf8985b --- /dev/null +++ b/lib/ml-lib/test_driver/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_ML_LIB_TEST_DRIVER) +=3D ml_lib_test_dev.o + +ml_lib_test_dev-y :=3D ml_lib_char_dev.o diff --git a/lib/ml-lib/test_driver/README.md b/lib/ml-lib/test_driver/READ= ME.md new file mode 100644 index 000000000000..0bb4105c8aa4 --- /dev/null +++ b/lib/ml-lib/test_driver/README.md @@ -0,0 +1,233 @@ +# ML Library Testing Device Driver (mllibdev) + +ML library testing character device driver for the Linux kernel: +- Basic read/write operations +- IOCTL interface for device control +- Sysfs attributes for runtime information +- Procfs entry for debugging + +## Features + +### Character Device Operations +- **Open/Close**: Device can be opened and closed multiple times +- **Read**: Read data from a kernel buffer +- **Write**: Write data to a kernel buffer (1KB capacity) +- **Seek**: Support for lseek() operations + +### IOCTL Commands +- `ML_LIB_TEST_DEV_IOCRESET`: Clear the device buffer +- `ML_LIB_TEST_DEV_IOCGETSIZE`: Get current data size +- `ML_LIB_TEST_DEV_IOCSETSIZE`: Set data size + +### Sysfs Attributes +Located at `/sys/class/ml_lib_test/mllibdev`: +- `buffer_size`: Maximum buffer capacity (read-only) +- `data_size`: Current amount of data in buffer (read-only) +- `access_count`: Number of times device has been opened (read-only) +- `stats`: Comprehensive statistics (opens, reads, writes) + +### Procfs Entry +Located at `/proc/mllibdev`: Provides formatted driver information + +## Building the Driver + +### Option 1: Build as a Module + +1. Configure the kernel to build mllibdev as a module: + ```bash + make menuconfig + # Navigate to: Library routines -> ML library testing character device = driver + # Select: ML library testing character device driver + ``` + +2. Build the module: + ```bash + make -j$(nproc) M=3Dlib/ml-lib/test_driver + ``` + +### Option 2: Build into Kernel + +1. Configure the kernel: + ```bash + make menuconfig + # Navigate to: Library routines -> ML library testing character device = driver + # Select: <*> ML library testing character device driver + ``` + +2. Build the kernel: + ```bash + make -j$(nproc) + ``` + +### Option 3: Quick Module Build (Out-of-Tree) + +For quick testing, you can build just the module: + +```bash +cd lib/ml-lib/test_driver +make -C /lib/modules/$(uname -r)/build M=3D$(pwd) modules +``` + +## Loading the Driver + +If built as a module: + +```bash +# Load the module +sudo insmod /lib/modules/$(uname -r)/build/lib/ml-lib/test_driver/ml_lib_t= est_dev.ko + +# Verify it's loaded +sudo lsmod | grep ml_lib_test_dev + +# Check kernel messages +sudo dmesg | tail -20 +``` + +You should see messages like: +``` +ml_lib_test_dev: Initializing driver +ml_lib_test_dev: Device number allocated: XXX:0 +ml_lib_test_dev: Driver initialized successfully +ml_lib_test_dev: Device created at /dev/mllibdev +ml_lib_test_dev: Proc entry created at /proc/mllibdev +``` + +## Testing the Driver + +### Quick Manual Test + +```bash +# Write data to the device +sudo su +echo "Hello, kernel!" > /dev/mllibdev + +# Read data back +sudo su +cat /dev/mllibdev + +# Check sysfs attributes +cat /sys/class/ml_lib_test/mllibdev/stats + +# Check proc entry +cat /proc/mllibdev +``` + +### Using the Test Program + +1. Compile the test program: + ```bash + cd lib/ml-lib/test_driver/test_application + gcc -o ml_lib_test_dev test_ml_lib_char_dev.c + ``` + +2. Run the test program: + ```bash + sudo ./ml_lib_test_dev + ``` + +The test program will: +- Open the device +- Write test data +- Read the data back +- Test all IOCTL commands +- Display sysfs attributes +- Show procfs information + +### Example Test Output + +``` +ML Library Testing Device Driver Test Program +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Device opened successfully: /dev/mllibdev + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Write Test =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Successfully wrote 62 bytes +Data: "Hello from userspace! This is a test of the mllibdev driver." + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Read Test =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Successfully read 62 bytes +Data: "Hello from userspace! This is a test of the mllibdev driver." + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D IOCTL Tests =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Current data size: 62 bytes +Set data size to: 50 bytes +Verified new size: 50 bytes +Buffer reset successfully +Size after reset: 0 bytes + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Sysfs Attributes =3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D +buffer_size: 1024 +data_size: 0 +access_count: 1 + +stats: Opens: 1 +Reads: 1 +Writes: 1 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Procfs Information =3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D +ML Library Testing Device Driver Information +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D +Device name: mllibdev +Buffer size: 1024 bytes +Data size: 0 bytes +Access count: 1 +Read count: 1 +Write count: 1 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Final Test =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +All tests completed successfully! +``` + +## Unloading the Driver + +```bash +# Remove the module +sudo rmmod ml_lib_test_dev + +# Verify it's unloaded +sudo lsmod | grep ml_lib_test_dev + +# Check cleanup messages +sudo dmesg | tail -10 +``` + +## Troubleshooting + +### Device node doesn't exist +```bash +# Check if udev created the device +ls -l /dev/mllibdev + +# Manually create if needed (shouldn't be necessary) +sudo mknod /dev/mllibdev c MAJOR MINOR +``` + +### Permission denied +```bash +# Run commands with sudo +sudo cat /dev/mllibdev + +# Or change permissions +sudo chmod 666 /dev/mllibdev +``` + +### Module won't load +```bash +# Check kernel messages for errors +dmesg | tail -20 + +# Verify module dependencies +modinfo lib/ml-lib/test_driver/ml_lib_test_dev.ko +``` + +## License + +This driver is licensed under GPL-2.0. + +## Author + +Viacheslav Dubeyko + +## Version + +1.0 diff --git a/lib/ml-lib/test_driver/ml_lib_char_dev.c b/lib/ml-lib/test_dri= ver/ml_lib_char_dev.c new file mode 100644 index 000000000000..b2d6e27ece28 --- /dev/null +++ b/lib/ml-lib/test_driver/ml_lib_char_dev.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Machine Learning (ML) library + * Testing Character Device Driver + * + * Copyright (C) 2025-2026 Viacheslav Dubeyko + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "mllibdev" +#define CLASS_NAME "ml_lib_test" +#define BUFFER_SIZE 1024 + +/* IOCTL commands */ +#define ML_LIB_TEST_DEV_IOC_MAGIC 'M' +#define ML_LIB_TEST_DEV_IOCRESET _IO(ML_LIB_TEST_DEV_IOC_MAGIC, 0) +#define ML_LIB_TEST_DEV_IOCGETSIZE _IOR(ML_LIB_TEST_DEV_IOC_MAGIC, 1, int) +#define ML_LIB_TEST_DEV_IOCSETSIZE _IOW(ML_LIB_TEST_DEV_IOC_MAGIC, 2, int) + +/* Device data structure */ +struct ml_lib_test_dev_data { + struct cdev cdev; + struct device *device; + char *dataset_buf; + size_t dataset_buf_size; + size_t dataset_size; + char *recommendations_buf; + size_t recommendations_buf_size; + size_t recommendations_size; + struct mutex lock; + unsigned long access_count; + unsigned long read_count; + unsigned long write_count; + + struct ml_lib_model *ml_model1; +}; + +#define ML_MODEL_1_NAME "ml_model1" + +static +int ml_lib_test_dev_extract_dataset(struct ml_lib_model *ml_model, + struct ml_lib_dataset *dataset); + +static struct ml_lib_dataset_operations ml_lib_test_dev_dataset_ops =3D { + .extract =3D ml_lib_test_dev_extract_dataset, +}; + +static dev_t dev_number; +static struct class *ml_lib_test_dev_class; +static struct ml_lib_test_dev_data *dev_data; +static struct proc_dir_entry *proc_entry; + +/* ML model operations */ +static +int ml_lib_test_dev_extract_dataset(struct ml_lib_model *ml_model, + struct ml_lib_dataset *dataset) +{ + struct ml_lib_test_dev_data *data =3D + (struct ml_lib_test_dev_data *)ml_model->parent->private; + u8 pattern; + + mutex_lock(&data->lock); + get_random_bytes(&pattern, 1); + memset(data->dataset_buf, pattern, data->dataset_buf_size); + data->dataset_size =3D data->dataset_buf_size; + atomic_set(&dataset->type, ML_LIB_MEMORY_STREAM_DATASET); + atomic_set(&dataset->state, ML_LIB_DATASET_CLEAN); + dataset->allocated_size =3D data->dataset_buf_size; + dataset->portion_offset =3D 0; + dataset->portion_size =3D data->dataset_buf_size; + mutex_unlock(&data->lock); + + return 0; +} + +/* File operations */ +static int ml_lib_test_dev_open(struct inode *inode, struct file *file) +{ + struct ml_lib_test_dev_data *data =3D container_of(inode->i_cdev, + struct ml_lib_test_dev_data, + cdev); + + file->private_data =3D data; + + mutex_lock(&data->lock); + data->access_count++; + mutex_unlock(&data->lock); + + pr_info("ml_lib_test_dev: Device opened (total opens: %lu)\n", + data->access_count); + + return 0; +} + +static int ml_lib_test_dev_release(struct inode *inode, struct file *file) +{ + pr_info("ml_lib_test_dev: Device closed\n"); + return 0; +} + +static ssize_t ml_lib_test_dev_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct ml_lib_test_dev_data *data =3D file->private_data; + size_t to_read; + int ret; + + mutex_lock(&data->lock); + + if (*ppos >=3D data->dataset_size) { + mutex_unlock(&data->lock); + return 0; + } + + to_read =3D min(count, data->dataset_size - (size_t)*ppos); + + ret =3D copy_to_user(buf, data->dataset_buf + *ppos, to_read); + if (ret) { + mutex_unlock(&data->lock); + return -EFAULT; + } + + *ppos +=3D to_read; + data->read_count++; + + mutex_unlock(&data->lock); + + pr_info("ml_lib_test_dev: Read %zu bytes\n", to_read); + + return to_read; +} + +static ssize_t ml_lib_test_dev_write(struct file *file, const char __user = *buf, + size_t count, loff_t *ppos) +{ + struct ml_lib_test_dev_data *data =3D file->private_data; + size_t to_write; + int ret; + + mutex_lock(&data->lock); + + if (*ppos >=3D data->recommendations_buf_size) { + mutex_unlock(&data->lock); + return -ENOSPC; + } + + to_write =3D min(count, data->recommendations_buf_size - (size_t)*ppos); + + ret =3D copy_from_user(data->recommendations_buf + *ppos, buf, to_write); + if (ret) { + mutex_unlock(&data->lock); + return -EFAULT; + } + + *ppos +=3D to_write; + if (*ppos > data->recommendations_size) + data->recommendations_size =3D *ppos; + + data->write_count++; + + mutex_unlock(&data->lock); + + pr_info("ml_lib_test_dev: Wrote %zu bytes\n", to_write); + + return to_write; +} + +static long ml_lib_test_dev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ml_lib_test_dev_data *data =3D file->private_data; + int size; + + switch (cmd) { + case ML_LIB_TEST_DEV_IOCRESET: + mutex_lock(&data->lock); + memset(data->dataset_buf, + 0, data->dataset_buf_size); + data->dataset_size =3D 0; + memset(data->recommendations_buf, + 0, data->recommendations_buf_size); + data->recommendations_size =3D 0; + mutex_unlock(&data->lock); + pr_info("ml_lib_test_dev: Buffer reset via IOCTL\n"); + break; + + case ML_LIB_TEST_DEV_IOCGETSIZE: + mutex_lock(&data->lock); + size =3D data->dataset_size; + mutex_unlock(&data->lock); + if (copy_to_user((int __user *)arg, &size, sizeof(size))) + return -EFAULT; + break; + + case ML_LIB_TEST_DEV_IOCSETSIZE: + if (copy_from_user(&size, (int __user *)arg, sizeof(size))) + return -EFAULT; + if (size < 0 || size > data->recommendations_buf_size) + return -EINVAL; + mutex_lock(&data->lock); + data->recommendations_size =3D size; + mutex_unlock(&data->lock); + pr_info("ml_lib_test_dev: Data size set to %d via IOCTL\n", size); + break; + + default: + return -ENOTTY; + } + + return 0; +} + +static const struct file_operations ml_lib_test_dev_fops =3D { + .owner =3D THIS_MODULE, + .open =3D ml_lib_test_dev_open, + .release =3D ml_lib_test_dev_release, + .read =3D ml_lib_test_dev_read, + .write =3D ml_lib_test_dev_write, + .unlocked_ioctl =3D ml_lib_test_dev_ioctl, + .llseek =3D default_llseek, +}; + +/* Sysfs attributes */ +static ssize_t buffer_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ml_lib_test_dev_data *data =3D dev_get_drvdata(dev); + + return sprintf(buf, "%zu\n", data->dataset_buf_size); +} + +static ssize_t data_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ml_lib_test_dev_data *data =3D dev_get_drvdata(dev); + size_t size; + + mutex_lock(&data->lock); + size =3D data->dataset_size; + mutex_unlock(&data->lock); + + return sprintf(buf, "%zu\n", size); +} + +static ssize_t access_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ml_lib_test_dev_data *data =3D dev_get_drvdata(dev); + + return sprintf(buf, "%lu\n", data->access_count); +} + +static ssize_t stats_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ml_lib_test_dev_data *data =3D dev_get_drvdata(dev); + + return sprintf(buf, "Opens: %lu\nReads: %lu\nWrites: %lu\n", + data->access_count, data->read_count, + data->write_count); +} + +static DEVICE_ATTR_RO(buffer_size); +static DEVICE_ATTR_RO(data_size); +static DEVICE_ATTR_RO(access_count); +static DEVICE_ATTR_RO(stats); + +static struct attribute *ml_lib_test_dev_attrs[] =3D { + &dev_attr_buffer_size.attr, + &dev_attr_data_size.attr, + &dev_attr_access_count.attr, + &dev_attr_stats.attr, + NULL, +}; + +static const struct attribute_group ml_lib_test_dev_attr_group =3D { + .attrs =3D ml_lib_test_dev_attrs, +}; + +/* Procfs operations */ +static int ml_lib_test_dev_proc_show(struct seq_file *m, void *v) +{ + struct ml_lib_test_dev_data *data =3D dev_data; + + seq_printf(m, "ML Library Testing Device Driver Information\n"); + seq_printf(m, "=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D\n"); + seq_printf(m, "Device name: %s\n", DEVICE_NAME); + seq_printf(m, "Buffer size: %zu bytes\n", data->dataset_buf_size); + seq_printf(m, "Data size: %zu bytes\n", data->dataset_size); + seq_printf(m, "Access count: %lu\n", data->access_count); + seq_printf(m, "Read count: %lu\n", data->read_count); + seq_printf(m, "Write count: %lu\n", data->write_count); + + return 0; +} + +static int ml_lib_test_dev_proc_open(struct inode *inode, struct file *fil= e) +{ + return single_open(file, ml_lib_test_dev_proc_show, NULL); +} + +static const struct proc_ops ml_lib_test_dev_proc_ops =3D { + .proc_open =3D ml_lib_test_dev_proc_open, + .proc_read =3D seq_read, + .proc_lseek =3D seq_lseek, + .proc_release =3D single_release, +}; + +/* Module initialization */ +static int __init ml_lib_test_dev_init(void) +{ + struct ml_lib_model_options *options; + int ret; + + pr_info("ml_lib_test_dev: Initializing driver\n"); + + /* Allocate device data */ + dev_data =3D kzalloc(sizeof(struct ml_lib_test_dev_data), GFP_KERNEL); + if (!dev_data) + return -ENOMEM; + + /* Allocate dataset buffer */ + dev_data->dataset_buf =3D kzalloc(BUFFER_SIZE, GFP_KERNEL); + if (!dev_data->dataset_buf) { + ret =3D -ENOMEM; + goto err_free_data; + } + + dev_data->dataset_buf_size =3D BUFFER_SIZE; + dev_data->dataset_size =3D 0; + + /* Allocate recomendations buffer */ + dev_data->recommendations_buf =3D kzalloc(BUFFER_SIZE, GFP_KERNEL); + if (!dev_data->recommendations_buf) { + ret =3D -ENOMEM; + goto err_free_dataset_buffer; + } + + dev_data->recommendations_buf_size =3D BUFFER_SIZE; + dev_data->recommendations_size =3D 0; + + mutex_init(&dev_data->lock); + + /* Allocate device number */ + ret =3D alloc_chrdev_region(&dev_number, 0, 1, DEVICE_NAME); + if (ret < 0) { + pr_err("ml_lib_test_dev: Failed to allocate device number\n"); + goto err_free_recommendations_buffer; + } + + pr_info("ml_lib_test_dev: Device number allocated: %d:%d\n", + MAJOR(dev_number), MINOR(dev_number)); + + /* Create device class */ + ml_lib_test_dev_class =3D class_create(CLASS_NAME); + if (IS_ERR(ml_lib_test_dev_class)) { + ret =3D PTR_ERR(ml_lib_test_dev_class); + pr_err("ml_lib_test_dev: Failed to create class\n"); + goto err_unregister_chrdev; + } + + /* Initialize and add cdev */ + cdev_init(&dev_data->cdev, &ml_lib_test_dev_fops); + dev_data->cdev.owner =3D THIS_MODULE; + + ret =3D cdev_add(&dev_data->cdev, dev_number, 1); + if (ret < 0) { + pr_err("ml_lib_test_dev: Failed to add cdev\n"); + goto err_class_destroy; + } + + /* Create device */ + dev_data->device =3D device_create(ml_lib_test_dev_class, + NULL, dev_number, + dev_data, DEVICE_NAME); + if (IS_ERR(dev_data->device)) { + ret =3D PTR_ERR(dev_data->device); + pr_err("ml_lib_test_dev: Failed to create device\n"); + goto err_cdev_del; + } + + /* Create sysfs attributes */ + ret =3D sysfs_create_group(&dev_data->device->kobj, + &ml_lib_test_dev_attr_group); + if (ret < 0) { + pr_err("ml_lib_test_dev: Failed to create sysfs group\n"); + goto err_device_destroy; + } + + /* Create procfs entry */ + proc_entry =3D proc_create(DEVICE_NAME, 0444, NULL, + &ml_lib_test_dev_proc_ops); + if (!proc_entry) { + pr_err("ml_lib_test_dev: Failed to create proc entry\n"); + ret =3D -ENOMEM; + goto err_sysfs_remove; + } + + dev_data->ml_model1 =3D allocate_ml_model(sizeof(struct ml_lib_model), + GFP_KERNEL); + if (IS_ERR(dev_data->ml_model1)) { + ret =3D PTR_ERR(dev_data->ml_model1); + pr_err("ml_lib_test_dev: Failed to allocate ML model\n"); + goto err_procfs_remove; + } else if (!dev_data->ml_model1) { + ret =3D -ENOMEM; + pr_err("ml_lib_test_dev: Failed to allocate ML model\n"); + goto err_procfs_remove; + } + + ret =3D ml_model_create(dev_data->ml_model1, CLASS_NAME, + ML_MODEL_1_NAME, &dev_data->device->kobj); + if (ret < 0) { + pr_err("ml_lib_test_dev: Failed to create ML model\n"); + goto err_ml_model_free; + } + + dev_data->ml_model1->parent->private =3D dev_data; + dev_data->ml_model1->model_ops =3D NULL; + dev_data->ml_model1->dataset_ops =3D &ml_lib_test_dev_dataset_ops; + + options =3D allocate_ml_model_options(sizeof(struct ml_lib_model_options), + GFP_KERNEL); + if (IS_ERR(options)) { + ret =3D PTR_ERR(options); + pr_err("ml_lib_test_dev: Failed to allocate ML model options\n"); + goto err_ml_model_destroy; + } else if (!options) { + ret =3D -ENOMEM; + pr_err("ml_lib_test_dev: Failed to allocate ML model options\n"); + goto err_ml_model_destroy; + } + + ret =3D ml_model_init(dev_data->ml_model1, options); + if (ret < 0) { + pr_err("ml_lib_test_dev: Failed to init ML model\n"); + goto err_ml_model_options_free; + } + + pr_info("ml_lib_test_dev: Driver initialized successfully\n"); + pr_info("ml_lib_test_dev: Device created at /dev/%s\n", + DEVICE_NAME); + pr_info("ml_lib_test_dev: Proc entry created at /proc/%s\n", + DEVICE_NAME); + + return 0; + +err_ml_model_options_free: + free_ml_model_options(options); +err_ml_model_destroy: + ml_model_destroy(dev_data->ml_model1); +err_ml_model_free: + free_ml_model(dev_data->ml_model1); +err_procfs_remove: + proc_remove(proc_entry); +err_sysfs_remove: + sysfs_remove_group(&dev_data->device->kobj, + &ml_lib_test_dev_attr_group); +err_device_destroy: + device_destroy(ml_lib_test_dev_class, dev_number); +err_cdev_del: + cdev_del(&dev_data->cdev); +err_class_destroy: + class_destroy(ml_lib_test_dev_class); +err_unregister_chrdev: + unregister_chrdev_region(dev_number, 1); +err_free_recommendations_buffer: + kfree(dev_data->recommendations_buf); +err_free_dataset_buffer: + kfree(dev_data->dataset_buf); +err_free_data: + kfree(dev_data); + return ret; +} + +/* Module cleanup */ +static void __exit ml_lib_test_dev_exit(void) +{ + pr_info("ml_lib_test_dev: Cleaning up driver\n"); + + /* Destroy ML model */ + ml_model_destroy(dev_data->ml_model1); + free_ml_model(dev_data->ml_model1); + + /* Remove procfs entry */ + proc_remove(proc_entry); + + /* Remove sysfs attributes */ + sysfs_remove_group(&dev_data->device->kobj, + &ml_lib_test_dev_attr_group); + + /* Destroy device */ + device_destroy(ml_lib_test_dev_class, dev_number); + + /* Delete cdev */ + cdev_del(&dev_data->cdev); + + /* Destroy class */ + class_destroy(ml_lib_test_dev_class); + + /* Unregister device number */ + unregister_chrdev_region(dev_number, 1); + + /* Free buffers */ + kfree(dev_data->recommendations_buf); + kfree(dev_data->dataset_buf); + kfree(dev_data); + + pr_info("ml_lib_test_dev: Driver removed successfully\n"); +} + +module_init(ml_lib_test_dev_init); +module_exit(ml_lib_test_dev_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Viacheslav Dubeyko "); +MODULE_DESCRIPTION("ML libraray testing character device driver"); +MODULE_VERSION("1.0"); --=20 2.34.1