Skip to content

Commit

Permalink
xen/events/fifo: correctly align bitops
Browse files Browse the repository at this point in the history
FIFO event channels require bitops on 32-bit aligned values (the event
words).  Linux's bitops require unsigned long alignment which may be
64-bits.

On arm64 an incorrectly unaligned access will fault.

Fix this by aligning the bitops along with an adjustment for bit
position and using an unsigned long for the local copy of the ready
word.

Cc: stable@vger.kernel.org
Signed-off-by: Vladimir Murzin <murzin.v@gmail.com>
Tested-by: Pranavkumar Sawargaonkar <pranavkumar@linaro.org>
Reviewed-by: Ian Campbell <ian.campbell@citrix.com>
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
  • Loading branch information
Vladimir Murzin authored and David Vrabel committed Apr 28, 2014
1 parent 063aa8e commit 05a812a
Showing 1 changed file with 30 additions and 11 deletions.
41 changes: 30 additions & 11 deletions drivers/xen/events/events_fifo.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,22 @@ static DEFINE_PER_CPU(struct evtchn_fifo_queue, cpu_queue);
static event_word_t *event_array[MAX_EVENT_ARRAY_PAGES] __read_mostly;
static unsigned event_array_pages __read_mostly;

/*
* sync_set_bit() and friends must be unsigned long aligned on non-x86
* platforms.
*/
#if !defined(CONFIG_X86) && BITS_PER_LONG > 32

#define BM(w) (unsigned long *)((unsigned long)w & ~0x7UL)
#define EVTCHN_FIFO_BIT(b, w) \
(((unsigned long)w & 0x4UL) ? (EVTCHN_FIFO_ ##b + 32) : EVTCHN_FIFO_ ##b)

#else

#define BM(w) ((unsigned long *)(w))
#define EVTCHN_FIFO_BIT(b, w) EVTCHN_FIFO_ ##b

#endif

static inline event_word_t *event_word_from_port(unsigned port)
{
Expand Down Expand Up @@ -161,33 +176,38 @@ static void evtchn_fifo_bind_to_cpu(struct irq_info *info, unsigned cpu)
static void evtchn_fifo_clear_pending(unsigned port)
{
event_word_t *word = event_word_from_port(port);
sync_clear_bit(EVTCHN_FIFO_PENDING, BM(word));
sync_clear_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word));
}

static void evtchn_fifo_set_pending(unsigned port)
{
event_word_t *word = event_word_from_port(port);
sync_set_bit(EVTCHN_FIFO_PENDING, BM(word));
sync_set_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word));
}

static bool evtchn_fifo_is_pending(unsigned port)
{
event_word_t *word = event_word_from_port(port);
return sync_test_bit(EVTCHN_FIFO_PENDING, BM(word));
return sync_test_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word));
}

static bool evtchn_fifo_test_and_set_mask(unsigned port)
{
event_word_t *word = event_word_from_port(port);
return sync_test_and_set_bit(EVTCHN_FIFO_MASKED, BM(word));
return sync_test_and_set_bit(EVTCHN_FIFO_BIT(MASKED, word), BM(word));
}

static void evtchn_fifo_mask(unsigned port)
{
event_word_t *word = event_word_from_port(port);
sync_set_bit(EVTCHN_FIFO_MASKED, BM(word));
sync_set_bit(EVTCHN_FIFO_BIT(MASKED, word), BM(word));
}

static bool evtchn_fifo_is_masked(unsigned port)
{
event_word_t *word = event_word_from_port(port);
return sync_test_bit(EVTCHN_FIFO_BIT(MASKED, word), BM(word));
}
/*
* Clear MASKED, spinning if BUSY is set.
*/
Expand All @@ -211,7 +231,7 @@ static void evtchn_fifo_unmask(unsigned port)
BUG_ON(!irqs_disabled());

clear_masked(word);
if (sync_test_bit(EVTCHN_FIFO_PENDING, BM(word))) {
if (evtchn_fifo_is_pending(port)) {
struct evtchn_unmask unmask = { .port = port };
(void)HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &unmask);
}
Expand Down Expand Up @@ -243,7 +263,7 @@ static void handle_irq_for_port(unsigned port)

static void consume_one_event(unsigned cpu,
struct evtchn_fifo_control_block *control_block,
unsigned priority, uint32_t *ready)
unsigned priority, unsigned long *ready)
{
struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu);
uint32_t head;
Expand Down Expand Up @@ -273,10 +293,9 @@ static void consume_one_event(unsigned cpu,
* copy of the ready word.
*/
if (head == 0)
clear_bit(priority, BM(ready));
clear_bit(priority, ready);

if (sync_test_bit(EVTCHN_FIFO_PENDING, BM(word))
&& !sync_test_bit(EVTCHN_FIFO_MASKED, BM(word)))
if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port))
handle_irq_for_port(port);

q->head[priority] = head;
Expand All @@ -285,7 +304,7 @@ static void consume_one_event(unsigned cpu,
static void evtchn_fifo_handle_events(unsigned cpu)
{
struct evtchn_fifo_control_block *control_block;
uint32_t ready;
unsigned long ready;
unsigned q;

control_block = per_cpu(cpu_control_block, cpu);
Expand Down

0 comments on commit 05a812a

Please sign in to comment.