Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions drivers/iommu/iommufd/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ int iommufd_fault_alloc(struct iommufd_ucmd *ucmd)
spin_lock_init(&fault->lock);
init_waitqueue_head(&fault->wait_queue);

/* The filep is fput() by the core code during failure */
filep = anon_inode_getfile("[iommufd-pgfault]", &iommufd_fault_fops,
fault, O_RDWR);
if (IS_ERR(filep)) {
Expand All @@ -416,7 +417,7 @@ int iommufd_fault_alloc(struct iommufd_ucmd *ucmd)
fdno = get_unused_fd_flags(O_CLOEXEC);
if (fdno < 0) {
rc = fdno;
goto out_fput;
goto out_abort;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other Reviewer note: This took me a hot minute to figure out what was going on even with the description and the commit links.

its best to full unwind the context lines here if you're going to dig into it

}

cmd->out_fault_id = fault->obj.id;
Expand All @@ -432,8 +433,6 @@ int iommufd_fault_alloc(struct iommufd_ucmd *ucmd)
return 0;
out_put_fdno:
put_unused_fd(fdno);
out_fput:
fput(filep);
out_abort:
iommufd_object_abort_and_destroy(ucmd->ictx, &fault->obj);

Expand Down
34 changes: 31 additions & 3 deletions drivers/iommu/iommufd/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "iommufd_test.h"

struct iommufd_object_ops {
size_t file_offset;
void (*destroy)(struct iommufd_object *obj);
void (*abort)(struct iommufd_object *obj);
};
Expand Down Expand Up @@ -97,10 +98,30 @@ void iommufd_object_abort(struct iommufd_ctx *ictx, struct iommufd_object *obj)
void iommufd_object_abort_and_destroy(struct iommufd_ctx *ictx,
struct iommufd_object *obj)
{
if (iommufd_object_ops[obj->type].abort)
iommufd_object_ops[obj->type].abort(obj);
const struct iommufd_object_ops *ops = &iommufd_object_ops[obj->type];

if (ops->file_offset) {
struct file **filep = ((void *)obj) + ops->file_offset;

/*
* A file should hold a users refcount while the file is open
* and put it back in its release. The file should hold a
* pointer to obj in their private data. Normal fput() is
* deferred to a workqueue and can get out of order with the
* following kfree(obj). Using the sync version ensures the
* release happens immediately. During abort we require the file
* refcount is one at this point - meaning the object alloc
* function cannot do anything to allow another thread to take a
* refcount prior to a guaranteed success.
*/
if (*filep)
__fput_sync(*filep);
}

if (ops->abort)
ops->abort(obj);
else
iommufd_object_ops[obj->type].destroy(obj);
ops->destroy(obj);
iommufd_object_abort(ictx, obj);
}

Expand Down Expand Up @@ -498,6 +519,12 @@ void iommufd_ctx_put(struct iommufd_ctx *ictx)
}
EXPORT_SYMBOL_NS_GPL(iommufd_ctx_put, IOMMUFD);

#define IOMMUFD_FILE_OFFSET(_struct, _filep, _obj) \
.file_offset = (offsetof(_struct, _filep) + \
BUILD_BUG_ON_ZERO(!__same_type( \
struct file *, ((_struct *)NULL)->_filep)) + \
BUILD_BUG_ON_ZERO(offsetof(_struct, _obj)))

static const struct iommufd_object_ops iommufd_object_ops[] = {
[IOMMUFD_OBJ_ACCESS] = {
.destroy = iommufd_access_destroy_object,
Expand All @@ -518,6 +545,7 @@ static const struct iommufd_object_ops iommufd_object_ops[] = {
},
[IOMMUFD_OBJ_FAULT] = {
.destroy = iommufd_fault_destroy,
IOMMUFD_FILE_OFFSET(struct iommufd_fault, filep, obj),
},
#ifdef CONFIG_IOMMUFD_TEST
[IOMMUFD_OBJ_SELFTEST] = {
Expand Down
54 changes: 36 additions & 18 deletions fs/kernfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,24 @@ static struct kernfs_open_node *of_on(struct kernfs_open_file *of)
!list_empty(&of->list));
}

/* Get active reference to kernfs node for an open file */
static struct kernfs_open_file *kernfs_get_active_of(struct kernfs_open_file *of)
{
/* Skip if file was already released */
if (unlikely(of->released))
return NULL;

if (!kernfs_get_active(of->kn))
return NULL;

return of;
}

static void kernfs_put_active_of(struct kernfs_open_file *of)
{
return kernfs_put_active(of->kn);
}

/**
* kernfs_deref_open_node_locked - Get kernfs_open_node corresponding to @kn
*
Expand Down Expand Up @@ -139,7 +157,7 @@ static void kernfs_seq_stop_active(struct seq_file *sf, void *v)

if (ops->seq_stop)
ops->seq_stop(sf, v);
kernfs_put_active(of->kn);
kernfs_put_active_of(of);
}

static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos)
Expand All @@ -152,7 +170,7 @@ static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos)
* the ops aren't called concurrently for the same open file.
*/
mutex_lock(&of->mutex);
if (!kernfs_get_active(of->kn))
if (!kernfs_get_active_of(of))
return ERR_PTR(-ENODEV);

ops = kernfs_ops(of->kn);
Expand Down Expand Up @@ -243,7 +261,7 @@ static ssize_t kernfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
* the ops aren't called concurrently for the same open file.
*/
mutex_lock(&of->mutex);
if (!kernfs_get_active(of->kn)) {
if (!kernfs_get_active_of(of)) {
len = -ENODEV;
mutex_unlock(&of->mutex);
goto out_free;
Expand All @@ -257,7 +275,7 @@ static ssize_t kernfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
else
len = -EINVAL;

kernfs_put_active(of->kn);
kernfs_put_active_of(of);
mutex_unlock(&of->mutex);

if (len < 0)
Expand Down Expand Up @@ -328,7 +346,7 @@ static ssize_t kernfs_fop_write_iter(struct kiocb *iocb, struct iov_iter *iter)
* the ops aren't called concurrently for the same open file.
*/
mutex_lock(&of->mutex);
if (!kernfs_get_active(of->kn)) {
if (!kernfs_get_active_of(of)) {
mutex_unlock(&of->mutex);
len = -ENODEV;
goto out_free;
Expand All @@ -340,7 +358,7 @@ static ssize_t kernfs_fop_write_iter(struct kiocb *iocb, struct iov_iter *iter)
else
len = -EINVAL;

kernfs_put_active(of->kn);
kernfs_put_active_of(of);
mutex_unlock(&of->mutex);

if (len > 0)
Expand All @@ -362,13 +380,13 @@ static void kernfs_vma_open(struct vm_area_struct *vma)
if (!of->vm_ops)
return;

if (!kernfs_get_active(of->kn))
if (!kernfs_get_active_of(of))
return;

if (of->vm_ops->open)
of->vm_ops->open(vma);

kernfs_put_active(of->kn);
kernfs_put_active_of(of);
}

static vm_fault_t kernfs_vma_fault(struct vm_fault *vmf)
Expand All @@ -380,14 +398,14 @@ static vm_fault_t kernfs_vma_fault(struct vm_fault *vmf)
if (!of->vm_ops)
return VM_FAULT_SIGBUS;

if (!kernfs_get_active(of->kn))
if (!kernfs_get_active_of(of))
return VM_FAULT_SIGBUS;

ret = VM_FAULT_SIGBUS;
if (of->vm_ops->fault)
ret = of->vm_ops->fault(vmf);

kernfs_put_active(of->kn);
kernfs_put_active_of(of);
return ret;
}

Expand All @@ -400,7 +418,7 @@ static vm_fault_t kernfs_vma_page_mkwrite(struct vm_fault *vmf)
if (!of->vm_ops)
return VM_FAULT_SIGBUS;

if (!kernfs_get_active(of->kn))
if (!kernfs_get_active_of(of))
return VM_FAULT_SIGBUS;

ret = 0;
Expand All @@ -409,7 +427,7 @@ static vm_fault_t kernfs_vma_page_mkwrite(struct vm_fault *vmf)
else
file_update_time(file);

kernfs_put_active(of->kn);
kernfs_put_active_of(of);
return ret;
}

Expand All @@ -423,14 +441,14 @@ static int kernfs_vma_access(struct vm_area_struct *vma, unsigned long addr,
if (!of->vm_ops)
return -EINVAL;

if (!kernfs_get_active(of->kn))
if (!kernfs_get_active_of(of))
return -EINVAL;

ret = -EINVAL;
if (of->vm_ops->access)
ret = of->vm_ops->access(vma, addr, buf, len, write);

kernfs_put_active(of->kn);
kernfs_put_active_of(of);
return ret;
}

Expand Down Expand Up @@ -460,7 +478,7 @@ static int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma)
mutex_lock(&of->mutex);

rc = -ENODEV;
if (!kernfs_get_active(of->kn))
if (!kernfs_get_active_of(of))
goto out_unlock;

ops = kernfs_ops(of->kn);
Expand Down Expand Up @@ -493,7 +511,7 @@ static int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma)
of->vm_ops = vma->vm_ops;
vma->vm_ops = &kernfs_vm_ops;
out_put:
kernfs_put_active(of->kn);
kernfs_put_active_of(of);
out_unlock:
mutex_unlock(&of->mutex);

Expand Down Expand Up @@ -847,15 +865,15 @@ static __poll_t kernfs_fop_poll(struct file *filp, poll_table *wait)
struct kernfs_node *kn = kernfs_dentry_node(filp->f_path.dentry);
__poll_t ret;

if (!kernfs_get_active(kn))
if (!kernfs_get_active_of(of))
return DEFAULT_POLLMASK|EPOLLERR|EPOLLPRI;

if (kn->attr.ops->poll)
ret = kn->attr.ops->poll(of, wait);
else
ret = kernfs_generic_poll(of, wait);

kernfs_put_active(kn);
kernfs_put_active_of(of);
return ret;
}

Expand Down
12 changes: 10 additions & 2 deletions io_uring/fdinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,18 +148,26 @@ __cold void io_uring_show_fdinfo(struct seq_file *m, struct file *file)

if (has_lock && (ctx->flags & IORING_SETUP_SQPOLL)) {
struct io_sq_data *sq = ctx->sq_data;
struct task_struct *tsk;

rcu_read_lock();
tsk = rcu_dereference(sq->thread);
/*
* sq->thread might be NULL if we raced with the sqpoll
* thread termination.
*/
if (sq->thread) {
if (tsk) {
get_task_struct(tsk);
rcu_read_unlock();
getrusage(tsk, RUSAGE_SELF, &sq_usage);
put_task_struct(tsk);
sq_pid = sq->task_pid;
sq_cpu = sq->sq_cpu;
getrusage(sq->thread, RUSAGE_SELF, &sq_usage);
sq_total_time = (sq_usage.ru_stime.tv_sec * 1000000
+ sq_usage.ru_stime.tv_usec);
sq_work_time = sq->work_time;
} else {
rcu_read_unlock();
}
}

Expand Down
27 changes: 15 additions & 12 deletions io_uring/io_uring.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ struct io_defer_entry {

static bool io_uring_try_cancel_requests(struct io_ring_ctx *ctx,
struct task_struct *task,
bool cancel_all);
bool cancel_all,
bool is_sqpoll_thread);

static void io_queue_sqe(struct io_kiocb *req);

Expand Down Expand Up @@ -1262,10 +1263,7 @@ static void io_req_normal_work_add(struct io_kiocb *req)

/* SQPOLL doesn't need the task_work added, it'll run it itself */
if (ctx->flags & IORING_SETUP_SQPOLL) {
struct io_sq_data *sqd = ctx->sq_data;

if (sqd->thread)
__set_notify_signal(sqd->thread);
__set_notify_signal(req->task);
return;
}

Expand Down Expand Up @@ -2821,15 +2819,16 @@ static __cold void io_ring_exit_work(struct work_struct *work)
if (ctx->flags & IORING_SETUP_DEFER_TASKRUN)
io_move_task_work_from_local(ctx);

while (io_uring_try_cancel_requests(ctx, NULL, true))
/* The SQPOLL thread never reaches this path */
while (io_uring_try_cancel_requests(ctx, NULL, true, false))
cond_resched();

if (ctx->sq_data) {
struct io_sq_data *sqd = ctx->sq_data;
struct task_struct *tsk;

io_sq_thread_park(sqd);
tsk = sqd->thread;
tsk = sqpoll_task_locked(sqd);
if (tsk && tsk->io_uring && tsk->io_uring->io_wq)
io_wq_cancel_cb(tsk->io_uring->io_wq,
io_cancel_ctx_cb, ctx, true);
Expand Down Expand Up @@ -2989,7 +2988,8 @@ static __cold bool io_uring_try_cancel_iowq(struct io_ring_ctx *ctx)

static __cold bool io_uring_try_cancel_requests(struct io_ring_ctx *ctx,
struct task_struct *task,
bool cancel_all)
bool cancel_all,
bool is_sqpoll_thread)
{
struct io_task_cancel cancel = { .task = task, .all = cancel_all, };
struct io_uring_task *tctx = task ? task->io_uring : NULL;
Expand Down Expand Up @@ -3020,7 +3020,7 @@ static __cold bool io_uring_try_cancel_requests(struct io_ring_ctx *ctx,

/* SQPOLL thread does its own polling */
if ((!(ctx->flags & IORING_SETUP_SQPOLL) && cancel_all) ||
(ctx->sq_data && ctx->sq_data->thread == current)) {
is_sqpoll_thread) {
while (!wq_list_empty(&ctx->iopoll_list)) {
io_iopoll_try_reap_events(ctx);
ret = true;
Expand Down Expand Up @@ -3066,7 +3066,7 @@ __cold void io_uring_cancel_generic(bool cancel_all, struct io_sq_data *sqd)
s64 inflight;
DEFINE_WAIT(wait);

WARN_ON_ONCE(sqd && sqd->thread != current);
WARN_ON_ONCE(sqd && sqpoll_task_locked(sqd) != current);

if (!current->io_uring)
return;
Expand All @@ -3092,13 +3092,16 @@ __cold void io_uring_cancel_generic(bool cancel_all, struct io_sq_data *sqd)
if (node->ctx->sq_data)
continue;
loop |= io_uring_try_cancel_requests(node->ctx,
current, cancel_all);
current,
cancel_all,
false);
}
} else {
list_for_each_entry(ctx, &sqd->ctx_list, sqd_list)
loop |= io_uring_try_cancel_requests(ctx,
current,
cancel_all);
cancel_all,
true);
}

if (loop) {
Expand Down
Loading
Loading