Revision 055b889f hw/virtio-serial-bus.c

b/hw/virtio-serial-bus.c
41 41
    VirtIOSerialBus *bus;
42 42

  
43 43
    QTAILQ_HEAD(, VirtIOSerialPort) ports;
44

  
45
    /* bitmap for identifying active ports */
46
    uint32_t *ports_map;
47

  
44 48
    struct virtio_console_config config;
45 49
};
46 50

  
......
48 52
{
49 53
    VirtIOSerialPort *port;
50 54

  
55
    if (id == VIRTIO_CONSOLE_BAD_ID) {
56
        return NULL;
57
    }
58

  
51 59
    QTAILQ_FOREACH(port, &vser->ports, next) {
52 60
        if (port->id == id)
53 61
            return port;
......
208 216
    size_t buffer_len;
209 217

  
210 218
    gcpkt = buf;
211
    port = find_port_by_id(vser, ldl_p(&gcpkt->id));
212
    if (!port)
213
        return;
214 219

  
215 220
    cpkt.event = lduw_p(&gcpkt->event);
216 221
    cpkt.value = lduw_p(&gcpkt->value);
217 222

  
223
    port = find_port_by_id(vser, ldl_p(&gcpkt->id));
224
    if (!port && cpkt.event != VIRTIO_CONSOLE_DEVICE_READY)
225
        return;
226

  
218 227
    switch(cpkt.event) {
228
    case VIRTIO_CONSOLE_DEVICE_READY:
229
        /*
230
         * The device is up, we can now tell the device about all the
231
         * ports we have here.
232
         */
233
        QTAILQ_FOREACH(port, &vser->ports, next) {
234
            send_control_event(port, VIRTIO_CONSOLE_PORT_ADD, 1);
235
        }
236
        break;
237

  
219 238
    case VIRTIO_CONSOLE_PORT_READY:
220 239
        /*
221 240
         * Now that we know the guest asked for the port name, we're
......
363 382
    VirtIOSerial *s = opaque;
364 383
    VirtIOSerialPort *port;
365 384
    uint32_t nr_active_ports;
385
    unsigned int i;
366 386

  
367 387
    /* The virtio device */
368 388
    virtio_save(&s->vdev, f);
......
370 390
    /* The config space */
371 391
    qemu_put_be16s(f, &s->config.cols);
372 392
    qemu_put_be16s(f, &s->config.rows);
373
    qemu_put_be32s(f, &s->config.nr_ports);
374 393

  
375
    /* Items in struct VirtIOSerial */
394
    qemu_put_be32s(f, &s->config.max_nr_ports);
395

  
396
    /* The ports map */
397

  
398
    for (i = 0; i < (s->config.max_nr_ports + 31) / 32; i++) {
399
        qemu_put_be32s(f, &s->ports_map[i]);
400
    }
376 401

  
377
    qemu_put_be32s(f, &s->bus->max_nr_ports);
402
    /* Ports */
378 403

  
379
    /* Do this because we might have hot-unplugged some ports */
380 404
    nr_active_ports = 0;
381 405
    QTAILQ_FOREACH(port, &s->ports, next) {
382 406
        nr_active_ports++;
......
388 412
     * Items in struct VirtIOSerialPort.
389 413
     */
390 414
    QTAILQ_FOREACH(port, &s->ports, next) {
391
        /*
392
         * We put the port number because we may not have an active
393
         * port at id 0 that's reserved for a console port, or in case
394
         * of ports that might have gotten unplugged
395
         */
396 415
        qemu_put_be32s(f, &port->id);
397 416
        qemu_put_byte(f, port->guest_connected);
398 417
        qemu_put_byte(f, port->host_connected);
......
403 422
{
404 423
    VirtIOSerial *s = opaque;
405 424
    VirtIOSerialPort *port;
406
    uint32_t max_nr_ports, nr_active_ports, nr_ports;
425
    size_t ports_map_size;
426
    uint32_t max_nr_ports, nr_active_ports, *ports_map;
407 427
    unsigned int i;
408 428

  
409 429
    if (version_id > 2) {
......
420 440
    /* The config space */
421 441
    qemu_get_be16s(f, &s->config.cols);
422 442
    qemu_get_be16s(f, &s->config.rows);
423
    nr_ports = qemu_get_be32(f);
424 443

  
425
    if (nr_ports != s->config.nr_ports) {
426
        /*
427
         * Source hot-plugged/unplugged ports and we don't have all of
428
         * them here.
429
         *
430
         * Note: This condition cannot check for all hotplug/unplug
431
         * events: eg, if one port was hot-plugged and one was
432
         * unplugged, the nr_ports remains the same but the port id's
433
         * would have changed and we won't catch it here. A later
434
         * check for !find_port_by_id() will confirm if this happened.
435
         */
444
    qemu_get_be32s(f, &max_nr_ports);
445
    if (max_nr_ports > s->config.max_nr_ports) {
446
        /* Source could have had more ports than us. Fail migration. */
436 447
        return -EINVAL;
437 448
    }
438 449

  
439
    /* Items in struct VirtIOSerial */
450
    ports_map_size = sizeof(uint32_t) * (max_nr_ports + 31) / 32;
451
    ports_map = qemu_malloc(ports_map_size);
440 452

  
441
    qemu_get_be32s(f, &max_nr_ports);
442
    if (max_nr_ports > s->bus->max_nr_ports) {
443
        /* Source could have more ports than us. Fail migration. */
444
        return -EINVAL;
453
    for (i = 0; i < (max_nr_ports + 31) / 32; i++) {
454
        qemu_get_be32s(f, &ports_map[i]);
455

  
456
        if (ports_map[i] != s->ports_map[i]) {
457
            /*
458
             * Ports active on source and destination don't
459
             * match. Fail migration.
460
             */
461
            qemu_free(ports_map);
462
            return -EINVAL;
463
        }
445 464
    }
465
    qemu_free(ports_map);
446 466

  
447 467
    qemu_get_be32s(f, &nr_active_ports);
448 468

  
......
453 473

  
454 474
        id = qemu_get_be32(f);
455 475
        port = find_port_by_id(s, id);
456
        if (!port) {
457
            /*
458
             * The requested port was hot-plugged on the source but we
459
             * don't have it
460
             */
461
            return -EINVAL;
462
        }
463 476

  
464 477
        port->guest_connected = qemu_get_byte(f);
465 478
        host_connected = qemu_get_byte(f);
......
472 485
                               port->host_connected);
473 486
        }
474 487
    }
475

  
476 488
    return 0;
477 489
}
478 490

  
......
507 519
                   indent, "", port->host_connected);
508 520
}
509 521

  
522
/* This function is only used if a port id is not provided by the user */
523
static uint32_t find_free_port_id(VirtIOSerial *vser)
524
{
525
    unsigned int i;
526

  
527
    for (i = 0; i < (vser->config.max_nr_ports + 31) / 32; i++) {
528
        uint32_t map, bit;
529

  
530
        map = vser->ports_map[i];
531
        bit = ffs(~map);
532
        if (bit) {
533
            return (bit - 1) + i * 32;
534
        }
535
    }
536
    return VIRTIO_CONSOLE_BAD_ID;
537
}
538

  
539
static void mark_port_added(VirtIOSerial *vser, uint32_t port_id)
540
{
541
    unsigned int i;
542

  
543
    i = port_id / 32;
544
    vser->ports_map[i] |= 1U << (port_id % 32);
545
}
546

  
547
static void add_port(VirtIOSerial *vser, uint32_t port_id)
548
{
549
    mark_port_added(vser, port_id);
550

  
551
    send_control_event(find_port_by_id(vser, port_id),
552
                       VIRTIO_CONSOLE_PORT_ADD, 1);
553
}
554

  
555
static void remove_port(VirtIOSerial *vser, uint32_t port_id)
556
{
557
    unsigned int i;
558

  
559
    i = port_id / 32;
560
    vser->ports_map[i] &= ~(1U << (port_id % 32));
561

  
562
    send_control_event(find_port_by_id(vser, port_id),
563
                       VIRTIO_CONSOLE_PORT_REMOVE, 1);
564
}
565

  
510 566
static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base)
511 567
{
512 568
    VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev);
......
525 581
     */
526 582
    plugging_port0 = port->is_console && !find_port_by_id(port->vser, 0);
527 583

  
528
    if (port->vser->config.nr_ports == bus->max_nr_ports && !plugging_port0) {
529
        error_report("virtio-serial-bus: Maximum device limit reached");
584
    if (find_port_by_id(port->vser, port->id)) {
585
        error_report("virtio-serial-bus: A port already exists at id %u\n",
586
                     port->id);
530 587
        return -1;
531 588
    }
532
    dev->info = info;
533 589

  
590
    if (port->id == VIRTIO_CONSOLE_BAD_ID) {
591
        if (plugging_port0) {
592
            port->id = 0;
593
        } else {
594
            port->id = find_free_port_id(port->vser);
595
            if (port->id == VIRTIO_CONSOLE_BAD_ID) {
596
                error_report("virtio-serial-bus: Maximum port limit for this device reached\n");
597
                return -1;
598
            }
599
        }
600
    }
601

  
602
    if (port->id >= port->vser->config.max_nr_ports) {
603
        error_report("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u\n",
604
                     port->vser->config.max_nr_ports - 1);
605
        return -1;
606
    }
607

  
608
    dev->info = info;
534 609
    ret = info->init(dev);
535 610
    if (ret) {
536 611
        return ret;
537 612
    }
538 613

  
539
    port->id = plugging_port0 ? 0 : port->vser->config.nr_ports++;
540

  
541 614
    if (!use_multiport(port->vser)) {
542 615
        /*
543 616
         * Allow writes to guest in this case; we have no way of
......
550 623
    port->ivq = port->vser->ivqs[port->id];
551 624
    port->ovq = port->vser->ovqs[port->id];
552 625

  
626
    add_port(port->vser, port->id);
627

  
553 628
    /* Send an update to the guest about this new port added */
554 629
    virtio_notify_config(&port->vser->vdev);
555 630

  
......
562 637
    VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
563 638
    VirtIOSerial *vser = port->vser;
564 639

  
565
    send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1);
640
    remove_port(port->vser, port->id);
566 641

  
567
    /*
568
     * Don't decrement nr_ports here; thus we keep a linearly
569
     * increasing port id. Not utilising an id again saves us a couple
570
     * of complications:
571
     *
572
     * - Not having to bother about sending the port id to the guest
573
     *   kernel on hotplug or on addition of new ports; the guest can
574
     *   also linearly increment the port number. This is preferable
575
     *   because the config space won't have the need to store a
576
     *   ports_map.
577
     *
578
     * - Extra state to be stored for all the "holes" that got created
579
     *   so that we keep filling in the ids from the least available
580
     *   index.
581
     *
582
     * When such a functionality is desired, a control message to add
583
     * a port can be introduced.
584
     */
585 642
    QTAILQ_REMOVE(&vser->ports, port, next);
586 643

  
587 644
    if (port->info->exit)
......
641 698
    }
642 699

  
643 700
    vser->config.max_nr_ports = max_nr_ports;
701
    vser->ports_map = qemu_mallocz((max_nr_ports + 31) / 32);
644 702
    /*
645 703
     * Reserve location 0 for a console port for backward compat
646 704
     * (old kernel, new qemu)
647 705
     */
648
    vser->config.nr_ports = 1;
706
    mark_port_added(vser, 0);
649 707

  
650 708
    vser->vdev.get_features = get_features;
651 709
    vser->vdev.get_config = get_config;

Also available in: Unified diff