diff --git a/drivers/misc/mods/mods_config.h b/drivers/misc/mods/mods_config.h index ef08bd22..5a186e9b 100644 --- a/drivers/misc/mods/mods_config.h +++ b/drivers/misc/mods/mods_config.h @@ -86,6 +86,10 @@ #endif #endif +#if KERNEL_VERSION(4, 14, 0) <= MODS_KERNEL_VERSION +# define MODS_HAS_KERNEL_WRITE +#endif + #if KERNEL_VERSION(4, 16, 0) > MODS_KERNEL_VERSION && defined(CONFIG_X86) # define MODS_HAS_MAP_SG_ATTRS #endif diff --git a/drivers/misc/mods/mods_internal.h b/drivers/misc/mods/mods_internal.h index a68c1a5c..297ffbc1 100644 --- a/drivers/misc/mods/mods_internal.h +++ b/drivers/misc/mods/mods_internal.h @@ -88,31 +88,32 @@ struct irq_q_info { * different processes. This structure tracks data specific to each open fd. */ struct mods_client { - struct list_head irq_list; - struct list_head mem_alloc_list; - struct list_head mem_map_list; - struct list_head free_mem_list; /* list of unused UC/WC chunks */ + struct list_head irq_list; + struct list_head mem_alloc_list; + struct list_head mem_map_list; + struct list_head free_mem_list; /* unused UC/WC chunks */ #if defined(CONFIG_PPC64) - struct list_head ppc_tce_bypass_list; - struct list_head nvlink_sysmem_trained_list; + struct list_head ppc_tce_bypass_list; + struct list_head nvlink_sysmem_trained_list; #endif - wait_queue_head_t interrupt_event; - struct irq_q_info irq_queue; - spinlock_t irq_lock; - struct en_dev_entry *enabled_devices; - struct mem_type mem_type; + wait_queue_head_t interrupt_event; + struct irq_q_info irq_queue; + spinlock_t irq_lock; + struct en_dev_entry *enabled_devices; + struct workqueue_struct *work_queue; + struct mem_type mem_type; #if defined(CONFIG_PCI) - struct pci_dev *cached_dev; + struct pci_dev *cached_dev; #endif - struct mutex mtx; - int mods_fb_suspended[FB_MAX]; - u32 access_token; - atomic_t num_allocs; - atomic_t num_pages; + struct mutex mtx; + int mods_fb_suspended[FB_MAX]; + u32 access_token; + atomic_t num_allocs; + atomic_t num_pages; #if defined(MODS_HAS_CONSOLE_LOCK) - atomic_t console_is_locked; + atomic_t console_is_locked; #endif - u8 client_id; + u8 client_id; }; /* VM private data */ diff --git a/drivers/misc/mods/mods_krnl.c b/drivers/misc/mods/mods_krnl.c index d4847ac9..85145f21 100644 --- a/drivers/misc/mods/mods_krnl.c +++ b/drivers/misc/mods/mods_krnl.c @@ -1039,6 +1039,11 @@ static int mods_krnl_close(struct inode *ip, struct file *fp) } } + if (client->work_queue) { + destroy_workqueue(client->work_queue); + client->work_queue = NULL; + } + mods_free_client(client_id); pr_info("mods [%d]: driver closed\n", client_id); @@ -1618,10 +1623,17 @@ static void sysfs_write_task(struct work_struct *w) task->err = PTR_ERR(f); else { f->f_pos = 0; - task->err = f->f_op->write(f, - task->data, - task->data_size, - &f->f_pos); +#ifdef MODS_HAS_KERNEL_WRITE + task->err = kernel_write(f, + task->data, + task->data_size, + &f->f_pos); +#else + task->err = vfs_write(f, + (__force const char __user *)task->data, + task->data_size, + &f->f_pos); +#endif filp_close(f, NULL); } @@ -1630,23 +1642,39 @@ static void sysfs_write_task(struct work_struct *w) LOG_EXT(); } +static int create_work_queue(struct mods_client *client) +{ + int err = 0; + + if (unlikely(mutex_lock_interruptible(&client->mtx))) + return -EINTR; + + if (!client->work_queue) { + client->work_queue = create_singlethread_workqueue("mods_wq"); + if (!client->work_queue) { + cl_error("failed to create work queue\n"); + err = -ENOMEM; + } + } + + mutex_unlock(&client->mtx); + + return err; +} + static int run_write_task(struct mods_client *client, struct mods_file_work *task) { - struct workqueue_struct *wq; + int err = create_work_queue(client); - wq = create_singlethread_workqueue("mods_file_write"); - if (!wq) { - cl_error("failed to create work queue\n"); - return -ENOMEM; - } + if (err) + return err; cl_info("write %.*s to %s\n", task->data_size, task->data, task->path); INIT_WORK(&task->work, sysfs_write_task); - queue_work(wq, &task->work); - flush_workqueue(wq); - destroy_workqueue(wq); + queue_work(client->work_queue, &task->work); + flush_workqueue(client->work_queue); if (task->err < 0) cl_error("failed to write %.*s to %s\n",