diff --git a/work/tp2/pipebis.c b/work/tp2/pipebis.c index 43e97c4b49f356faa1ef3d0c9b5fa2e8d33ff438..2efe4d15be0db99aa83298d1ef3982e3d30937bc 100644 --- a/work/tp2/pipebis.c +++ b/work/tp2/pipebis.c @@ -176,7 +176,8 @@ static int device_release(struct inode *inode, struct file *file) /* Called when a process, which already opened the dev file, attempts to * read from it. */ -static int last_writen_bytes; +static int last_writen_bytes = 0; +static int last_read_bytes = 0; static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */ char __user *buffer, /* buffer to fill with data */ size_t length, /* length of the buffer */ @@ -188,8 +189,8 @@ static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */ /* Actually put the data into the buffer */ while (asee_buf_count > 0) { - if (last_writen_bytes >= asee_buf_size) { - last_writen_bytes = 0; + if (last_read_bytes >= asee_buf_size) { + last_read_bytes = 0; } /* The buffer is in the user data segment, not the kernel * segment so "*" assignment won't work. We have to use @@ -197,15 +198,14 @@ static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */ * the user data segment. */ - put_user(*(msg_ptr + last_writen_bytes), buffer++); + put_user(*(msg_ptr + last_read_bytes), buffer++); // Try to clear the read - *(msg_ptr + last_writen_bytes) = '\0'; - last_writen_bytes++; + *(msg_ptr + last_read_bytes) = '\0'; + last_read_bytes++; asee_buf_count--; bytes_read++; } - - *offset = last_writen_bytes; + *offset = last_read_bytes; /* Most read functions return the number of bytes put into the buffer. */ return bytes_read; @@ -226,8 +226,11 @@ static ssize_t device_write(struct file *filp, const char __user *buff, get_user(*(msg + current_bytes), buff + cpt); current_bytes++; bytes_writen++; - asee_buf_count++; + if (asee_buf_count < asee_buf_size) { + asee_buf_count++; + } } + last_read_bytes = (last_read_bytes + bytes_writen) % asee_buf_count; last_writen_bytes = current_bytes; *off = last_writen_bytes; @@ -238,4 +241,4 @@ static ssize_t device_write(struct file *filp, const char __user *buff, module_init(pipebis_init); module_exit(pipebis_exit); -MODULE_LICENSE("GPL"); \ No newline at end of file +MODULE_LICENSE("GPL"); diff --git a/work/tp3/Makefile b/work/tp3/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..6ed65a4e99ccb40b0d7e3b2259561f863ee028a0 --- /dev/null +++ b/work/tp3/Makefile @@ -0,0 +1,9 @@ +KDIR=../../build/kvm/ + +obj-m += sleep.o example_mutex.o example_spinlock.o example_rwlock.o example_atomic.o pipebis3.o +PWD := $(CURDIR) + +all: + make -C $(KDIR) M=$(PWD) modules +clean: + make -C $(KDIR) M=$(PWD) clean diff --git a/work/tp3/cat_nonblock b/work/tp3/cat_nonblock new file mode 100755 index 0000000000000000000000000000000000000000..89aa7580ea438fcd73a69d1d97bdf390a3aa7a30 Binary files /dev/null and b/work/tp3/cat_nonblock differ diff --git a/work/tp3/cat_nonblock.c b/work/tp3/cat_nonblock.c new file mode 100644 index 0000000000000000000000000000000000000000..017418282d819a0d4116da6fed5e7aced0f1d92a --- /dev/null +++ b/work/tp3/cat_nonblock.c @@ -0,0 +1,59 @@ +/* + * cat_nonblock.c - open a file and display its contents, but exit rather than + * wait for input. + */ +#include <errno.h> /* for errno */ +#include <fcntl.h> /* for open */ +#include <stdio.h> /* standard I/O */ +#include <stdlib.h> /* for exit */ +#include <unistd.h> /* for read */ + +#define MAX_BYTES 1024 * 4 + +int main(int argc, char *argv[]) +{ + int fd; /* The file descriptor for the file to read */ + size_t bytes; /* The number of bytes read */ + char buffer[MAX_BYTES]; /* The buffer for the bytes */ + + /* Usage */ + if (argc != 2) { + printf("Usage: %s <filename>\n", argv[0]); + puts("Reads the content of a file, but doesn't wait for input"); + exit(-1); + } + + /* Open the file for reading in non blocking mode */ + fd = open(argv[1], O_RDONLY | O_NONBLOCK); + + /* If open failed */ + if (fd == -1) { + puts(errno == EAGAIN ? "Open would block" : "Open failed"); + exit(-1); + } + + /* Read the file and output its contents */ + do { + /* Read characters from the file */ + bytes = read(fd, buffer, MAX_BYTES); + + /* If there's an error, report it and die */ + if (bytes == -1) { + if (errno == EAGAIN) + puts("Normally I'd block, but you told me not to"); + else + puts("Another read error"); + exit(-1); + } + + /* Print the characters */ + if (bytes > 0) { + for (int i = 0; i < bytes; i++) + putchar(buffer[i]); + } + + /* While there are no errors and the file isn't over */ + } while (bytes > 0); + + return 0; +} \ No newline at end of file diff --git a/work/tp3/example_atomic.c b/work/tp3/example_atomic.c new file mode 100644 index 0000000000000000000000000000000000000000..19a1e8e4c4341e2b383401fa519ff90cf6226ff5 --- /dev/null +++ b/work/tp3/example_atomic.c @@ -0,0 +1,75 @@ +/* + * example_atomic.c + */ +#include <linux/atomic.h> +#include <linux/bitops.h> +#include <linux/module.h> +#include <linux/printk.h> + +#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" +#define BYTE_TO_BINARY(byte) \ + ((byte & 0x80) ? '1' : '0'), ((byte & 0x40) ? '1' : '0'), \ + ((byte & 0x20) ? '1' : '0'), ((byte & 0x10) ? '1' : '0'), \ + ((byte & 0x08) ? '1' : '0'), ((byte & 0x04) ? '1' : '0'), \ + ((byte & 0x02) ? '1' : '0'), ((byte & 0x01) ? '1' : '0') + +static void atomic_add_subtract(void) +{ + atomic_t debbie; + atomic_t chris = ATOMIC_INIT(50); + + atomic_set(&debbie, 45); + + /* subtract one */ + atomic_dec(&debbie); + + atomic_add(7, &debbie); + + /* add one */ + atomic_inc(&debbie); + + pr_info("chris: %d, debbie: %d\n", atomic_read(&chris), + atomic_read(&debbie)); +} + +static void atomic_bitwise(void) +{ + unsigned long word = 0; + + pr_info("Bits 0: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); + set_bit(3, &word); + set_bit(5, &word); + pr_info("Bits 1: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); + clear_bit(5, &word); + pr_info("Bits 2: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); + change_bit(3, &word); + + pr_info("Bits 3: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); + if (test_and_set_bit(3, &word)) + pr_info("wrong\n"); + pr_info("Bits 4: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); + + word = 255; + pr_info("Bits 5: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(word)); +} + +static int __init example_atomic_init(void) +{ + pr_info("example_atomic started\n"); + + atomic_add_subtract(); + atomic_bitwise(); + + return 0; +} + +static void __exit example_atomic_exit(void) +{ + pr_info("example_atomic exit\n"); +} + +module_init(example_atomic_init); +module_exit(example_atomic_exit); + +MODULE_DESCRIPTION("Atomic operations example"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/work/tp3/example_atomic.mod.c b/work/tp3/example_atomic.mod.c new file mode 100644 index 0000000000000000000000000000000000000000..ec0a3ffa9fa289fdf2ed69f86e76d1e7f9b8556a --- /dev/null +++ b/work/tp3/example_atomic.mod.c @@ -0,0 +1,48 @@ +#include <linux/module.h> +#define INCLUDE_VERMAGIC +#include <linux/build-salt.h> +#include <linux/elfnote-lto.h> +#include <linux/export-internal.h> +#include <linux/vermagic.h> +#include <linux/compiler.h> + +#ifdef CONFIG_UNWINDER_ORC +#include <asm/orc_header.h> +ORC_HEADER; +#endif + +BUILD_SALT; +BUILD_LTO_INFO; + +MODULE_INFO(vermagic, VERMAGIC_STRING); +MODULE_INFO(name, KBUILD_MODNAME); + +__visible struct module __this_module +__section(".gnu.linkonce.this_module") = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +#ifdef CONFIG_RETPOLINE +MODULE_INFO(retpoline, "Y"); +#endif + + + +static const struct modversion_info ____versions[] +__used __section("__versions") = { + { 0x5b8239ca, "__x86_return_thunk" }, + { 0xf0fdf6cb, "__stack_chk_fail" }, + { 0xbdfb6dbb, "__fentry__" }, + { 0x122c3a7e, "_printk" }, + { 0x76800044, "module_layout" }, +}; + +MODULE_INFO(depends, ""); + + +MODULE_INFO(srcversion, "AEE2A7B8CBD828FC0381EA8"); diff --git a/work/tp3/example_mutex.c b/work/tp3/example_mutex.c new file mode 100644 index 0000000000000000000000000000000000000000..1504a5fb7b65bd572be3ab454dea1e4d7d3b32c0 --- /dev/null +++ b/work/tp3/example_mutex.c @@ -0,0 +1,40 @@ +/* + * example_mutex.c + */ +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/printk.h> + +static DEFINE_MUTEX(mymutex); + +static int __init example_mutex_init(void) +{ + int ret; + + pr_info("example_mutex init\n"); + + ret = mutex_trylock(&mymutex); + if (ret != 0) { + pr_info("mutex is locked\n"); + + if (mutex_is_locked(&mymutex) == 0) + pr_info("The mutex failed to lock!\n"); + + mutex_unlock(&mymutex); + pr_info("mutex is unlocked\n"); + } else + pr_info("Failed to lock\n"); + + return 0; +} + +static void __exit example_mutex_exit(void) +{ + pr_info("example_mutex exit\n"); +} + +module_init(example_mutex_init); +module_exit(example_mutex_exit); + +MODULE_DESCRIPTION("Mutex example"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/work/tp3/example_mutex.mod.c b/work/tp3/example_mutex.mod.c new file mode 100644 index 0000000000000000000000000000000000000000..f6d2b4a89b2e3b15498f77e4f00adc6d48d85830 --- /dev/null +++ b/work/tp3/example_mutex.mod.c @@ -0,0 +1,50 @@ +#include <linux/module.h> +#define INCLUDE_VERMAGIC +#include <linux/build-salt.h> +#include <linux/elfnote-lto.h> +#include <linux/export-internal.h> +#include <linux/vermagic.h> +#include <linux/compiler.h> + +#ifdef CONFIG_UNWINDER_ORC +#include <asm/orc_header.h> +ORC_HEADER; +#endif + +BUILD_SALT; +BUILD_LTO_INFO; + +MODULE_INFO(vermagic, VERMAGIC_STRING); +MODULE_INFO(name, KBUILD_MODNAME); + +__visible struct module __this_module +__section(".gnu.linkonce.this_module") = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +#ifdef CONFIG_RETPOLINE +MODULE_INFO(retpoline, "Y"); +#endif + + + +static const struct modversion_info ____versions[] +__used __section("__versions") = { + { 0xbdfb6dbb, "__fentry__" }, + { 0xbb9ed3bf, "mutex_trylock" }, + { 0x364c23ad, "mutex_is_locked" }, + { 0x3213f038, "mutex_unlock" }, + { 0x122c3a7e, "_printk" }, + { 0x5b8239ca, "__x86_return_thunk" }, + { 0x76800044, "module_layout" }, +}; + +MODULE_INFO(depends, ""); + + +MODULE_INFO(srcversion, "DFB03C104EE786ED530AED0"); diff --git a/work/tp3/example_rwlock.c b/work/tp3/example_rwlock.c new file mode 100644 index 0000000000000000000000000000000000000000..002a655eedee8b1226c79e4ca87ab626b45975b4 --- /dev/null +++ b/work/tp3/example_rwlock.c @@ -0,0 +1,55 @@ +/* + * example_rwlock.c + */ +#include <linux/module.h> +#include <linux/printk.h> +#include <linux/rwlock.h> + +static DEFINE_RWLOCK(myrwlock); + +static void example_read_lock(void) +{ + unsigned long flags; + + read_lock_irqsave(&myrwlock, flags); + pr_info("Read Locked\n"); + + /* Read from something */ + + read_unlock_irqrestore(&myrwlock, flags); + pr_info("Read Unlocked\n"); +} + +static void example_write_lock(void) +{ + unsigned long flags; + + write_lock_irqsave(&myrwlock, flags); + pr_info("Write Locked\n"); + + /* Write to something */ + + write_unlock_irqrestore(&myrwlock, flags); + pr_info("Write Unlocked\n"); +} + +static int __init example_rwlock_init(void) +{ + pr_info("example_rwlock started\n"); + + example_read_lock(); + example_write_lock(); + + return 0; +} + +static void __exit example_rwlock_exit(void) +{ + pr_info("example_rwlock exit\n"); +} + +module_init(example_rwlock_init); +module_exit(example_rwlock_exit); + +MODULE_DESCRIPTION("Read/Write locks example"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/work/tp3/example_rwlock.mod.c b/work/tp3/example_rwlock.mod.c new file mode 100644 index 0000000000000000000000000000000000000000..b0003317d9c6eb19def25f79343733523ab04d97 --- /dev/null +++ b/work/tp3/example_rwlock.mod.c @@ -0,0 +1,51 @@ +#include <linux/module.h> +#define INCLUDE_VERMAGIC +#include <linux/build-salt.h> +#include <linux/elfnote-lto.h> +#include <linux/export-internal.h> +#include <linux/vermagic.h> +#include <linux/compiler.h> + +#ifdef CONFIG_UNWINDER_ORC +#include <asm/orc_header.h> +ORC_HEADER; +#endif + +BUILD_SALT; +BUILD_LTO_INFO; + +MODULE_INFO(vermagic, VERMAGIC_STRING); +MODULE_INFO(name, KBUILD_MODNAME); + +__visible struct module __this_module +__section(".gnu.linkonce.this_module") = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +#ifdef CONFIG_RETPOLINE +MODULE_INFO(retpoline, "Y"); +#endif + + + +static const struct modversion_info ____versions[] +__used __section("__versions") = { + { 0xb1342cdb, "_raw_read_lock_irqsave" }, + { 0xdf2ebb87, "_raw_read_unlock_irqrestore" }, + { 0x5021bd81, "_raw_write_lock_irqsave" }, + { 0xeb078aee, "_raw_write_unlock_irqrestore" }, + { 0x5b8239ca, "__x86_return_thunk" }, + { 0xbdfb6dbb, "__fentry__" }, + { 0x122c3a7e, "_printk" }, + { 0x76800044, "module_layout" }, +}; + +MODULE_INFO(depends, ""); + + +MODULE_INFO(srcversion, "D9918670193AA1C346E918B"); diff --git a/work/tp3/example_spinlock.c b/work/tp3/example_spinlock.c new file mode 100644 index 0000000000000000000000000000000000000000..9850a6470b2bb2c902486c0cfbd59090cfd84b24 --- /dev/null +++ b/work/tp3/example_spinlock.c @@ -0,0 +1,62 @@ +/* + * example_spinlock.c + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/printk.h> +#include <linux/spinlock.h> + +static DEFINE_SPINLOCK(sl_static); +static spinlock_t sl_dynamic; + +static void example_spinlock_static(void) +{ + unsigned long flags; + + spin_lock_irqsave(&sl_static, flags); + pr_info("Locked static spinlock\n"); + + /* Do something or other safely. Because this uses 100% CPU time, this + * code should take no more than a few milliseconds to run. + */ + + spin_unlock_irqrestore(&sl_static, flags); + pr_info("Unlocked static spinlock\n"); +} + +static void example_spinlock_dynamic(void) +{ + unsigned long flags; + + spin_lock_init(&sl_dynamic); + spin_lock_irqsave(&sl_dynamic, flags); + pr_info("Locked dynamic spinlock\n"); + + /* Do something or other safely. Because this uses 100% CPU time, this + * code should take no more than a few milliseconds to run. + */ + + spin_unlock_irqrestore(&sl_dynamic, flags); + pr_info("Unlocked dynamic spinlock\n"); +} + +static int __init example_spinlock_init(void) +{ + pr_info("example spinlock started\n"); + + example_spinlock_static(); + example_spinlock_dynamic(); + + return 0; +} + +static void __exit example_spinlock_exit(void) +{ + pr_info("example spinlock exit\n"); +} + +module_init(example_spinlock_init); +module_exit(example_spinlock_exit); + +MODULE_DESCRIPTION("Spinlock example"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/work/tp3/example_spinlock.mod.c b/work/tp3/example_spinlock.mod.c new file mode 100644 index 0000000000000000000000000000000000000000..27d03f182aacb6c68d29b6b58c92b952f9a60f99 --- /dev/null +++ b/work/tp3/example_spinlock.mod.c @@ -0,0 +1,49 @@ +#include <linux/module.h> +#define INCLUDE_VERMAGIC +#include <linux/build-salt.h> +#include <linux/elfnote-lto.h> +#include <linux/export-internal.h> +#include <linux/vermagic.h> +#include <linux/compiler.h> + +#ifdef CONFIG_UNWINDER_ORC +#include <asm/orc_header.h> +ORC_HEADER; +#endif + +BUILD_SALT; +BUILD_LTO_INFO; + +MODULE_INFO(vermagic, VERMAGIC_STRING); +MODULE_INFO(name, KBUILD_MODNAME); + +__visible struct module __this_module +__section(".gnu.linkonce.this_module") = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +#ifdef CONFIG_RETPOLINE +MODULE_INFO(retpoline, "Y"); +#endif + + + +static const struct modversion_info ____versions[] +__used __section("__versions") = { + { 0x34db050b, "_raw_spin_lock_irqsave" }, + { 0xd35cce70, "_raw_spin_unlock_irqrestore" }, + { 0x5b8239ca, "__x86_return_thunk" }, + { 0xbdfb6dbb, "__fentry__" }, + { 0x122c3a7e, "_printk" }, + { 0x76800044, "module_layout" }, +}; + +MODULE_INFO(depends, ""); + + +MODULE_INFO(srcversion, "13FF04EED34E7348D027C8D"); diff --git a/work/tp3/modules.order b/work/tp3/modules.order new file mode 100644 index 0000000000000000000000000000000000000000..13e726a8e69115b02677ae5d97d0643856d122c2 --- /dev/null +++ b/work/tp3/modules.order @@ -0,0 +1,6 @@ +/root/Desktop/asee/work/tp3/sleep.o +/root/Desktop/asee/work/tp3/example_mutex.o +/root/Desktop/asee/work/tp3/example_spinlock.o +/root/Desktop/asee/work/tp3/example_rwlock.o +/root/Desktop/asee/work/tp3/example_atomic.o +/root/Desktop/asee/work/tp3/pipebis3.o diff --git a/work/tp3/pipebis.mod.c b/work/tp3/pipebis.mod.c new file mode 100644 index 0000000000000000000000000000000000000000..bcfd385cf9f8e0cc5ac7d36f681e1c3de6f28b40 --- /dev/null +++ b/work/tp3/pipebis.mod.c @@ -0,0 +1,65 @@ +#include <linux/module.h> +#define INCLUDE_VERMAGIC +#include <linux/build-salt.h> +#include <linux/elfnote-lto.h> +#include <linux/export-internal.h> +#include <linux/vermagic.h> +#include <linux/compiler.h> + +#ifdef CONFIG_UNWINDER_ORC +#include <asm/orc_header.h> +ORC_HEADER; +#endif + +BUILD_SALT; +BUILD_LTO_INFO; + +MODULE_INFO(vermagic, VERMAGIC_STRING); +MODULE_INFO(name, KBUILD_MODNAME); + +__visible struct module __this_module +__section(".gnu.linkonce.this_module") = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +#ifdef CONFIG_RETPOLINE +MODULE_INFO(retpoline, "Y"); +#endif + + + +static const struct modversion_info ____versions[] +__used __section("__versions") = { + { 0xfdfe3852, "class_destroy" }, + { 0x6bc3fbc0, "__unregister_chrdev" }, + { 0xecc78457, "kobject_put" }, + { 0x73895700, "__register_chrdev" }, + { 0xeb233a45, "__kmalloc" }, + { 0xfb890f0b, "class_create" }, + { 0x41b15bc9, "device_create" }, + { 0xa6ef1646, "kernel_kobj" }, + { 0x10bd90f2, "kobject_create_and_add" }, + { 0xa3efd5d1, "sysfs_create_file_ns" }, + { 0xbcab6ee6, "sscanf" }, + { 0x37a0cba, "kfree" }, + { 0xbdfb6dbb, "__fentry__" }, + { 0x5b8239ca, "__x86_return_thunk" }, + { 0xc3aaf0a9, "__put_user_1" }, + { 0x167e7f9d, "__get_user_1" }, + { 0x3c3ff9fd, "sprintf" }, + { 0x122c3a7e, "_printk" }, + { 0xab632ed, "module_put" }, + { 0x47143bb3, "try_module_get" }, + { 0xf23bcaeb, "device_destroy" }, + { 0x76800044, "module_layout" }, +}; + +MODULE_INFO(depends, ""); + + +MODULE_INFO(srcversion, "0239DCAF7B987EAF91F373E"); diff --git a/work/tp3/pipebis3.c b/work/tp3/pipebis3.c new file mode 100644 index 0000000000000000000000000000000000000000..9d3edcf8ae01ae06f1167e04f61ec2c4fea55360 --- /dev/null +++ b/work/tp3/pipebis3.c @@ -0,0 +1,317 @@ +/* + * pipebis.c: Creates a read-only char device that says how many times + * you have read from the dev file + */ + +#include <linux/atomic.h> +#include <linux/cdev.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/kernel.h> /* for sprintf() */ +#include <linux/module.h> +#include <linux/printk.h> +#include <linux/types.h> +#include <linux/uaccess.h> /* for get_user and put_user */ +#include <linux/version.h> +#include <linux/wait.h> /* For putting processes to sleep and waking them up */ + +#include <asm/current.h> +#include <asm/errno.h> + +/* Prototypes - this would normally go in a .h file */ +static int device_open(struct inode *, struct file *); +static int device_release(struct inode *, struct file *); +static ssize_t device_read(struct file *, char __user *, size_t, loff_t *); +static ssize_t device_write(struct file *, const char __user *, size_t, + loff_t *); + +#define SUCCESS 0 +#define DEVICE_NAME "asee_mod" /* Dev name as it appears in /proc/devices */ +#define BUF_LEN 16 /* Max length of the message from the device */ + +/* Global variables are declared as static, so are global within the file. */ + +static char *msg; /* The msg the device will give when asked */ + +static struct kobject *pipebis_module; + +static int asee_buf_count = 0; + +static int asee_buf_size = BUF_LEN; + +static ssize_t asee_buf_size_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", asee_buf_size); +} + +static ssize_t asee_buf_size_store(struct kobject *kobj, struct kobj_attribute *attr, char *buf, size_t count) +{ + int old_size = asee_buf_size; + sscanf(buf, "%du", &asee_buf_size); + if (asee_buf_size <= asee_buf_count) { + asee_buf_size = old_size; + pr_err("New size for buffer should be less than current content in it"); + return count; + } + + char * second_message = kcalloc(asee_buf_size, sizeof(char), GFP_KERNEL); + for(int cpt = 0; cpt < old_size; cpt++) { + second_message[cpt] = msg[cpt]; + } + kfree(msg); + msg = second_message; + // TODO: incresae buffer size + return count; +} + +static struct kobj_attribute asee_buf_size_attribute = __ATTR(asee_buf_size, 0660, asee_buf_size_show, (void *)asee_buf_size_store); + + +static ssize_t asee_buf_count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", asee_buf_count); +} + +static ssize_t asee_buf_count_store(struct kobject *kobj, struct kobj_attribute *attr, char *buf, size_t count) +{ + return count; +} + +static struct kobj_attribute asee_buf_count_attribute = __ATTR(asee_buf_count, 0660, asee_buf_count_show, (void *)asee_buf_count_store); + +static int major; /* major number assigned to our device driver */ + +enum { + CDEV_NOT_USED = 0, + CDEV_EXCLUSIVE_OPEN = 1, +}; + +/* Is device open? Used to prevent multiple access to device */ +static atomic_t already_open = ATOMIC_INIT(CDEV_NOT_USED); +static atomic_t already_full = ATOMIC_INIT(0); +static DECLARE_WAIT_QUEUE_HEAD(wait_full); + +static atomic_t empty_buffer = ATOMIC_INIT(0); +static DECLARE_WAIT_QUEUE_HEAD(wait_empty); + +static struct class *cls; + +static struct file_operations pipebis_fops = { + .read = device_read, + .write = device_write, + .open = device_open, + .release = device_release, +}; + +static int __init pipebis_init(void) +{ + major = register_chrdev(0, DEVICE_NAME, &pipebis_fops); + + if (major < 0) { + pr_alert("Registering char device failed with %d\n", major); + return major; + } + + pr_info("I was assigned major number %d.\n", major); + + msg = kcalloc(asee_buf_size, sizeof(char), GFP_KERNEL); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) + cls = class_create(DEVICE_NAME); +#else + cls = class_create(THIS_MODULE, DEVICE_NAME); +#endif + device_create(cls, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); + + pr_info("Device created on /dev/%s\n", DEVICE_NAME); + + pipebis_module = kobject_create_and_add("asee_mod", kernel_kobj); + if (!pipebis_module) return -ENOMEM; + + int error = 0; + error = sysfs_create_file(pipebis_module, &asee_buf_size_attribute.attr); + error += sysfs_create_file(pipebis_module, &asee_buf_count_attribute.attr); + if (error) { + pr_info("failed to create a asee variable in /sys/kernel/asee_mod\n"); + return error; + } + + return SUCCESS; +} + +static void __exit pipebis_exit(void) +{ + device_destroy(cls, MKDEV(major, 0)); + class_destroy(cls); + + /* Unregister the device */ + unregister_chrdev(major, DEVICE_NAME); + kobject_put(pipebis_module); +} + +/* Methods */ + +/* Called when a process tries to open the device file, like + * "sudo cat /dev/pipebis" + */ +static int device_open(struct inode *inode, struct file *file) +{ + // if (atomic_cmpxchg(&already_open, CDEV_NOT_USED, CDEV_EXCLUSIVE_OPEN)) + // return -EBUSY; + + if ((file->f_flags & O_NONBLOCK) && atomic_read(&already_full)) + return -EAGAIN; + + if ((file->f_flags & O_NONBLOCK) && atomic_read(&empty_buffer)) + return -EAGAIN; + + try_module_get(THIS_MODULE); + + return SUCCESS; +} + +/* Called when a process closes the device file. */ +static int device_release(struct inode *inode, struct file *file) +{ + /* We're now ready for our next caller */ + //atomic_set(&already_open, CDEV_NOT_USED); + + /* Decrement the usage count, or else once you opened the file, you will + * never get rid of the module. + */ + module_put(THIS_MODULE); + + return SUCCESS; +} + + + +/* Called when a process, which already opened the dev file, attempts to + * read from it. + */ + +static int block_empty_buff(void) +{ + while (atomic_cmpxchg(&empty_buffer, 0, 1)) { + int i, is_sig = 0; + + wait_event_interruptible(wait_empty, !atomic_read(&empty_buffer)); + + for (i = 0; i < _NSIG_WORDS && !is_sig; i++) { + is_sig = current->pending.signal.sig[i] & ~current->blocked.sig[i]; + } + + if (is_sig) { + module_put(THIS_MODULE); + return -1; + } + } + return 0; +} + +static int last_writen_bytes = 0; +static int last_read_bytes = 0; +static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */ + char __user *buffer, /* buffer to fill with data */ + size_t length, /* length of the buffer */ + loff_t *offset) +{ + /* Number of bytes actually written to the buffer */ + int bytes_read = 0; + char *msg_ptr = msg; + + if (asee_buf_count <= 0) { + if (block_empty_buff() < 0) return 0; + } + + /* Actually put the data into the buffer */ + while (asee_buf_count > 0) { + if (last_read_bytes >= asee_buf_size) { + last_read_bytes = 0; + } + /* The buffer is in the user data segment, not the kernel + * segment so "*" assignment won't work. We have to use + * put_user which copies data from the kernel data segment to + * the user data segment. + */ + + put_user(*(msg_ptr + last_read_bytes), buffer++); + // Try to clear the read + *(msg_ptr + last_read_bytes) = '\0'; + last_read_bytes++; + asee_buf_count--; + bytes_read++; + atomic_set(&already_full, 0); + wake_up(&wait_full); + //if (asee_buf_count <= 0) { + // if (block_empty_buff() < 0) return 0; + //} + } + *offset = last_read_bytes; + + /* Most read functions return the number of bytes put into the buffer. */ + return bytes_read; +} + +static int block_full_buff(void) +{ + while (atomic_cmpxchg(&already_full, 0, 1)) { + int i, is_sig = 0; + + wait_event_interruptible(wait_full, !atomic_read(&already_full)); + + for (i = 0; i < _NSIG_WORDS && !is_sig; i++) { + is_sig = current->pending.signal.sig[i] & ~current->blocked.sig[i]; + } + + if (is_sig) { + module_put(THIS_MODULE); + return -1; + } + } + return 0; +} + +/* Called when a process writes to dev file: echo "hi" > /dev/hello */ +static ssize_t device_write(struct file *filp, const char __user *buff, + size_t len, loff_t *off) +{ + int bytes_writen = 0; + + int current_bytes = last_writen_bytes; + + if (asee_buf_count >= asee_buf_size) { + if ( block_full_buff() < 0 ) return 0; + } + + for (int cpt = 0; cpt < len; cpt++) { + if (current_bytes >= asee_buf_size) { + current_bytes = 0; + } + get_user(*(msg + current_bytes), buff + cpt); + current_bytes++; + bytes_writen++; + + atomic_set(&empty_buffer, 0); + wake_up(&wait_empty); + + if (asee_buf_count < asee_buf_size) { + asee_buf_count++; + } else { + if ( block_full_buff() < 0 ) return 0; + } + } + last_read_bytes = (last_read_bytes + bytes_writen) % asee_buf_count; + + last_writen_bytes = current_bytes; + *off = last_writen_bytes; + + return bytes_writen; +} + +module_init(pipebis_init); +module_exit(pipebis_exit); + +MODULE_LICENSE("GPL"); diff --git a/work/tp3/pipebis3.mod.c b/work/tp3/pipebis3.mod.c new file mode 100644 index 0000000000000000000000000000000000000000..1508ee4d1860b16736802e186644dd9882ca7251 --- /dev/null +++ b/work/tp3/pipebis3.mod.c @@ -0,0 +1,73 @@ +#include <linux/module.h> +#define INCLUDE_VERMAGIC +#include <linux/build-salt.h> +#include <linux/elfnote-lto.h> +#include <linux/export-internal.h> +#include <linux/vermagic.h> +#include <linux/compiler.h> + +#ifdef CONFIG_UNWINDER_ORC +#include <asm/orc_header.h> +ORC_HEADER; +#endif + +BUILD_SALT; +BUILD_LTO_INFO; + +MODULE_INFO(vermagic, VERMAGIC_STRING); +MODULE_INFO(name, KBUILD_MODNAME); + +__visible struct module __this_module +__section(".gnu.linkonce.this_module") = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +#ifdef CONFIG_RETPOLINE +MODULE_INFO(retpoline, "Y"); +#endif + + + +static const struct modversion_info ____versions[] +__used __section("__versions") = { + { 0x92540fbf, "finish_wait" }, + { 0xf0fdf6cb, "__stack_chk_fail" }, + { 0x167e7f9d, "__get_user_1" }, + { 0xe2964344, "__wake_up" }, + { 0xc3aaf0a9, "__put_user_1" }, + { 0xf23bcaeb, "device_destroy" }, + { 0xfdfe3852, "class_destroy" }, + { 0x6bc3fbc0, "__unregister_chrdev" }, + { 0xecc78457, "kobject_put" }, + { 0x73895700, "__register_chrdev" }, + { 0x122c3a7e, "_printk" }, + { 0xeb233a45, "__kmalloc" }, + { 0xfb890f0b, "class_create" }, + { 0x41b15bc9, "device_create" }, + { 0xa6ef1646, "kernel_kobj" }, + { 0x10bd90f2, "kobject_create_and_add" }, + { 0xa3efd5d1, "sysfs_create_file_ns" }, + { 0xbcab6ee6, "sscanf" }, + { 0x37a0cba, "kfree" }, + { 0xbdfb6dbb, "__fentry__" }, + { 0x5b8239ca, "__x86_return_thunk" }, + { 0x3c3ff9fd, "sprintf" }, + { 0xab632ed, "module_put" }, + { 0x47143bb3, "try_module_get" }, + { 0x8470dec7, "pcpu_hot" }, + { 0xe2c17b5d, "__SCT__might_resched" }, + { 0xfe487975, "init_wait_entry" }, + { 0x1000e51, "schedule" }, + { 0x8c26d495, "prepare_to_wait_event" }, + { 0x76800044, "module_layout" }, +}; + +MODULE_INFO(depends, ""); + + +MODULE_INFO(srcversion, "FC2D859DF4303E84366E606"); diff --git a/work/tp3/sleep.c b/work/tp3/sleep.c new file mode 100644 index 0000000000000000000000000000000000000000..53905725507814f87eaf84e911977c7cc14fdfca --- /dev/null +++ b/work/tp3/sleep.c @@ -0,0 +1,222 @@ +/* + * sleep.c - create a /proc file, and if several processes try to open it + * at the same time, put all but one to sleep. + */ + +#include <linux/atomic.h> +#include <linux/fs.h> +#include <linux/kernel.h> /* for sprintf() */ +#include <linux/module.h> /* Specifically, a module */ +#include <linux/printk.h> +#include <linux/proc_fs.h> /* Necessary because we use proc fs */ +#include <linux/types.h> +#include <linux/uaccess.h> /* for get_user and put_user */ +#include <linux/version.h> +#include <linux/wait.h> /* For putting processes to sleep and + waking them up */ + +#include <asm/current.h> +#include <asm/errno.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +#define HAVE_PROC_OPS +#endif + +/* Here we keep the last message received, to prove that we can process our + * input. + */ +#define MESSAGE_LENGTH 80 +static char message[MESSAGE_LENGTH]; + +static struct proc_dir_entry *our_proc_file; +#define PROC_ENTRY_FILENAME "sleep" + +/* Since we use the file operations struct, we can't use the special proc + * output provisions - we have to use a standard read function, which is this + * function. + */ +static ssize_t module_output(struct file *file, /* see include/linux/fs.h */ + char __user *buf, /* The buffer to put data to + (in the user segment) */ + size_t len, /* The length of the buffer */ + loff_t *offset) +{ + static int finished = 0; + int i; + char output_msg[MESSAGE_LENGTH + 30]; + + /* Return 0 to signify end of file - that we have nothing more to say + * at this point. + */ + if (finished) { + finished = 0; + return 0; + } + + sprintf(output_msg, "Last input:%s\n", message); + for (i = 0; i < len && output_msg[i]; i++) + put_user(output_msg[i], buf + i); + + finished = 1; + return i; /* Return the number of bytes "read" */ +} + +/* This function receives input from the user when the user writes to the + * /proc file. + */ +static ssize_t module_input(struct file *file, /* The file itself */ + const char __user *buf, /* The buffer with input */ + size_t length, /* The buffer's length */ + loff_t *offset) /* offset to file - ignore */ +{ + int i; + + /* Put the input into Message, where module_output will later be able + * to use it. + */ + for (i = 0; i < MESSAGE_LENGTH - 1 && i < length; i++) + get_user(message[i], buf + i); + /* we want a standard, zero terminated string */ + message[i] = '\0'; + + /* We need to return the number of input characters used */ + return i; +} + +/* 1 if the file is currently open by somebody */ +static atomic_t already_open = ATOMIC_INIT(0); + +/* Queue of processes who want our file */ +static DECLARE_WAIT_QUEUE_HEAD(waitq); + +/* Called when the /proc file is opened */ +static int module_open(struct inode *inode, struct file *file) +{ + /* If the file's flags include O_NONBLOCK, it means the process does not + * want to wait for the file. In this case, if the file is already open, + * we should fail with -EAGAIN, meaning "you will have to try again", + * instead of blocking a process which would rather stay awake. + */ + if ((file->f_flags & O_NONBLOCK) && atomic_read(&already_open)) + return -EAGAIN; + + /* This is the correct place for try_module_get(THIS_MODULE) because if + * a process is in the loop, which is within the kernel module, + * the kernel module must not be removed. + */ + try_module_get(THIS_MODULE); + + while (atomic_cmpxchg(&already_open, 0, 1)) { + int i, is_sig = 0; + + /* This function puts the current process, including any system + * calls, such as us, to sleep. Execution will be resumed right + * after the function call, either because somebody called + * wake_up(&waitq) (only module_close does that, when the file + * is closed) or when a signal, such as Ctrl-C, is sent + * to the process + */ + wait_event_interruptible(waitq, !atomic_read(&already_open)); + + /* If we woke up because we got a signal we're not blocking, + * return -EINTR (fail the system call). This allows processes + * to be killed or stopped. + */ + for (i = 0; i < _NSIG_WORDS && !is_sig; i++) + is_sig = current->pending.signal.sig[i] & ~current->blocked.sig[i]; + + if (is_sig) { + /* It is important to put module_put(THIS_MODULE) here, because + * for processes where the open is interrupted there will never + * be a corresponding close. If we do not decrement the usage + * count here, we will be left with a positive usage count + * which we will have no way to bring down to zero, giving us + * an immortal module, which can only be killed by rebooting + * the machine. + */ + module_put(THIS_MODULE); + return -EINTR; + } + } + + return 0; /* Allow the access */ +} + +/* Called when the /proc file is closed */ +static int module_close(struct inode *inode, struct file *file) +{ + /* Set already_open to zero, so one of the processes in the waitq will + * be able to set already_open back to one and to open the file. All + * the other processes will be called when already_open is back to one, + * so they'll go back to sleep. + */ + atomic_set(&already_open, 0); + + /* Wake up all the processes in waitq, so if anybody is waiting for the + * file, they can have it. + */ + wake_up(&waitq); + + module_put(THIS_MODULE); + + return 0; /* success */ +} + +/* Structures to register as the /proc file, with pointers to all the relevant + * functions. + */ + +/* File operations for our proc file. This is where we place pointers to all + * the functions called when somebody tries to do something to our file. NULL + * means we don't want to deal with something. + */ +#ifdef HAVE_PROC_OPS +static const struct proc_ops file_ops_4_our_proc_file = { + .proc_read = module_output, /* "read" from the file */ + .proc_write = module_input, /* "write" to the file */ + .proc_open = module_open, /* called when the /proc file is opened */ + .proc_release = module_close, /* called when it's closed */ + .proc_lseek = noop_llseek, /* return file->f_pos */ +}; +#else +static const struct file_operations file_ops_4_our_proc_file = { + .read = module_output, + .write = module_input, + .open = module_open, + .release = module_close, + .llseek = noop_llseek, +}; +#endif + +/* Initialize the module - register the proc file */ +static int __init sleep_init(void) +{ + our_proc_file = + proc_create(PROC_ENTRY_FILENAME, 0644, NULL, &file_ops_4_our_proc_file); + if (our_proc_file == NULL) { + pr_debug("Error: Could not initialize /proc/%s\n", PROC_ENTRY_FILENAME); + return -ENOMEM; + } + proc_set_size(our_proc_file, 80); + proc_set_user(our_proc_file, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID); + + pr_info("/proc/%s created\n", PROC_ENTRY_FILENAME); + + return 0; +} + +/* Cleanup - unregister our file from /proc. This could get dangerous if + * there are still processes waiting in waitq, because they are inside our + * open function, which will get unloaded. I'll explain how to avoid removal + * of a kernel module in such a case in chapter 10. + */ +static void __exit sleep_exit(void) +{ + remove_proc_entry(PROC_ENTRY_FILENAME, NULL); + pr_debug("/proc/%s removed\n", PROC_ENTRY_FILENAME); +} + +module_init(sleep_init); +module_exit(sleep_exit); + +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/work/tp3/sleep.mod.c b/work/tp3/sleep.mod.c new file mode 100644 index 0000000000000000000000000000000000000000..af9ab7a81f87e72c192416195b488c1d5c43b075 --- /dev/null +++ b/work/tp3/sleep.mod.c @@ -0,0 +1,67 @@ +#include <linux/module.h> +#define INCLUDE_VERMAGIC +#include <linux/build-salt.h> +#include <linux/elfnote-lto.h> +#include <linux/export-internal.h> +#include <linux/vermagic.h> +#include <linux/compiler.h> + +#ifdef CONFIG_UNWINDER_ORC +#include <asm/orc_header.h> +ORC_HEADER; +#endif + +BUILD_SALT; +BUILD_LTO_INFO; + +MODULE_INFO(vermagic, VERMAGIC_STRING); +MODULE_INFO(name, KBUILD_MODNAME); + +__visible struct module __this_module +__section(".gnu.linkonce.this_module") = { + .name = KBUILD_MODNAME, + .init = init_module, +#ifdef CONFIG_MODULE_UNLOAD + .exit = cleanup_module, +#endif + .arch = MODULE_ARCH_INIT, +}; + +#ifdef CONFIG_RETPOLINE +MODULE_INFO(retpoline, "Y"); +#endif + + + +static const struct modversion_info ____versions[] +__used __section("__versions") = { + { 0x122c3a7e, "_printk" }, + { 0x2cf56265, "__dynamic_pr_debug" }, + { 0xe2964344, "__wake_up" }, + { 0xab632ed, "module_put" }, + { 0x4c37b7b3, "remove_proc_entry" }, + { 0x3c3ff9fd, "sprintf" }, + { 0xc3aaf0a9, "__put_user_1" }, + { 0xf0fdf6cb, "__stack_chk_fail" }, + { 0x47143bb3, "try_module_get" }, + { 0x8470dec7, "pcpu_hot" }, + { 0xe2c17b5d, "__SCT__might_resched" }, + { 0xfe487975, "init_wait_entry" }, + { 0x1000e51, "schedule" }, + { 0x8c26d495, "prepare_to_wait_event" }, + { 0x92540fbf, "finish_wait" }, + { 0xfcda5799, "noop_llseek" }, + { 0xbdfb6dbb, "__fentry__" }, + { 0x167e7f9d, "__get_user_1" }, + { 0x5b8239ca, "__x86_return_thunk" }, + { 0x87a21cb3, "__ubsan_handle_out_of_bounds" }, + { 0x1fb706c, "proc_create" }, + { 0xb093cc6c, "proc_set_size" }, + { 0xa4e0184b, "proc_set_user" }, + { 0x76800044, "module_layout" }, +}; + +MODULE_INFO(depends, ""); + + +MODULE_INFO(srcversion, "54ECDB7965AE96459D8F160");