Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/common/event_fifo.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * FIFO event channel management.
3
 *
4
 * Copyright (C) 2013 Citrix Systems R&D Ltd.
5
 *
6
 * This source code is licensed under the GNU General Public License,
7
 * Version 2 or later.  See the file COPYING for more details.
8
 */
9
10
#include <xen/init.h>
11
#include <xen/lib.h>
12
#include <xen/errno.h>
13
#include <xen/sched.h>
14
#include <xen/event.h>
15
#include <xen/event_fifo.h>
16
#include <xen/paging.h>
17
#include <xen/mm.h>
18
#include <xen/domain_page.h>
19
20
#include <public/event_channel.h>
21
22
static inline event_word_t *evtchn_fifo_word_from_port(const struct domain *d,
23
                                                       unsigned int port)
24
0
{
25
0
    unsigned int p, w;
26
0
27
0
    if ( unlikely(port >= d->evtchn_fifo->num_evtchns) )
28
0
        return NULL;
29
0
30
0
    /*
31
0
     * Callers aren't required to hold d->event_lock, so we need to synchronize
32
0
     * with add_page_to_event_array().
33
0
     */
34
0
    smp_rmb();
35
0
36
0
    p = port / EVTCHN_FIFO_EVENT_WORDS_PER_PAGE;
37
0
    w = port % EVTCHN_FIFO_EVENT_WORDS_PER_PAGE;
38
0
39
0
    return d->evtchn_fifo->event_array[p] + w;
40
0
}
41
42
static void evtchn_fifo_init(struct domain *d, struct evtchn *evtchn)
43
0
{
44
0
    event_word_t *word;
45
0
46
0
    evtchn->priority = EVTCHN_FIFO_PRIORITY_DEFAULT;
47
0
48
0
    /*
49
0
     * If this event is still linked, the first event may be delivered
50
0
     * on the wrong VCPU or with an unexpected priority.
51
0
     */
52
0
    word = evtchn_fifo_word_from_port(d, evtchn->port);
53
0
    if ( word && test_bit(EVTCHN_FIFO_LINKED, word) )
54
0
        gdprintk(XENLOG_WARNING, "domain %d, port %d already on a queue\n",
55
0
                 d->domain_id, evtchn->port);
56
0
}
57
58
static struct evtchn_fifo_queue *lock_old_queue(const struct domain *d,
59
                                                struct evtchn *evtchn,
60
                                                unsigned long *flags)
61
0
{
62
0
    struct vcpu *v;
63
0
    struct evtchn_fifo_queue *q, *old_q;
64
0
    unsigned int try;
65
0
66
0
    for ( try = 0; try < 3; try++ )
67
0
    {
68
0
        v = d->vcpu[evtchn->last_vcpu_id];
69
0
        old_q = &v->evtchn_fifo->queue[evtchn->last_priority];
70
0
71
0
        spin_lock_irqsave(&old_q->lock, *flags);
72
0
73
0
        v = d->vcpu[evtchn->last_vcpu_id];
74
0
        q = &v->evtchn_fifo->queue[evtchn->last_priority];
75
0
76
0
        if ( old_q == q )
77
0
            return old_q;
78
0
79
0
        spin_unlock_irqrestore(&old_q->lock, *flags);
80
0
    }
81
0
82
0
    gprintk(XENLOG_WARNING,
83
0
            "dom%d port %d lost event (too many queue changes)\n",
84
0
            d->domain_id, evtchn->port);
85
0
    return NULL;
86
0
}          
87
88
static int try_set_link(event_word_t *word, event_word_t *w, uint32_t link)
89
0
{
90
0
    event_word_t new, old;
91
0
92
0
    if ( !(*w & (1 << EVTCHN_FIFO_LINKED)) )
93
0
        return 0;
94
0
95
0
    old = *w;
96
0
    new = (old & ~((1 << EVTCHN_FIFO_BUSY) | EVTCHN_FIFO_LINK_MASK)) | link;
97
0
    *w = cmpxchg(word, old, new);
98
0
    if ( *w == old )
99
0
        return 1;
100
0
101
0
    return -EAGAIN;
102
0
}
103
104
/*
105
 * Atomically set the LINK field iff it is still LINKED.
106
 *
107
 * The guest is only permitted to make the following changes to a
108
 * LINKED event.
109
 *
110
 * - set MASKED
111
 * - clear MASKED
112
 * - clear PENDING
113
 * - clear LINKED (and LINK)
114
 *
115
 * We block unmasking by the guest by marking the tail word as BUSY,
116
 * therefore, the cmpxchg() may fail at most 4 times.
117
 */
118
static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word,
119
                                   uint32_t link)
120
0
{
121
0
    event_word_t w;
122
0
    unsigned int try;
123
0
    int ret;
124
0
125
0
    w = read_atomic(word);
126
0
127
0
    ret = try_set_link(word, &w, link);
128
0
    if ( ret >= 0 )
129
0
        return ret;
130
0
131
0
    /* Lock the word to prevent guest unmasking. */
132
0
    set_bit(EVTCHN_FIFO_BUSY, word);
133
0
134
0
    w = read_atomic(word);
135
0
136
0
    for ( try = 0; try < 4; try++ )
137
0
    {
138
0
        ret = try_set_link(word, &w, link);
139
0
        if ( ret >= 0 )
140
0
        {
141
0
            if ( ret == 0 )
142
0
                clear_bit(EVTCHN_FIFO_BUSY, word);
143
0
            return ret;
144
0
        }
145
0
    }
146
0
    gdprintk(XENLOG_WARNING, "domain %d, port %d not linked\n",
147
0
             d->domain_id, link);
148
0
    clear_bit(EVTCHN_FIFO_BUSY, word);
149
0
    return 1;
150
0
}
151
152
static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn)
153
0
{
154
0
    struct domain *d = v->domain;
155
0
    unsigned int port;
156
0
    event_word_t *word;
157
0
    unsigned long flags;
158
0
    bool_t was_pending;
159
0
160
0
    port = evtchn->port;
161
0
    word = evtchn_fifo_word_from_port(d, port);
162
0
163
0
    /*
164
0
     * Event array page may not exist yet, save the pending state for
165
0
     * when the page is added.
166
0
     */
167
0
    if ( unlikely(!word) )
168
0
    {
169
0
        evtchn->pending = 1;
170
0
        return;
171
0
    }
172
0
173
0
    was_pending = test_and_set_bit(EVTCHN_FIFO_PENDING, word);
174
0
175
0
    /*
176
0
     * Link the event if it unmasked and not already linked.
177
0
     */
178
0
    if ( !test_bit(EVTCHN_FIFO_MASKED, word)
179
0
         && !test_bit(EVTCHN_FIFO_LINKED, word) )
180
0
    {
181
0
        struct evtchn_fifo_queue *q, *old_q;
182
0
        event_word_t *tail_word;
183
0
        bool_t linked = 0;
184
0
185
0
        /*
186
0
         * Control block not mapped.  The guest must not unmask an
187
0
         * event until the control block is initialized, so we can
188
0
         * just drop the event.
189
0
         */
190
0
        if ( unlikely(!v->evtchn_fifo->control_block) )
191
0
        {
192
0
            printk(XENLOG_G_WARNING
193
0
                   "%pv has no FIFO event channel control block\n", v);
194
0
            goto done;
195
0
        }
196
0
197
0
        /*
198
0
         * No locking around getting the queue. This may race with
199
0
         * changing the priority but we are allowed to signal the
200
0
         * event once on the old priority.
201
0
         */
202
0
        q = &v->evtchn_fifo->queue[evtchn->priority];
203
0
204
0
        old_q = lock_old_queue(d, evtchn, &flags);
205
0
        if ( !old_q )
206
0
            goto done;
207
0
208
0
        if ( test_and_set_bit(EVTCHN_FIFO_LINKED, word) )
209
0
        {
210
0
            spin_unlock_irqrestore(&old_q->lock, flags);
211
0
            goto done;
212
0
        }
213
0
214
0
        /*
215
0
         * If this event was a tail, the old queue is now empty and
216
0
         * its tail must be invalidated to prevent adding an event to
217
0
         * the old queue from corrupting the new queue.
218
0
         */
219
0
        if ( old_q->tail == port )
220
0
            old_q->tail = 0;
221
0
222
0
        /* Moved to a different queue? */
223
0
        if ( old_q != q )
224
0
        {
225
0
            evtchn->last_vcpu_id = evtchn->notify_vcpu_id;
226
0
            evtchn->last_priority = evtchn->priority;
227
0
228
0
            spin_unlock_irqrestore(&old_q->lock, flags);
229
0
            spin_lock_irqsave(&q->lock, flags);
230
0
        }
231
0
232
0
        /*
233
0
         * Atomically link the tail to port iff the tail is linked.
234
0
         * If the tail is unlinked the queue is empty.
235
0
         *
236
0
         * If port is the same as tail, the queue is empty but q->tail
237
0
         * will appear linked as we just set LINKED above.
238
0
         *
239
0
         * If the queue is empty (i.e., we haven't linked to the new
240
0
         * event), head must be updated.
241
0
         */
242
0
        if ( q->tail )
243
0
        {
244
0
            tail_word = evtchn_fifo_word_from_port(d, q->tail);
245
0
            linked = evtchn_fifo_set_link(d, tail_word, port);
246
0
        }
247
0
        if ( !linked )
248
0
            write_atomic(q->head, port);
249
0
        q->tail = port;
250
0
251
0
        spin_unlock_irqrestore(&q->lock, flags);
252
0
253
0
        if ( !linked
254
0
             && !test_and_set_bit(q->priority,
255
0
                                  &v->evtchn_fifo->control_block->ready) )
256
0
            vcpu_mark_events_pending(v);
257
0
    }
258
0
 done:
259
0
    if ( !was_pending )
260
0
        evtchn_check_pollers(d, port);
261
0
}
262
263
static void evtchn_fifo_clear_pending(struct domain *d, struct evtchn *evtchn)
264
0
{
265
0
    event_word_t *word;
266
0
267
0
    word = evtchn_fifo_word_from_port(d, evtchn->port);
268
0
    if ( unlikely(!word) )
269
0
        return;
270
0
271
0
    /*
272
0
     * Just clear the P bit.
273
0
     *
274
0
     * No need to unlink as the guest will unlink and ignore
275
0
     * non-pending events.
276
0
     */
277
0
    clear_bit(EVTCHN_FIFO_PENDING, word);
278
0
}
279
280
static void evtchn_fifo_unmask(struct domain *d, struct evtchn *evtchn)
281
0
{
282
0
    struct vcpu *v = d->vcpu[evtchn->notify_vcpu_id];
283
0
    event_word_t *word;
284
0
285
0
    word = evtchn_fifo_word_from_port(d, evtchn->port);
286
0
    if ( unlikely(!word) )
287
0
        return;
288
0
289
0
    clear_bit(EVTCHN_FIFO_MASKED, word);
290
0
291
0
    /* Relink if pending. */
292
0
    if ( test_bit(EVTCHN_FIFO_PENDING, word) )
293
0
        evtchn_fifo_set_pending(v, evtchn);
294
0
}
295
296
static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port)
297
0
{
298
0
    const event_word_t *word = evtchn_fifo_word_from_port(d, port);
299
0
300
0
    return word && test_bit(EVTCHN_FIFO_PENDING, word);
301
0
}
302
303
static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port)
304
0
{
305
0
    const event_word_t *word = evtchn_fifo_word_from_port(d, port);
306
0
307
0
    return !word || test_bit(EVTCHN_FIFO_MASKED, word);
308
0
}
309
310
static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port)
311
0
{
312
0
    const event_word_t *word = evtchn_fifo_word_from_port(d, port);
313
0
314
0
    return word && test_bit(EVTCHN_FIFO_LINKED, word);
315
0
}
316
317
static int evtchn_fifo_set_priority(struct domain *d, struct evtchn *evtchn,
318
                                    unsigned int priority)
319
0
{
320
0
    if ( priority > EVTCHN_FIFO_PRIORITY_MIN )
321
0
        return -EINVAL;
322
0
323
0
    /*
324
0
     * Only need to switch to the new queue for future events. If the
325
0
     * event is already pending or in the process of being linked it
326
0
     * will be on the old queue -- this is fine.
327
0
     */
328
0
    evtchn->priority = priority;
329
0
330
0
    return 0;
331
0
}
332
333
static void evtchn_fifo_print_state(struct domain *d,
334
                                    const struct evtchn *evtchn)
335
0
{
336
0
    event_word_t *word;
337
0
338
0
    word = evtchn_fifo_word_from_port(d, evtchn->port);
339
0
    if ( !word )
340
0
        printk("?     ");
341
0
    else if ( test_bit(EVTCHN_FIFO_LINKED, word) )
342
0
        printk("%c %-4u", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' ',
343
0
               *word & EVTCHN_FIFO_LINK_MASK);
344
0
    else
345
0
        printk("%c -   ", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' ');
346
0
}
347
348
static const struct evtchn_port_ops evtchn_port_ops_fifo =
349
{
350
    .init          = evtchn_fifo_init,
351
    .set_pending   = evtchn_fifo_set_pending,
352
    .clear_pending = evtchn_fifo_clear_pending,
353
    .unmask        = evtchn_fifo_unmask,
354
    .is_pending    = evtchn_fifo_is_pending,
355
    .is_masked     = evtchn_fifo_is_masked,
356
    .is_busy       = evtchn_fifo_is_busy,
357
    .set_priority  = evtchn_fifo_set_priority,
358
    .print_state   = evtchn_fifo_print_state,
359
};
360
361
static int map_guest_page(struct domain *d, uint64_t gfn, void **virt)
362
0
{
363
0
    struct page_info *p;
364
0
365
0
    p = get_page_from_gfn(d, gfn, NULL, P2M_ALLOC);
366
0
    if ( !p )
367
0
        return -EINVAL;
368
0
369
0
    if ( !get_page_type(p, PGT_writable_page) )
370
0
    {
371
0
        put_page(p);
372
0
        return -EINVAL;
373
0
    }
374
0
375
0
    *virt = __map_domain_page_global(p);
376
0
    if ( !*virt )
377
0
    {
378
0
        put_page_and_type(p);
379
0
        return -ENOMEM;
380
0
    }
381
0
    return 0;
382
0
}
383
384
static void unmap_guest_page(void *virt)
385
0
{
386
0
    struct page_info *page;
387
0
388
0
    if ( !virt )
389
0
        return;
390
0
391
0
    virt = (void *)((unsigned long)virt & PAGE_MASK);
392
0
    page = mfn_to_page(domain_page_map_to_mfn(virt));
393
0
394
0
    unmap_domain_page_global(virt);
395
0
    put_page_and_type(page);
396
0
}
397
398
static void init_queue(struct vcpu *v, struct evtchn_fifo_queue *q,
399
                       unsigned int i)
400
0
{
401
0
    spin_lock_init(&q->lock);
402
0
    q->priority = i;
403
0
}
404
405
static int setup_control_block(struct vcpu *v)
406
0
{
407
0
    struct evtchn_fifo_vcpu *efv;
408
0
    unsigned int i;
409
0
410
0
    efv = xzalloc(struct evtchn_fifo_vcpu);
411
0
    if ( !efv )
412
0
        return -ENOMEM;
413
0
414
0
    for ( i = 0; i <= EVTCHN_FIFO_PRIORITY_MIN; i++ )
415
0
        init_queue(v, &efv->queue[i], i);
416
0
417
0
    v->evtchn_fifo = efv;
418
0
419
0
    return 0;
420
0
}
421
422
static int map_control_block(struct vcpu *v, uint64_t gfn, uint32_t offset)
423
0
{
424
0
    void *virt;
425
0
    unsigned int i;
426
0
    int rc;
427
0
428
0
    if ( v->evtchn_fifo->control_block )
429
0
        return -EINVAL;
430
0
431
0
    rc = map_guest_page(v->domain, gfn, &virt);
432
0
    if ( rc < 0 )
433
0
        return rc;
434
0
435
0
    v->evtchn_fifo->control_block = virt + offset;
436
0
437
0
    for ( i = 0; i <= EVTCHN_FIFO_PRIORITY_MIN; i++ )
438
0
        v->evtchn_fifo->queue[i].head = &v->evtchn_fifo->control_block->head[i];
439
0
440
0
    return 0;
441
0
}
442
443
static void cleanup_control_block(struct vcpu *v)
444
0
{
445
0
    if ( !v->evtchn_fifo )
446
0
        return;
447
0
448
0
    unmap_guest_page(v->evtchn_fifo->control_block);
449
0
    xfree(v->evtchn_fifo);
450
0
    v->evtchn_fifo = NULL;
451
0
}
452
453
/*
454
 * Setup an event array with no pages.
455
 */
456
static int setup_event_array(struct domain *d)
457
0
{
458
0
    d->evtchn_fifo = xzalloc(struct evtchn_fifo_domain);
459
0
    if ( !d->evtchn_fifo )
460
0
        return -ENOMEM;
461
0
462
0
    return 0;
463
0
}
464
465
static void cleanup_event_array(struct domain *d)
466
0
{
467
0
    unsigned int i;
468
0
469
0
    if ( !d->evtchn_fifo )
470
0
        return;
471
0
472
0
    for ( i = 0; i < EVTCHN_FIFO_MAX_EVENT_ARRAY_PAGES; i++ )
473
0
        unmap_guest_page(d->evtchn_fifo->event_array[i]);
474
0
    xfree(d->evtchn_fifo);
475
0
    d->evtchn_fifo = NULL;
476
0
}
477
478
static void setup_ports(struct domain *d)
479
0
{
480
0
    unsigned int port;
481
0
482
0
    /*
483
0
     * For each port that is already bound:
484
0
     *
485
0
     * - save its pending state.
486
0
     * - set default priority.
487
0
     */
488
0
    for ( port = 1; port < d->max_evtchns; port++ )
489
0
    {
490
0
        struct evtchn *evtchn;
491
0
492
0
        if ( !port_is_valid(d, port) )
493
0
            break;
494
0
495
0
        evtchn = evtchn_from_port(d, port);
496
0
497
0
        if ( test_bit(port, &shared_info(d, evtchn_pending)) )
498
0
            evtchn->pending = 1;
499
0
500
0
        evtchn_fifo_set_priority(d, evtchn, EVTCHN_FIFO_PRIORITY_DEFAULT);
501
0
    }
502
0
}
503
504
int evtchn_fifo_init_control(struct evtchn_init_control *init_control)
505
0
{
506
0
    struct domain *d = current->domain;
507
0
    uint32_t vcpu_id;
508
0
    uint64_t gfn;
509
0
    uint32_t offset;
510
0
    struct vcpu *v;
511
0
    int rc;
512
0
513
0
    init_control->link_bits = EVTCHN_FIFO_LINK_BITS;
514
0
515
0
    vcpu_id = init_control->vcpu;
516
0
    gfn     = init_control->control_gfn;
517
0
    offset  = init_control->offset;
518
0
519
0
    if ( vcpu_id >= d->max_vcpus || !d->vcpu[vcpu_id] )
520
0
        return -ENOENT;
521
0
    v = d->vcpu[vcpu_id];
522
0
523
0
    /* Must not cross page boundary. */
524
0
    if ( offset > (PAGE_SIZE - sizeof(evtchn_fifo_control_block_t)) )
525
0
        return -EINVAL;
526
0
527
0
    /* Must be 8-bytes aligned. */
528
0
    if ( offset & (8 - 1) )
529
0
        return -EINVAL;
530
0
531
0
    spin_lock(&d->event_lock);
532
0
533
0
    /*
534
0
     * If this is the first control block, setup an empty event array
535
0
     * and switch to the fifo port ops.
536
0
     */
537
0
    if ( !d->evtchn_fifo )
538
0
    {
539
0
        struct vcpu *vcb;
540
0
541
0
        for_each_vcpu ( d, vcb ) {
542
0
            rc = setup_control_block(vcb);
543
0
            if ( rc < 0 )
544
0
                goto error;
545
0
        }
546
0
547
0
        rc = setup_event_array(d);
548
0
        if ( rc < 0 )
549
0
            goto error;
550
0
551
0
        rc = map_control_block(v, gfn, offset);
552
0
        if ( rc < 0 )
553
0
            goto error;
554
0
555
0
        d->evtchn_port_ops = &evtchn_port_ops_fifo;
556
0
        d->max_evtchns = EVTCHN_FIFO_NR_CHANNELS;
557
0
        setup_ports(d);
558
0
    }
559
0
    else
560
0
        rc = map_control_block(v, gfn, offset);
561
0
562
0
    spin_unlock(&d->event_lock);
563
0
564
0
    return rc;
565
0
566
0
 error:
567
0
    evtchn_fifo_destroy(d);
568
0
    spin_unlock(&d->event_lock);
569
0
    return rc;
570
0
}
571
572
static int add_page_to_event_array(struct domain *d, unsigned long gfn)
573
0
{
574
0
    void *virt;
575
0
    unsigned int slot;
576
0
    unsigned int port = d->evtchn_fifo->num_evtchns;
577
0
    int rc;
578
0
579
0
    slot = d->evtchn_fifo->num_evtchns / EVTCHN_FIFO_EVENT_WORDS_PER_PAGE;
580
0
    if ( slot >= EVTCHN_FIFO_MAX_EVENT_ARRAY_PAGES )
581
0
        return -ENOSPC;
582
0
583
0
    rc = map_guest_page(d, gfn, &virt);
584
0
    if ( rc < 0 )
585
0
        return rc;
586
0
587
0
    d->evtchn_fifo->event_array[slot] = virt;
588
0
589
0
    /* Synchronize with evtchn_fifo_word_from_port(). */
590
0
    smp_wmb();
591
0
592
0
    d->evtchn_fifo->num_evtchns += EVTCHN_FIFO_EVENT_WORDS_PER_PAGE;
593
0
594
0
    /*
595
0
     * Re-raise any events that were pending while this array page was
596
0
     * missing.
597
0
     */
598
0
    for ( ; port < d->evtchn_fifo->num_evtchns; port++ )
599
0
    {
600
0
        struct evtchn *evtchn;
601
0
602
0
        if ( !port_is_valid(d, port) )
603
0
            break;
604
0
605
0
        evtchn = evtchn_from_port(d, port);
606
0
        if ( evtchn->pending )
607
0
            evtchn_fifo_set_pending(d->vcpu[evtchn->notify_vcpu_id], evtchn);
608
0
    }
609
0
610
0
    return 0;
611
0
}
612
613
int evtchn_fifo_expand_array(const struct evtchn_expand_array *expand_array)
614
0
{
615
0
    struct domain *d = current->domain;
616
0
    int rc;
617
0
618
0
    if ( !d->evtchn_fifo )
619
0
        return -EOPNOTSUPP;
620
0
621
0
    spin_lock(&d->event_lock);
622
0
    rc = add_page_to_event_array(d, expand_array->array_gfn);
623
0
    spin_unlock(&d->event_lock);
624
0
625
0
    return rc;
626
0
}
627
628
void evtchn_fifo_destroy(struct domain *d)
629
0
{
630
0
    struct vcpu *v;
631
0
632
0
    for_each_vcpu( d, v )
633
0
        cleanup_control_block(v);
634
0
    cleanup_event_array(d);
635
0
}
636
637
/*
638
 * Local variables:
639
 * mode: C
640
 * c-file-style: "BSD"
641
 * c-basic-offset: 4
642
 * tab-width: 4
643
 * indent-tabs-mode: nil
644
 * End:
645
 */