L4Re/departure

Annotated libfsserver/lib/generic/notification.cc

645:3047b11cc814
7 months ago Paul Boddie Fixed the file_data_available result to return zero if the populated span has somehow become less than the current position in the memory region.
paul@133 1
/*
paul@133 2
 * Notification support.
paul@133 3
 *
paul@481 4
 * Copyright (C) 2021, 2022, 2023 Paul Boddie <paul@boddie.org.uk>
paul@133 5
 *
paul@133 6
 * This program is free software; you can redistribute it and/or
paul@133 7
 * modify it under the terms of the GNU General Public License as
paul@133 8
 * published by the Free Software Foundation; either version 2 of
paul@133 9
 * the License, or (at your option) any later version.
paul@133 10
 *
paul@133 11
 * This program is distributed in the hope that it will be useful,
paul@133 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@133 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@133 14
 * GNU General Public License for more details.
paul@133 15
 *
paul@133 16
 * You should have received a copy of the GNU General Public License
paul@133 17
 * along with this program; if not, write to the Free Software
paul@133 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@133 19
 * Boston, MA  02110-1301, USA
paul@133 20
 */
paul@133 21
paul@133 22
#include <ipc/cap_alloc.h>
paul@535 23
#include <resource/resource_server.h>
paul@133 24
paul@133 25
#include "notification.h"
paul@133 26
#include "notifier_client.h"
paul@288 27
paul@133 28
paul@133 29
paul@133 30
/* Initialise endpoints and flags for notifications. */
paul@133 31
paul@133 32
NotificationSupport::NotificationSupport(unsigned int endpoints)
paul@138 33
: _min_endpoints(endpoints)
paul@133 34
{
paul@138 35
    _endpoints.resize(_min_endpoints);
paul@133 36
}
paul@133 37
paul@193 38
NotificationSupport::~NotificationSupport()
paul@193 39
{
paul@193 40
}
paul@193 41
paul@485 42
/* Return an endpoint number, removing the mapping from the given notifier if
paul@485 43
   indicated. */
paul@485 44
paul@485 45
long NotificationSupport::_get_endpoint(l4_cap_idx_t notifier, bool remove, unsigned int *endpoint)
paul@485 46
{
paul@485 47
    NotifierEndpointMap::iterator itp = _subscribers.find(notifier);
paul@485 48
paul@485 49
    if (itp == _subscribers.end())
paul@485 50
        return -L4_ENOENT;
paul@485 51
paul@485 52
    *endpoint = itp->second;
paul@485 53
paul@485 54
    if (remove)
paul@485 55
        _subscribers.erase(itp);
paul@485 56
paul@485 57
    return L4_EOK;
paul@485 58
}
paul@485 59
paul@140 60
/* Subscribe to notifications using a notification object, returning the
paul@140 61
   endpoint number. */
paul@140 62
paul@485 63
long NotificationSupport::subscribe(l4_cap_idx_t notifier, notify_flags_t flags)
paul@140 64
{
paul@140 65
    std::lock_guard<std::mutex> guard(_lock);
paul@140 66
paul@140 67
    unsigned int endpoint = _endpoints.size();
paul@140 68
    _endpoints.resize(endpoint + 1);
paul@140 69
paul@484 70
    /* Propagate deferred flags and values for new endpoints. */
paul@140 71
paul@484 72
    if (_deferred_flags)
paul@484 73
    {
paul@484 74
        _endpoints[endpoint].deferred_flags = _deferred_flags;
paul@484 75
        _endpoints[endpoint].deferred_values = _deferred_values;
paul@484 76
    }
paul@140 77
paul@485 78
    return _subscribe(endpoint, notifier, flags);
paul@140 79
}
paul@140 80
paul@140 81
/* Subscribe to a specific endpoint's notifications using a notification
paul@140 82
   object. */
paul@133 83
paul@288 84
long NotificationSupport::subscribe(unsigned int endpoint, l4_cap_idx_t notifier,
paul@485 85
                                    notify_flags_t flags)
paul@133 86
{
paul@140 87
    std::lock_guard<std::mutex> guard(_lock);
paul@140 88
paul@138 89
    if (endpoint >= _endpoints.size())
paul@138 90
        _endpoints.resize(endpoint + 1);
paul@138 91
paul@400 92
    /* Propagate deferred flags for new endpoints. */
paul@140 93
paul@484 94
    if (_deferred_flags)
paul@484 95
    {
paul@484 96
        _endpoints[endpoint].deferred_flags = _deferred_flags;
paul@484 97
        _endpoints[endpoint].deferred_values = _deferred_values;
paul@484 98
    }
paul@140 99
paul@485 100
    return _subscribe(endpoint, notifier, flags);
paul@288 101
}
paul@288 102
paul@288 103
/* Unsubscribe from an endpoint's notifications. */
paul@288 104
paul@488 105
long NotificationSupport::unsubscribe(l4_cap_idx_t notifier)
paul@288 106
{
paul@288 107
    std::lock_guard<std::mutex> guard(_lock);
paul@288 108
paul@485 109
    _unsubscribe(notifier);
paul@488 110
    return L4_EOK;
paul@140 111
}
paul@140 112
paul@140 113
/* Common subscription functionality. */
paul@140 114
paul@288 115
long NotificationSupport::_subscribe(unsigned int endpoint, l4_cap_idx_t notifier,
paul@485 116
                                     notify_flags_t flags)
paul@140 117
{
paul@288 118
    /* Record details of the notifier itself. */
paul@288 119
paul@138 120
    NotificationEndpoint &ep = _endpoints[endpoint];
paul@138 121
paul@138 122
    ep.notifiers.insert(notifier);
paul@138 123
    ep.flags = flags;
paul@133 124
paul@133 125
    /* Send deferred conditions held from before subscription occurred. */
paul@133 126
paul@484 127
    if (ep.deferred_flags)
paul@133 128
    {
paul@484 129
        _notify(endpoint, ep.deferred_flags, ep.deferred_values);
paul@484 130
        ep.deferred_flags = 0;
paul@484 131
        ep.deferred_values = NOTIFY_VALUES_NULL;
paul@133 132
    }
paul@288 133
paul@485 134
    /* Record the notifier's endpoint. */
paul@288 135
paul@485 136
    _subscribers[notifier] = endpoint;
paul@290 137
    return L4_EOK;
paul@288 138
}
paul@288 139
paul@485 140
void NotificationSupport::_unsubscribe(l4_cap_idx_t notifier)
paul@288 141
{
paul@485 142
    /* Find and remove the notifier endpoint record. */
paul@290 143
paul@485 144
    unsigned int endpoint;
paul@485 145
    long err = _get_endpoint(notifier, true, &endpoint);
paul@290 146
paul@485 147
    if (err)
paul@290 148
        return;
paul@290 149
paul@290 150
    /* Release the notifier. */
paul@290 151
paul@288 152
    NotificationEndpoint &ep = _endpoints[endpoint];
paul@288 153
    NotifierSet::iterator it = ep.notifiers.find(notifier);
paul@288 154
paul@288 155
    if (it != ep.notifiers.end())
paul@288 156
    {
paul@288 157
        ep.notifiers.erase(it);
paul@288 158
        ipc_cap_free_um(notifier);
paul@288 159
paul@288 160
        if (ep.notifiers.empty())
paul@288 161
        {
paul@288 162
            ep.flags = 0;
paul@484 163
            ep.deferred_flags = 0;
paul@484 164
            ep.deferred_values = NOTIFY_VALUES_NULL;
paul@288 165
        }
paul@288 166
    }
paul@133 167
}
paul@133 168
paul@137 169
/* Notify a particular endpoint. */
paul@133 170
paul@484 171
void NotificationSupport::notify(unsigned int endpoint, notify_flags_t flags,
paul@484 172
                                 notify_values_t values)
paul@133 173
{
paul@140 174
    std::lock_guard<std::mutex> guard(_lock);
paul@140 175
paul@484 176
    _notify(endpoint, flags, values);
paul@140 177
}
paul@140 178
paul@484 179
void NotificationSupport::_notify(unsigned int endpoint, notify_flags_t flags,
paul@484 180
                                  notify_values_t values)
paul@140 181
{
paul@138 182
    if (endpoint >= _endpoints.size())
paul@138 183
        return;
paul@138 184
paul@138 185
    NotificationEndpoint &ep = _endpoints[endpoint];
paul@138 186
paul@133 187
    /* Notify the endpoint or hold any notification for potential future
paul@133 188
       subscription. */
paul@133 189
paul@138 190
    if (!ep.notifiers.empty())
paul@133 191
    {
paul@138 192
        if (flags & ep.flags)
paul@133 193
        {
paul@138 194
            NotifierSet::iterator it;
paul@133 195
paul@138 196
            for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++)
paul@133 197
            {
paul@133 198
                client_Notifier notifier(*it);
paul@133 199
paul@484 200
                notifier.notify(flags & ep.flags, values);
paul@133 201
            }
paul@133 202
        }
paul@133 203
    }
paul@133 204
    else
paul@484 205
    {
paul@484 206
        ep.deferred_flags = flags;
paul@484 207
        ep.deferred_values = values;
paul@484 208
    }
paul@133 209
}
paul@133 210
paul@211 211
/* Notify all endpoints. */
paul@211 212
paul@484 213
void NotificationSupport::notify_all(notify_flags_t flags, notify_values_t values)
paul@211 214
{
paul@211 215
    std::lock_guard<std::mutex> guard(_lock);
paul@211 216
paul@211 217
    for (unsigned int i = 0; i < _endpoints.size(); i++)
paul@484 218
        _notify(i, flags, values);
paul@211 219
paul@484 220
    /* Accumulate flags, but only record the most recent values. */
paul@484 221
paul@484 222
    _deferred_flags |= flags;
paul@484 223
    _deferred_values = values;
paul@211 224
}
paul@211 225
paul@137 226
/* Notify the other endpoints. */
paul@137 227
paul@484 228
void NotificationSupport::notify_others(unsigned int endpoint, notify_flags_t flags,
paul@484 229
                                        notify_values_t values)
paul@137 230
{
paul@140 231
    std::lock_guard<std::mutex> guard(_lock);
paul@140 232
paul@485 233
    _notify_others(endpoint, flags, values);
paul@485 234
}
paul@485 235
paul@485 236
/* Notify the other endpoints given a notifier. */
paul@485 237
paul@485 238
void NotificationSupport::notify_others(l4_cap_idx_t notifier, notify_flags_t flags,
paul@485 239
                                        notify_values_t values)
paul@485 240
{
paul@485 241
    std::lock_guard<std::mutex> guard(_lock);
paul@485 242
paul@485 243
    unsigned int endpoint;
paul@485 244
    long err = _get_endpoint(notifier, false, &endpoint);
paul@485 245
paul@485 246
    if (err)
paul@485 247
        return;
paul@485 248
paul@485 249
    _notify_others(endpoint, flags, values);
paul@485 250
}
paul@485 251
paul@485 252
void NotificationSupport::_notify_others(unsigned int endpoint,
paul@485 253
                                         notify_flags_t flags,
paul@485 254
                                         notify_values_t values)
paul@485 255
{
paul@138 256
    for (unsigned int i = 0; i < _endpoints.size(); i++)
paul@137 257
        if (i != endpoint)
paul@484 258
            _notify(i, flags, values);
paul@140 259
paul@484 260
    /* Accumulate flags, but only record the most recent values. */
paul@484 261
paul@484 262
    _deferred_flags |= flags;
paul@484 263
    _deferred_values = values;
paul@137 264
}
paul@137 265
paul@133 266
/* Release notifiers for each endpoint. */
paul@133 267
paul@133 268
void NotificationSupport::release_notifiers()
paul@133 269
{
paul@140 270
    std::lock_guard<std::mutex> guard(_lock);
paul@140 271
paul@138 272
    for (unsigned int endpoint = 0; endpoint < _endpoints.size(); endpoint++)
paul@133 273
    {
paul@138 274
        NotificationEndpoint &ep = _endpoints[endpoint];
paul@138 275
        NotifierSet::iterator it;
paul@133 276
paul@138 277
        for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++)
paul@133 278
            ipc_cap_free_um(*it);
paul@133 279
paul@138 280
        ep.notifiers.clear();
paul@138 281
        ep.flags = 0;
paul@484 282
        ep.deferred_flags = 0;
paul@484 283
        ep.deferred_values = NOTIFY_VALUES_NULL;
paul@133 284
    }
paul@133 285
}
paul@133 286
paul@133 287
// vim: tabstop=4 expandtab shiftwidth=4