L4Re/departure

Annotated libfsserver/lib/pipes/pipe_pager.cc

544:754959243588
19 months ago Paul Boddie Sought to prevent a race condition when deleting pipe paging coordinators.
paul@93 1
/*
paul@93 2
 * A pipe pager providing access to pipe content and navigation support.
paul@93 3
 *
paul@485 4
 * Copyright (C) 2021, 2022, 2023 Paul Boddie <paul@boddie.org.uk>
paul@93 5
 *
paul@93 6
 * This program is free software; you can redistribute it and/or
paul@93 7
 * modify it under the terms of the GNU General Public License as
paul@93 8
 * published by the Free Software Foundation; either version 2 of
paul@93 9
 * the License, or (at your option) any later version.
paul@93 10
 *
paul@93 11
 * This program is distributed in the hope that it will be useful,
paul@93 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@93 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@93 14
 * GNU General Public License for more details.
paul@93 15
 *
paul@93 16
 * You should have received a copy of the GNU General Public License
paul@93 17
 * along with this program; if not, write to the Free Software
paul@93 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@93 19
 * Boston, MA  02110-1301, USA
paul@93 20
 */
paul@93 21
paul@65 22
#include "pipe_pager.h"
paul@65 23
#include "pipe_object_server.h"
paul@65 24
paul@72 25
paul@72 26
paul@65 27
/* Initialise a pager for a pipe with a shared page mapper for moderating
paul@65 28
   access to loaded pages. At first, no mapper will be configured: this must be
paul@65 29
   done by requesting a region. */
paul@65 30
paul@66 31
PipePager::PipePager(PipePaging *paging, bool writing)
paul@87 32
: Pager(NULL, writing ? L4RE_DS_MAP_FLAG_RW : L4RE_DS_MAP_FLAG_RO),
paul@66 33
  _paging(paging), _writing(writing)
paul@65 34
{
paul@66 35
    /* Initialise the size of the paged region. */
paul@65 36
paul@66 37
    _size = _paging->region_size();
paul@166 38
paul@166 39
    /* Obtain any initial page mapper, this having been set up in the paging
paul@166 40
       coordinator. */
paul@166 41
paul@166 42
    _mapper = _paging->current_region(_writing);
paul@65 43
}
paul@65 44
paul@459 45
ipc_server_default_config_type PipePager::config()
paul@65 46
{
paul@459 47
    return config_PipeObject;
paul@65 48
}
paul@65 49
paul@65 50
paul@65 51
paul@127 52
/* Close the pager, detaching from the paging coordinator for the pipe. When
paul@127 53
   both pagers have detached, this will release all active page mappers and
paul@127 54
   related resources. */
paul@67 55
paul@67 56
void PipePager::close()
paul@67 57
{
paul@199 58
    if (_paging == NULL)
paul@199 59
        return;
paul@199 60
paul@386 61
    /* Notify the other endpoint and unsubscribe. */
paul@117 62
paul@484 63
    _paging->notify_others(_writing ? PipePaging::WRITER : PipePaging::READER,
paul@484 64
                           NOTIFY_PEER_CLOSED, NOTIFY_VALUES_NULL);
paul@488 65
    unsubscribe(_notifier);
paul@199 66
paul@199 67
    /* Deallocate the paging coordinator if no other endpoints are active. */
paul@199 68
paul@544 69
    if (!_paging->detach())
paul@199 70
    {
paul@199 71
        delete _paging;
paul@199 72
        _paging = NULL;
paul@199 73
    }
paul@67 74
}
paul@67 75
paul@65 76
/* Support paging. */
paul@65 77
paul@410 78
long PipePager::map(offset_t offset, map_address_t hot_spot, map_flags_t flags, l4_snd_fpage_t *region)
paul@65 79
{
paul@65 80
    return Pager::map(offset, hot_spot, flags, region);
paul@65 81
}
paul@65 82
paul@65 83
paul@65 84
paul@117 85
/* Return whether the pipe is closed or partly closed. */
paul@117 86
paul@117 87
long PipePager::closed(int *closed)
paul@117 88
{
paul@117 89
    *closed = _paging->closed();
paul@117 90
    return L4_EOK;
paul@117 91
}
paul@117 92
paul@65 93
/* Return details of the current region. */
paul@65 94
paul@65 95
long PipePager::current_region(offset_t *populated_size, offset_t *size)
paul@65 96
{
paul@65 97
    if (_mapper != NULL)
paul@65 98
    {
paul@65 99
        *populated_size = _mapper->get_data_size();
paul@65 100
        *size = _size;
paul@65 101
        return L4_EOK;
paul@65 102
    }
paul@65 103
    else
paul@114 104
        return pipe_error();
paul@65 105
}
paul@65 106
paul@65 107
/* Obtain the next region and its details. */
paul@65 108
paul@65 109
long PipePager::next_region(offset_t *populated_size, offset_t *size)
paul@65 110
{
paul@65 111
    /* Obtain a new region if writing. */
paul@65 112
paul@66 113
    if (_writing)
paul@65 114
        return next_region_for_writer(populated_size, size);
paul@65 115
    else
paul@65 116
        return next_region_for_reader(populated_size, size);
paul@65 117
}
paul@65 118
paul@65 119
long PipePager::next_region_for_reader(offset_t *populated_size, offset_t *size)
paul@65 120
{
paul@72 121
    PageMapper *mapper = _paging->next_region();
paul@65 122
paul@72 123
    if (mapper == NULL)
paul@114 124
        return pipe_error();
paul@65 125
paul@72 126
    _mapper = mapper;
paul@65 127
paul@65 128
    return current_region(populated_size, size);
paul@65 129
}
paul@65 130
paul@65 131
long PipePager::next_region_for_writer(offset_t *populated_size, offset_t *size)
paul@65 132
{
paul@65 133
    /* Set the populated size before moving on. */
paul@65 134
paul@65 135
    if (_mapper != NULL)
paul@65 136
        _mapper->set_data_size(*populated_size);
paul@65 137
paul@72 138
    /* Obtain the page mapper for the region. */
paul@66 139
paul@72 140
    PageMapper *mapper = _paging->add_region();
paul@65 141
paul@72 142
    if (mapper == NULL)
paul@114 143
        return pipe_error();
paul@65 144
paul@72 145
    _mapper = mapper;
paul@65 146
paul@72 147
    return current_region(populated_size, size);
paul@65 148
}
paul@65 149
paul@114 150
long PipePager::pipe_error()
paul@114 151
{
paul@114 152
    if (_paging->closed())
paul@114 153
        return -L4_EIO;
paul@114 154
    else
paul@114 155
        return -L4_EBUSY;
paul@114 156
}
paul@114 157
paul@117 158
paul@117 159
paul@117 160
/* Update the populated size of a pipe region and notify the other endpoint. */
paul@117 161
paul@117 162
long PipePager::flush(offset_t populated_size, offset_t *size)
paul@117 163
{
paul@117 164
    if (_mapper != NULL)
paul@117 165
        _mapper->set_data_size(populated_size);
paul@117 166
paul@117 167
    *size = _size;
paul@117 168
paul@122 169
    // NOTE: Perhaps employ a distinct event type for metadata updates.
paul@122 170
paul@137 171
    _paging->notify_others(_writing ? PipePaging::WRITER : PipePaging::READER,
paul@484 172
                           NOTIFY_CONTENT_AVAILABLE | NOTIFY_SPACE_AVAILABLE,
paul@484 173
                           NOTIFY_VALUES_NULL);
paul@117 174
    return L4_EOK;
paul@117 175
}
paul@117 176
paul@117 177
paul@117 178
paul@488 179
/* Subscribe to notifications.
paul@488 180
   Readers can subscribe to new data (at end), and pipe closed events.
paul@488 181
   Writers can subscribe to new space and pipe closed events. */
paul@117 182
paul@290 183
long PipePager::subscribe(l4_cap_idx_t notifier, notify_flags_t flags)
paul@117 184
{
paul@488 185
    /* A single notifier is recorded so that it may be unsubscribed when the
paul@488 186
       file is closed. */
paul@488 187
paul@488 188
    if ((notifier != _notifier) && l4_is_valid_cap(_notifier))
paul@488 189
        unsubscribe(_notifier);
paul@117 190
paul@485 191
    _notifier = notifier;
paul@485 192
paul@288 193
    return _paging->subscribe(_writing ? PipePaging::WRITER : PipePaging::READER,
paul@485 194
                              notifier, flags);
paul@290 195
}
paul@290 196
paul@290 197
/* Unsubscribe from notifications. */
paul@290 198
paul@488 199
long PipePager::unsubscribe(l4_cap_idx_t notifier)
paul@290 200
{
paul@488 201
    return _paging->unsubscribe(notifier);
paul@117 202
}
paul@117 203
paul@65 204
// vim: tabstop=4 expandtab shiftwidth=4