cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

client-connection.c (11917B)


      1/*
      2 * QEMU Block driver for  NBD
      3 *
      4 * Copyright (c) 2021 Virtuozzo International GmbH.
      5 *
      6 * Permission is hereby granted, free of charge, to any person obtaining a copy
      7 * of this software and associated documentation files (the "Software"), to deal
      8 * in the Software without restriction, including without limitation the rights
      9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10 * copies of the Software, and to permit persons to whom the Software is
     11 * furnished to do so, subject to the following conditions:
     12 *
     13 * The above copyright notice and this permission notice shall be included in
     14 * all copies or substantial portions of the Software.
     15 *
     16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22 * THE SOFTWARE.
     23 */
     24
     25#include "qemu/osdep.h"
     26
     27#include "block/nbd.h"
     28
     29#include "qapi/qapi-visit-sockets.h"
     30#include "qapi/clone-visitor.h"
     31
     32struct NBDClientConnection {
     33    /* Initialization constants, never change */
     34    SocketAddress *saddr; /* address to connect to */
     35    QCryptoTLSCreds *tlscreds;
     36    NBDExportInfo initial_info;
     37    bool do_negotiation;
     38    bool do_retry;
     39
     40    QemuMutex mutex;
     41
     42    /*
     43     * @sioc and @err represent a connection attempt.  While running
     44     * is true, they are only used by the connection thread, and mutex
     45     * locking is not needed.  Once the thread finishes,
     46     * nbd_co_establish_connection then steals these pointers while
     47     * under the mutex.
     48     */
     49    NBDExportInfo updated_info;
     50    QIOChannelSocket *sioc;
     51    QIOChannel *ioc;
     52    Error *err;
     53
     54    /* All further fields are accessed only under mutex */
     55    bool running; /* thread is running now */
     56    bool detached; /* thread is detached and should cleanup the state */
     57
     58    /*
     59     * wait_co: if non-NULL, which coroutine to wake in
     60     * nbd_co_establish_connection() after yield()
     61     */
     62    Coroutine *wait_co;
     63};
     64
     65/*
     66 * The function isn't protected by any mutex, only call it when the client
     67 * connection attempt has not yet started.
     68 */
     69void nbd_client_connection_enable_retry(NBDClientConnection *conn)
     70{
     71    conn->do_retry = true;
     72}
     73
     74NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr,
     75                                               bool do_negotiation,
     76                                               const char *export_name,
     77                                               const char *x_dirty_bitmap,
     78                                               QCryptoTLSCreds *tlscreds)
     79{
     80    NBDClientConnection *conn = g_new(NBDClientConnection, 1);
     81
     82    object_ref(OBJECT(tlscreds));
     83    *conn = (NBDClientConnection) {
     84        .saddr = QAPI_CLONE(SocketAddress, saddr),
     85        .tlscreds = tlscreds,
     86        .do_negotiation = do_negotiation,
     87
     88        .initial_info.request_sizes = true,
     89        .initial_info.structured_reply = true,
     90        .initial_info.base_allocation = true,
     91        .initial_info.x_dirty_bitmap = g_strdup(x_dirty_bitmap),
     92        .initial_info.name = g_strdup(export_name ?: "")
     93    };
     94
     95    qemu_mutex_init(&conn->mutex);
     96
     97    return conn;
     98}
     99
    100static void nbd_client_connection_do_free(NBDClientConnection *conn)
    101{
    102    if (conn->sioc) {
    103        qio_channel_close(QIO_CHANNEL(conn->sioc), NULL);
    104        object_unref(OBJECT(conn->sioc));
    105    }
    106    error_free(conn->err);
    107    qapi_free_SocketAddress(conn->saddr);
    108    object_unref(OBJECT(conn->tlscreds));
    109    g_free(conn->initial_info.x_dirty_bitmap);
    110    g_free(conn->initial_info.name);
    111    g_free(conn);
    112}
    113
    114/*
    115 * Connect to @addr and do NBD negotiation if @info is not null. If @tlscreds
    116 * are given @outioc is returned. @outioc is provided only on success.  The call
    117 * may be cancelled from other thread by simply qio_channel_shutdown(sioc).
    118 */
    119static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr,
    120                       NBDExportInfo *info, QCryptoTLSCreds *tlscreds,
    121                       QIOChannel **outioc, Error **errp)
    122{
    123    int ret;
    124
    125    if (outioc) {
    126        *outioc = NULL;
    127    }
    128
    129    ret = qio_channel_socket_connect_sync(sioc, addr, errp);
    130    if (ret < 0) {
    131        return ret;
    132    }
    133
    134    qio_channel_set_delay(QIO_CHANNEL(sioc), false);
    135
    136    if (!info) {
    137        return 0;
    138    }
    139
    140    ret = nbd_receive_negotiate(NULL, QIO_CHANNEL(sioc), tlscreds,
    141                                tlscreds ? addr->u.inet.host : NULL,
    142                                outioc, info, errp);
    143    if (ret < 0) {
    144        /*
    145         * nbd_receive_negotiate() may setup tls ioc and return it even on
    146         * failure path. In this case we should use it instead of original
    147         * channel.
    148         */
    149        if (outioc && *outioc) {
    150            qio_channel_close(QIO_CHANNEL(*outioc), NULL);
    151            object_unref(OBJECT(*outioc));
    152            *outioc = NULL;
    153        } else {
    154            qio_channel_close(QIO_CHANNEL(sioc), NULL);
    155        }
    156
    157        return ret;
    158    }
    159
    160    return 0;
    161}
    162
    163static void *connect_thread_func(void *opaque)
    164{
    165    NBDClientConnection *conn = opaque;
    166    int ret;
    167    bool do_free;
    168    uint64_t timeout = 1;
    169    uint64_t max_timeout = 16;
    170
    171    qemu_mutex_lock(&conn->mutex);
    172    while (!conn->detached) {
    173        assert(!conn->sioc);
    174        conn->sioc = qio_channel_socket_new();
    175
    176        qemu_mutex_unlock(&conn->mutex);
    177
    178        error_free(conn->err);
    179        conn->err = NULL;
    180        conn->updated_info = conn->initial_info;
    181
    182        ret = nbd_connect(conn->sioc, conn->saddr,
    183                          conn->do_negotiation ? &conn->updated_info : NULL,
    184                          conn->tlscreds, &conn->ioc, &conn->err);
    185
    186        /*
    187         * conn->updated_info will finally be returned to the user. Clear the
    188         * pointers to our internally allocated strings, which are IN parameters
    189         * of nbd_receive_negotiate() and therefore nbd_connect(). Caller
    190         * shoudn't be interested in these fields.
    191         */
    192        conn->updated_info.x_dirty_bitmap = NULL;
    193        conn->updated_info.name = NULL;
    194
    195        qemu_mutex_lock(&conn->mutex);
    196
    197        if (ret < 0) {
    198            object_unref(OBJECT(conn->sioc));
    199            conn->sioc = NULL;
    200            if (conn->do_retry && !conn->detached) {
    201                qemu_mutex_unlock(&conn->mutex);
    202
    203                sleep(timeout);
    204                if (timeout < max_timeout) {
    205                    timeout *= 2;
    206                }
    207
    208                qemu_mutex_lock(&conn->mutex);
    209                continue;
    210            }
    211        }
    212
    213        break;
    214    }
    215
    216    /* mutex is locked */
    217
    218    assert(conn->running);
    219    conn->running = false;
    220    if (conn->wait_co) {
    221        aio_co_wake(conn->wait_co);
    222        conn->wait_co = NULL;
    223    }
    224    do_free = conn->detached;
    225
    226    qemu_mutex_unlock(&conn->mutex);
    227
    228    if (do_free) {
    229        nbd_client_connection_do_free(conn);
    230    }
    231
    232    return NULL;
    233}
    234
    235void nbd_client_connection_release(NBDClientConnection *conn)
    236{
    237    bool do_free = false;
    238
    239    if (!conn) {
    240        return;
    241    }
    242
    243    WITH_QEMU_LOCK_GUARD(&conn->mutex) {
    244        assert(!conn->detached);
    245        if (conn->running) {
    246            conn->detached = true;
    247        } else {
    248            do_free = true;
    249        }
    250        if (conn->sioc) {
    251            qio_channel_shutdown(QIO_CHANNEL(conn->sioc),
    252                                 QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
    253        }
    254    }
    255
    256    if (do_free) {
    257        nbd_client_connection_do_free(conn);
    258    }
    259}
    260
    261/*
    262 * Get a new connection in context of @conn:
    263 *   if the thread is running, wait for completion
    264 *   if the thread already succeeded in the background, and user didn't get the
    265 *     result, just return it now
    266 *   otherwise the thread is not running, so start a thread and wait for
    267 *     completion
    268 *
    269 * If @blocking is false, don't wait for the thread, return immediately.
    270 *
    271 * If @info is not NULL, also do nbd-negotiation after successful connection.
    272 * In this case info is used only as out parameter, and is fully initialized by
    273 * nbd_co_establish_connection(). "IN" fields of info as well as related only to
    274 * nbd_receive_export_list() would be zero (see description of NBDExportInfo in
    275 * include/block/nbd.h).
    276 */
    277QIOChannel *coroutine_fn
    278nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info,
    279                            bool blocking, Error **errp)
    280{
    281    QemuThread thread;
    282
    283    if (conn->do_negotiation) {
    284        assert(info);
    285    }
    286
    287    WITH_QEMU_LOCK_GUARD(&conn->mutex) {
    288        /*
    289         * Don't call nbd_co_establish_connection() in several coroutines in
    290         * parallel. Only one call at once is supported.
    291         */
    292        assert(!conn->wait_co);
    293
    294        if (!conn->running) {
    295            if (conn->sioc) {
    296                /* Previous attempt finally succeeded in background */
    297                if (conn->do_negotiation) {
    298                    memcpy(info, &conn->updated_info, sizeof(*info));
    299                    if (conn->ioc) {
    300                        /* TLS channel now has own reference to parent */
    301                        object_unref(OBJECT(conn->sioc));
    302                        conn->sioc = NULL;
    303
    304                        return g_steal_pointer(&conn->ioc);
    305                    }
    306                }
    307
    308                assert(!conn->ioc);
    309
    310                return QIO_CHANNEL(g_steal_pointer(&conn->sioc));
    311            }
    312
    313            conn->running = true;
    314            error_free(conn->err);
    315            conn->err = NULL;
    316            qemu_thread_create(&thread, "nbd-connect",
    317                               connect_thread_func, conn, QEMU_THREAD_DETACHED);
    318        }
    319
    320        if (!blocking) {
    321            error_setg(errp, "No connection at the moment");
    322            return NULL;
    323        }
    324
    325        conn->wait_co = qemu_coroutine_self();
    326    }
    327
    328    /*
    329     * We are going to wait for connect-thread finish, but
    330     * nbd_co_establish_connection_cancel() can interrupt.
    331     */
    332    qemu_coroutine_yield();
    333
    334    WITH_QEMU_LOCK_GUARD(&conn->mutex) {
    335        if (conn->running) {
    336            /*
    337             * The connection attempt was canceled and the coroutine resumed
    338             * before the connection thread finished its job.  Report the
    339             * attempt as failed, but leave the connection thread running,
    340             * to reuse it for the next connection attempt.
    341             */
    342            error_setg(errp, "Connection attempt cancelled by other operation");
    343            return NULL;
    344        } else {
    345            error_propagate(errp, conn->err);
    346            conn->err = NULL;
    347            if (!conn->sioc) {
    348                return NULL;
    349            }
    350            if (conn->do_negotiation) {
    351                memcpy(info, &conn->updated_info, sizeof(*info));
    352                if (conn->ioc) {
    353                    /* TLS channel now has own reference to parent */
    354                    object_unref(OBJECT(conn->sioc));
    355                    conn->sioc = NULL;
    356
    357                    return g_steal_pointer(&conn->ioc);
    358                }
    359            }
    360
    361            assert(!conn->ioc);
    362
    363            return QIO_CHANNEL(g_steal_pointer(&conn->sioc));
    364        }
    365    }
    366
    367    abort(); /* unreachable */
    368}
    369
    370/*
    371 * nbd_co_establish_connection_cancel
    372 * Cancel nbd_co_establish_connection() asynchronously.
    373 *
    374 * Note that this function neither directly stops the thread nor closes the
    375 * socket, but rather safely wakes nbd_co_establish_connection() which is
    376 * sleeping in yield()
    377 */
    378void nbd_co_establish_connection_cancel(NBDClientConnection *conn)
    379{
    380    Coroutine *wait_co;
    381
    382    WITH_QEMU_LOCK_GUARD(&conn->mutex) {
    383        wait_co = g_steal_pointer(&conn->wait_co);
    384    }
    385
    386    if (wait_co) {
    387        aio_co_wake(wait_co);
    388    }
    389}