Skip to content

Commit

Permalink
completion: Use simple wait queues
Browse files Browse the repository at this point in the history
Completions have no long lasting callbacks and therefor do not need
the complex waitqueue variant. Use simple waitqueues which reduces the
contention on the waitqueue lock.

Signed-off-by: Thomas Gleixner <[email protected]>
[[email protected]: Move __prepare_to_swait() into the do loop because
  swake_up_locked() removes the waiter on wake from the queue while in the
  original code it is not the case]
Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
  • Loading branch information
KAGA-KOKO authored and Tom Zanussi committed Apr 11, 2022
1 parent 7bfe6f2 commit c11f308
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 29 deletions.
4 changes: 2 additions & 2 deletions arch/powerpc/platforms/ps3/device-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -738,8 +738,8 @@ static int ps3_notification_read_write(struct ps3_notification_device *dev,
}
pr_debug("%s:%u: notification %s issued\n", __func__, __LINE__, op);

res = wait_event_interruptible(dev->done.wait,
dev->done.done || kthread_should_stop());
res = swait_event_interruptible_exclusive(dev->done.wait,
dev->done.done || kthread_should_stop());
if (kthread_should_stop())
res = -EINTR;
if (res) {
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/wireless/intersil/orinoco/orinoco_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -693,8 +693,8 @@ static void ezusb_req_ctx_wait(struct ezusb_priv *upriv,
while (!ctx->done.done && msecs--)
udelay(1000);
} else {
wait_event_interruptible(ctx->done.wait,
ctx->done.done);
swait_event_interruptible_exclusive(ctx->done.wait,
ctx->done.done);
}
break;
default:
Expand Down
2 changes: 1 addition & 1 deletion drivers/usb/gadget/function/f_fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1719,7 +1719,7 @@ static void ffs_data_put(struct ffs_data *ffs)
ffs_data_clear(ffs);
ffs_release_dev(ffs->private_data);
BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
waitqueue_active(&ffs->ep0req_completion.wait) ||
swait_active(&ffs->ep0req_completion.wait) ||
waitqueue_active(&ffs->wait));
destroy_workqueue(ffs->io_completion_wq);
kfree(ffs->dev_name);
Expand Down
4 changes: 2 additions & 2 deletions drivers/usb/gadget/legacy/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ ep_io (struct ep_data *epdata, void *buf, unsigned len)
spin_unlock_irq (&epdata->dev->lock);

if (likely (value == 0)) {
value = wait_event_interruptible (done.wait, done.done);
value = swait_event_interruptible_exclusive(done.wait, done.done);
if (value != 0) {
spin_lock_irq (&epdata->dev->lock);
if (likely (epdata->ep != NULL)) {
Expand All @@ -355,7 +355,7 @@ ep_io (struct ep_data *epdata, void *buf, unsigned len)
usb_ep_dequeue (epdata->ep, epdata->req);
spin_unlock_irq (&epdata->dev->lock);

wait_event (done.wait, done.done);
swait_event_exclusive(done.wait, done.done);
if (epdata->status == -ECONNRESET)
epdata->status = -EINTR;
} else {
Expand Down
8 changes: 4 additions & 4 deletions include/linux/completion.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* See kernel/sched/completion.c for details.
*/

#include <linux/wait.h>
#include <linux/swait.h>

/*
* struct completion - structure used to maintain state for a "completion"
Expand All @@ -25,7 +25,7 @@
*/
struct completion {
unsigned int done;
wait_queue_head_t wait;
struct swait_queue_head wait;
};

#define init_completion_map(x, m) __init_completion(x)
Expand All @@ -34,7 +34,7 @@ static inline void complete_acquire(struct completion *x) {}
static inline void complete_release(struct completion *x) {}

#define COMPLETION_INITIALIZER(work) \
{ 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) }
{ 0, __SWAIT_QUEUE_HEAD_INITIALIZER((work).wait) }

#define COMPLETION_INITIALIZER_ONSTACK_MAP(work, map) \
(*({ init_completion_map(&(work), &(map)); &(work); }))
Expand Down Expand Up @@ -85,7 +85,7 @@ static inline void complete_release(struct completion *x) {}
static inline void __init_completion(struct completion *x)
{
x->done = 0;
init_waitqueue_head(&x->wait);
init_swait_queue_head(&x->wait);
}

/**
Expand Down
6 changes: 6 additions & 0 deletions include/linux/suspend.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ struct platform_s2idle_ops {
void (*end)(void);
};

#if defined(CONFIG_SUSPEND) || defined(CONFIG_HIBERNATION)
extern bool pm_in_action;
#else
# define pm_in_action false
#endif

#ifdef CONFIG_SUSPEND
extern suspend_state_t mem_sleep_current;
extern suspend_state_t mem_sleep_default;
Expand Down
2 changes: 2 additions & 0 deletions include/linux/swait.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ static inline bool swq_has_sleeper(struct swait_queue_head *wq)
extern void swake_up_one(struct swait_queue_head *q);
extern void swake_up_all(struct swait_queue_head *q);
extern void swake_up_locked(struct swait_queue_head *q);
extern void swake_up_all_locked(struct swait_queue_head *q);

extern void __prepare_to_swait(struct swait_queue_head *q, struct swait_queue *wait);
extern void prepare_to_swait_exclusive(struct swait_queue_head *q, struct swait_queue *wait, int state);
extern long prepare_to_swait_event(struct swait_queue_head *q, struct swait_queue *wait, int state);

Expand Down
7 changes: 7 additions & 0 deletions kernel/power/hibernate.c
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,10 @@ static int load_image_and_restore(void)
return error;
}

#ifndef CONFIG_SUSPEND
bool pm_in_action;
#endif

/**
* hibernate - Carry out system hibernation, including saving the image.
*/
Expand All @@ -702,6 +706,8 @@ int hibernate(void)
return -EPERM;
}

pm_in_action = true;

lock_system_sleep();
/* The snapshot device should not be opened while we're running */
if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
Expand Down Expand Up @@ -778,6 +784,7 @@ int hibernate(void)
atomic_inc(&snapshot_device_available);
Unlock:
unlock_system_sleep();
pm_in_action = false;
pr_info("hibernation exit\n");

return error;
Expand Down
4 changes: 4 additions & 0 deletions kernel/power/suspend.c
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,8 @@ static int enter_state(suspend_state_t state)
return error;
}

bool pm_in_action;

/**
* pm_suspend - Externally visible function for suspending the system.
* @state: System sleep state to enter.
Expand All @@ -607,6 +609,7 @@ int pm_suspend(suspend_state_t state)
if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
return -EINVAL;

pm_in_action = true;
pr_info("suspend entry (%s)\n", mem_sleep_labels[state]);
error = enter_state(state);
if (error) {
Expand All @@ -616,6 +619,7 @@ int pm_suspend(suspend_state_t state)
suspend_stats.success++;
}
pr_info("suspend exit\n");
pm_in_action = false;
return error;
}
EXPORT_SYMBOL(pm_suspend);
34 changes: 17 additions & 17 deletions kernel/sched/completion.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ void complete(struct completion *x)
{
unsigned long flags;

spin_lock_irqsave(&x->wait.lock, flags);
raw_spin_lock_irqsave(&x->wait.lock, flags);

if (x->done != UINT_MAX)
x->done++;
__wake_up_locked(&x->wait, TASK_NORMAL, 1);
spin_unlock_irqrestore(&x->wait.lock, flags);
swake_up_locked(&x->wait);
raw_spin_unlock_irqrestore(&x->wait.lock, flags);
}
EXPORT_SYMBOL(complete);

Expand All @@ -58,10 +58,10 @@ void complete_all(struct completion *x)
{
unsigned long flags;

spin_lock_irqsave(&x->wait.lock, flags);
raw_spin_lock_irqsave(&x->wait.lock, flags);
x->done = UINT_MAX;
__wake_up_locked(&x->wait, TASK_NORMAL, 0);
spin_unlock_irqrestore(&x->wait.lock, flags);
swake_up_all_locked(&x->wait);
raw_spin_unlock_irqrestore(&x->wait.lock, flags);
}
EXPORT_SYMBOL(complete_all);

Expand All @@ -70,20 +70,20 @@ do_wait_for_common(struct completion *x,
long (*action)(long), long timeout, int state)
{
if (!x->done) {
DECLARE_WAITQUEUE(wait, current);
DECLARE_SWAITQUEUE(wait);

__add_wait_queue_entry_tail_exclusive(&x->wait, &wait);
do {
if (signal_pending_state(state, current)) {
timeout = -ERESTARTSYS;
break;
}
__prepare_to_swait(&x->wait, &wait);
__set_current_state(state);
spin_unlock_irq(&x->wait.lock);
raw_spin_unlock_irq(&x->wait.lock);
timeout = action(timeout);
spin_lock_irq(&x->wait.lock);
raw_spin_lock_irq(&x->wait.lock);
} while (!x->done && timeout);
__remove_wait_queue(&x->wait, &wait);
__finish_swait(&x->wait, &wait);
if (!x->done)
return timeout;
}
Expand All @@ -100,9 +100,9 @@ __wait_for_common(struct completion *x,

complete_acquire(x);

spin_lock_irq(&x->wait.lock);
raw_spin_lock_irq(&x->wait.lock);
timeout = do_wait_for_common(x, action, timeout, state);
spin_unlock_irq(&x->wait.lock);
raw_spin_unlock_irq(&x->wait.lock);

complete_release(x);

Expand Down Expand Up @@ -291,12 +291,12 @@ bool try_wait_for_completion(struct completion *x)
if (!READ_ONCE(x->done))
return false;

spin_lock_irqsave(&x->wait.lock, flags);
raw_spin_lock_irqsave(&x->wait.lock, flags);
if (!x->done)
ret = false;
else if (x->done != UINT_MAX)
x->done--;
spin_unlock_irqrestore(&x->wait.lock, flags);
raw_spin_unlock_irqrestore(&x->wait.lock, flags);
return ret;
}
EXPORT_SYMBOL(try_wait_for_completion);
Expand All @@ -322,8 +322,8 @@ bool completion_done(struct completion *x)
* otherwise we can end up freeing the completion before complete()
* is done referencing it.
*/
spin_lock_irqsave(&x->wait.lock, flags);
spin_unlock_irqrestore(&x->wait.lock, flags);
raw_spin_lock_irqsave(&x->wait.lock, flags);
raw_spin_unlock_irqrestore(&x->wait.lock, flags);
return true;
}
EXPORT_SYMBOL(completion_done);
21 changes: 20 additions & 1 deletion kernel/sched/swait.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,25 @@ void swake_up_locked(struct swait_queue_head *q)
}
EXPORT_SYMBOL(swake_up_locked);

void swake_up_all_locked(struct swait_queue_head *q)
{
struct swait_queue *curr;
int wakes = 0;

while (!list_empty(&q->task_list)) {

curr = list_first_entry(&q->task_list, typeof(*curr),
task_list);
wake_up_process(curr->task);
list_del_init(&curr->task_list);
wakes++;
}
if (pm_in_action)
return;
WARN(wakes > 2, "complete_all() with %d waiters\n", wakes);
}
EXPORT_SYMBOL(swake_up_all_locked);

void swake_up_one(struct swait_queue_head *q)
{
unsigned long flags;
Expand Down Expand Up @@ -70,7 +89,7 @@ void swake_up_all(struct swait_queue_head *q)
}
EXPORT_SYMBOL(swake_up_all);

static void __prepare_to_swait(struct swait_queue_head *q, struct swait_queue *wait)
void __prepare_to_swait(struct swait_queue_head *q, struct swait_queue *wait)
{
wait->task = current;
if (list_empty(&wait->task_list))
Expand Down

0 comments on commit c11f308

Please sign in to comment.