~martin-decky/helenos/rcu

« back to all changes in this revision

Viewing changes to uspace/srv/net/net/packet_server.c

  • Committer: Martin Decky
  • Date: 2012-04-30 09:56:41 UTC
  • mfrom: (1460.2.24 main-clone)
  • Revision ID: martin@decky.cz-20120430095641-knbfd26jq7hagfzx
mergeĀ mainlineĀ changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (c) 2009 Lukas Mejdrech
3
 
 * Copyright (c) 2011 Radim Vansa
4
 
 * All rights reserved.
5
 
 *
6
 
 * Redistribution and use in source and binary forms, with or without
7
 
 * modification, are permitted provided that the following conditions
8
 
 * are met:
9
 
 *
10
 
 * - Redistributions of source code must retain the above copyright
11
 
 *   notice, this list of conditions and the following disclaimer.
12
 
 * - Redistributions in binary form must reproduce the above copyright
13
 
 *   notice, this list of conditions and the following disclaimer in the
14
 
 *   documentation and/or other materials provided with the distribution.
15
 
 * - The name of the author may not be used to endorse or promote products
16
 
 *   derived from this software without specific prior written permission.
17
 
 *
18
 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19
 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20
 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
 
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22
 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23
 
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27
 
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 
 */
29
 
 
30
 
/** @addtogroup libpacket
31
 
 *  @{
32
 
 */
33
 
 
34
 
/** @file
35
 
 * Packet server implementation.
36
 
 */
37
 
 
38
 
#include <align.h>
39
 
#include <assert.h>
40
 
#include <async.h>
41
 
#include <errno.h>
42
 
#include <str_error.h>
43
 
#include <stdio.h>
44
 
#include <fibril_synch.h>
45
 
#include <unistd.h>
46
 
#include <sys/mman.h>
47
 
#include <ipc/packet.h>
48
 
#include <ipc/net.h>
49
 
#include <net/packet.h>
50
 
#include <net/packet_header.h>
51
 
 
52
 
#include "packet_server.h"
53
 
 
54
 
#define PACKET_SERVER_PROFILE 1
55
 
 
56
 
/** Number of queues cacheing the unused packets */
57
 
#define FREE_QUEUES_COUNT       7
58
 
/** Maximum number of packets in each queue */
59
 
#define FREE_QUEUE_MAX_LENGTH   16
60
 
 
61
 
/** The default address length reserved for new packets. */
62
 
#define DEFAULT_ADDR_LEN        32
63
 
 
64
 
/** The default prefix reserved for new packets. */
65
 
#define DEFAULT_PREFIX          64
66
 
 
67
 
/** The default suffix reserved for new packets. */
68
 
#define DEFAULT_SUFFIX          64
69
 
 
70
 
/** The queue with unused packets */
71
 
typedef struct packet_queue {
72
 
        packet_t *first;        /**< First packet in the queue */
73
 
        size_t packet_size; /**< Maximal size of the packets in this queue */
74
 
        int count;                      /**< Length of the queue */
75
 
} packet_queue_t;
76
 
 
77
 
/** Packet server global data. */
78
 
static struct {
79
 
        /** Safety lock. */
80
 
        fibril_mutex_t lock;
81
 
        /** Free packet queues. */
82
 
        packet_queue_t free_queues[FREE_QUEUES_COUNT];
83
 
        
84
 
        /** Total packets allocated. */
85
 
        packet_id_t next_id;
86
 
} ps_globals = {
87
 
        .lock = FIBRIL_MUTEX_INITIALIZER(ps_globals.lock),
88
 
        .free_queues = {
89
 
                { NULL, PAGE_SIZE, 0},
90
 
                { NULL, PAGE_SIZE * 2, 0},
91
 
                { NULL, PAGE_SIZE * 4, 0},
92
 
                { NULL, PAGE_SIZE * 8, 0},
93
 
                { NULL, PAGE_SIZE * 16, 0},
94
 
                { NULL, PAGE_SIZE * 32, 0},
95
 
                { NULL, PAGE_SIZE * 64, 0},
96
 
        },
97
 
        .next_id = 1
98
 
};
99
 
 
100
 
/** Clears and initializes the packet according to the given dimensions.
101
 
 *
102
 
 * @param[in] packet    The packet to be initialized.
103
 
 * @param[in] addr_len  The source and destination addresses maximal length in
104
 
 *                      bytes.
105
 
 * @param[in] max_prefix The maximal prefix length in bytes.
106
 
 * @param[in] max_content The maximal content length in bytes.
107
 
 * @param[in] max_suffix The maximal suffix length in bytes.
108
 
 */
109
 
static void packet_init(packet_t *packet,
110
 
        size_t addr_len, size_t max_prefix, size_t max_content, size_t max_suffix)
111
 
{
112
 
        /* Clear the packet content */
113
 
        bzero(((void *) packet) + sizeof(packet_t),
114
 
            packet->length - sizeof(packet_t));
115
 
        
116
 
        /* Clear the packet header */
117
 
        packet->order = 0;
118
 
        packet->metric = 0;
119
 
        packet->previous = 0;
120
 
        packet->next = 0;
121
 
        packet->offload_info = 0;
122
 
        packet->offload_mask = 0;
123
 
        packet->addr_len = 0;
124
 
        packet->src_addr = sizeof(packet_t);
125
 
        packet->dest_addr = packet->src_addr + addr_len;
126
 
        packet->max_prefix = max_prefix;
127
 
        packet->max_content = max_content;
128
 
        packet->data_start = packet->dest_addr + addr_len + packet->max_prefix;
129
 
        packet->data_end = packet->data_start;
130
 
}
131
 
 
132
 
/**
133
 
 * Releases the memory allocated for the packet
134
 
 *
135
 
 * @param[in] packet Pointer to the memory where the packet was allocated
136
 
 */
137
 
static void packet_dealloc(packet_t *packet)
138
 
{
139
 
        pm_remove(packet);
140
 
        munmap(packet, packet->length);
141
 
}
142
 
 
143
 
/** Creates a new packet of dimensions at least as given.
144
 
 *
145
 
 * @param[in] length    The total length of the packet, including the header,
146
 
 *                      the addresses and the data of the packet.
147
 
 * @param[in] addr_len  The source and destination addresses maximal length in
148
 
 *                      bytes.
149
 
 * @param[in] max_prefix The maximal prefix length in bytes.
150
 
 * @param[in] max_content The maximal content length in bytes.
151
 
 * @param[in] max_suffix The maximal suffix length in bytes.
152
 
 * @return              The packet of dimensions at least as given.
153
 
 * @return              NULL if there is not enough memory left.
154
 
 */
155
 
static packet_t *packet_alloc(size_t length, size_t addr_len,
156
 
        size_t max_prefix, size_t max_content, size_t max_suffix)
157
 
{
158
 
        packet_t *packet;
159
 
        int rc;
160
 
 
161
 
        /* Global lock is locked */
162
 
        assert(fibril_mutex_is_locked(&ps_globals.lock));
163
 
        /* The length is some multiple of PAGE_SIZE */
164
 
        assert(!(length & (PAGE_SIZE - 1)));
165
 
 
166
 
        packet = (packet_t *) mmap(NULL, length, PROTO_READ | PROTO_WRITE,
167
 
                MAP_SHARED | MAP_ANONYMOUS, 0, 0);
168
 
        if (packet == MAP_FAILED)
169
 
                return NULL;
170
 
        
171
 
        /* Using 32bit packet_id the id could overflow */
172
 
        packet_id_t pid;
173
 
        do {
174
 
                pid = ps_globals.next_id;
175
 
                ps_globals.next_id++;
176
 
        } while (!pid || pm_find(pid));
177
 
        packet->packet_id = pid;
178
 
 
179
 
        packet->length = length;
180
 
        packet_init(packet, addr_len, max_prefix, max_content, max_suffix);
181
 
        packet->magic_value = PACKET_MAGIC_VALUE;
182
 
        rc = pm_add(packet);
183
 
        if (rc != EOK) {
184
 
                packet_dealloc(packet);
185
 
                return NULL;
186
 
        }
187
 
        
188
 
        return packet;
189
 
}
190
 
 
191
 
/** Return the packet of dimensions at least as given.
192
 
 *
193
 
 * Try to reuse free packets first.
194
 
 * Create a new packet aligned to the memory page size if none available.
195
 
 * Lock the global data during its processing.
196
 
 *
197
 
 * @param[in] addr_len  The source and destination addresses maximal length in
198
 
 *                      bytes.
199
 
 * @param[in] max_prefix The maximal prefix length in bytes.
200
 
 * @param[in] max_content The maximal content length in bytes.
201
 
 * @param[in] max_suffix The maximal suffix length in bytes.
202
 
 * @return              The packet of dimensions at least as given.
203
 
 * @return              NULL if there is not enough memory left.
204
 
 */
205
 
static packet_t *packet_get_local(size_t addr_len,
206
 
        size_t max_prefix, size_t max_content, size_t max_suffix)
207
 
{
208
 
        size_t length = ALIGN_UP(sizeof(packet_t) + 2 * addr_len
209
 
                + max_prefix + max_content + max_suffix, PAGE_SIZE);
210
 
        
211
 
        if (length > PACKET_MAX_LENGTH)
212
 
                return NULL;
213
 
 
214
 
        fibril_mutex_lock(&ps_globals.lock);
215
 
        
216
 
        packet_t *packet;
217
 
        unsigned int index;
218
 
        
219
 
        for (index = 0; index < FREE_QUEUES_COUNT; index++) {
220
 
                if ((length > ps_globals.free_queues[index].packet_size) &&
221
 
                        (index < FREE_QUEUES_COUNT - 1))
222
 
                        continue;
223
 
                
224
 
                packet = ps_globals.free_queues[index].first;
225
 
                while (packet_is_valid(packet) && (packet->length < length))
226
 
                        packet = pm_find(packet->next);
227
 
                
228
 
                if (packet_is_valid(packet)) {
229
 
                        ps_globals.free_queues[index].count--;
230
 
                        if (packet == ps_globals.free_queues[index].first) {
231
 
                                ps_globals.free_queues[index].first = pq_detach(packet);
232
 
                        } else {
233
 
                                pq_detach(packet);
234
 
                        }
235
 
                        
236
 
                        packet_init(packet, addr_len, max_prefix, max_content, max_suffix);
237
 
                        fibril_mutex_unlock(&ps_globals.lock);
238
 
                        
239
 
                        return packet;
240
 
                }
241
 
        }
242
 
        
243
 
        packet = packet_alloc(length, addr_len,
244
 
                max_prefix, max_content, max_suffix);
245
 
        
246
 
        fibril_mutex_unlock(&ps_globals.lock);
247
 
        
248
 
        return packet;
249
 
}
250
 
 
251
 
/** Release the packet and returns it to the appropriate free packet queue.
252
 
 *
253
 
 * @param[in] packet    The packet to be released.
254
 
 *
255
 
 */
256
 
static void packet_release(packet_t *packet)
257
 
{
258
 
        int index;
259
 
        int result;
260
 
 
261
 
        assert(fibril_mutex_is_locked(&ps_globals.lock));
262
 
 
263
 
        for (index = 0; (index < FREE_QUEUES_COUNT - 1) &&
264
 
            (packet->length > ps_globals.free_queues[index].packet_size); index++) {
265
 
                ;
266
 
        }
267
 
        
268
 
        ps_globals.free_queues[index].count++;
269
 
        result = pq_add(&ps_globals.free_queues[index].first, packet,
270
 
                packet->length, packet->length);
271
 
        assert(result == EOK);
272
 
}
273
 
 
274
 
/** Releases the packet queue.
275
 
 *
276
 
 * @param[in] packet_id The first packet identifier.
277
 
 * @return              EOK on success.
278
 
 * @return              ENOENT if there is no such packet.
279
 
 */
280
 
static int packet_release_wrapper(packet_id_t packet_id)
281
 
{
282
 
        packet_t *packet;
283
 
 
284
 
        packet = pm_find(packet_id);
285
 
        if (!packet_is_valid(packet))
286
 
                return ENOENT;
287
 
 
288
 
        fibril_mutex_lock(&ps_globals.lock);
289
 
        pq_destroy(packet, packet_release);
290
 
        fibril_mutex_unlock(&ps_globals.lock);
291
 
 
292
 
        return EOK;
293
 
}
294
 
 
295
 
/** Shares the packet memory block.
296
 
 * @param[in] packet    The packet to be shared.
297
 
 * @return              EOK on success.
298
 
 * @return              EINVAL if the packet is not valid.
299
 
 * @return              EINVAL if the calling module does not accept the memory.
300
 
 * @return              ENOMEM if the desired and actual sizes differ.
301
 
 * @return              Other error codes as defined for the
302
 
 *                      async_share_in_finalize() function.
303
 
 */
304
 
static int packet_reply(packet_t *packet)
305
 
{
306
 
        if (!packet_is_valid(packet))
307
 
                return EINVAL;
308
 
        
309
 
        ipc_callid_t callid;
310
 
        size_t size;
311
 
        if (!async_share_in_receive(&callid, &size)) {
312
 
                async_answer_0(callid, EINVAL);
313
 
                return EINVAL;
314
 
        }
315
 
        
316
 
        if (size != packet->length) {
317
 
                async_answer_0(callid, ENOMEM);
318
 
                return ENOMEM;
319
 
        }
320
 
        
321
 
        return async_share_in_finalize(callid, packet,
322
 
            PROTO_READ | PROTO_WRITE);
323
 
}
324
 
 
325
 
/** Processes the packet server message.
326
 
 *
327
 
 * @param[in] callid    The message identifier.
328
 
 * @param[in] call      The message parameters.
329
 
 * @param[out] answer   The message answer parameters.
330
 
 * @param[out] answer_count The last parameter for the actual answer in the
331
 
 *                      answer parameter.
332
 
 * @return              EOK on success.
333
 
 * @return              ENOMEM if there is not enough memory left.
334
 
 * @return              ENOENT if there is no such packet as in the packet
335
 
 *                      message parameter.
336
 
 * @return              ENOTSUP if the message is not known.
337
 
 * @return              Other error codes as defined for the
338
 
 *                      packet_release_wrapper() function.
339
 
 */
340
 
int packet_server_message(ipc_callid_t callid, ipc_call_t *call, ipc_call_t *answer,
341
 
    size_t *answer_count)
342
 
{
343
 
        packet_t *packet;
344
 
        
345
 
        if (!IPC_GET_IMETHOD(*call))
346
 
                return EOK;
347
 
        
348
 
        *answer_count = 0;
349
 
        switch (IPC_GET_IMETHOD(*call)) {
350
 
        case NET_PACKET_CREATE_1:
351
 
                packet = packet_get_local(DEFAULT_ADDR_LEN, DEFAULT_PREFIX,
352
 
                        IPC_GET_CONTENT(*call), DEFAULT_SUFFIX);
353
 
                if (!packet)
354
 
                        return ENOMEM;
355
 
                *answer_count = 2;
356
 
                IPC_SET_ARG1(*answer, (sysarg_t) packet->packet_id);
357
 
                IPC_SET_ARG2(*answer, (sysarg_t) packet->length);
358
 
                return EOK;
359
 
        
360
 
        case NET_PACKET_CREATE_4:
361
 
                packet = packet_get_local(
362
 
                        ((DEFAULT_ADDR_LEN < IPC_GET_ADDR_LEN(*call)) ?
363
 
                    IPC_GET_ADDR_LEN(*call) : DEFAULT_ADDR_LEN),
364
 
                    DEFAULT_PREFIX + IPC_GET_PREFIX(*call),
365
 
                    IPC_GET_CONTENT(*call),
366
 
                    DEFAULT_SUFFIX + IPC_GET_SUFFIX(*call));
367
 
                if (!packet)
368
 
                        return ENOMEM;
369
 
                *answer_count = 2;
370
 
                IPC_SET_ARG1(*answer, (sysarg_t) packet->packet_id);
371
 
                IPC_SET_ARG2(*answer, (sysarg_t) packet->length);
372
 
                return EOK;
373
 
        
374
 
        case NET_PACKET_GET:
375
 
                packet = pm_find(IPC_GET_ID(*call));
376
 
                if (!packet_is_valid(packet))
377
 
                        return ENOENT;
378
 
                
379
 
                return packet_reply(packet);
380
 
        
381
 
        case NET_PACKET_GET_SIZE:
382
 
                packet = pm_find(IPC_GET_ID(*call));
383
 
                if (!packet_is_valid(packet))
384
 
                        return ENOENT;
385
 
                IPC_SET_ARG1(*answer, (sysarg_t) packet->length);
386
 
                *answer_count = 1;
387
 
                return EOK;
388
 
        
389
 
        case NET_PACKET_RELEASE:
390
 
                return packet_release_wrapper(IPC_GET_ID(*call));
391
 
        }
392
 
        
393
 
        return ENOTSUP;
394
 
}
395
 
 
396
 
int packet_server_init()
397
 
{
398
 
        return EOK;
399
 
}
400
 
 
401
 
/** @}
402
 
 */