misc: mods: fix ioctl for writing into sysfs files

f_op->write doesn't always have to exist, it can be NULL.
Instead, use kernel function which handles this correctly.

Change-Id: I955bc1c2e97d3bfee3e5c7e48fefcbda4af1214c
Signed-off-by: Chris Dragan <kdragan@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2457926
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: Anand Nahar <anahar@nvidia.com>
Reviewed-by: Sachin Nikam <snikam@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
GVS: Gerrit_Virtual_Submit
This commit is contained in:
Chris Dragan
2020-12-10 00:36:19 -08:00
committed by Laxman Dewangan
parent a51241efac
commit 32e7a1caab
3 changed files with 65 additions and 32 deletions

View File

@@ -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

View File

@@ -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 */

View File

@@ -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",