source: mainline/uspace/lib/packet/generic/packet_server.c@ d3ce33fa

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d3ce33fa was 79ae36dd, checked in by Martin Decky <martin@…>, 14 years ago

new async framework with integrated exchange tracking

  • strict isolation between low-level IPC and high-level async framework with integrated exchange tracking
    • each IPC connection is represented by an async_sess_t structure
    • each IPC exchange is represented by an async_exch_t structure
    • exchange management is either based on atomic messages (EXCHANGE_ATOMIC), locking (EXCHANGE_SERIALIZE) or connection cloning (EXCHANGE_CLONE)
  • async_obsolete: temporary compatibility layer to keep old async clients working (several pieces of code are currently broken, but only non-essential functionality)
  • IPC_M_PHONE_HANGUP is now method no. 0 (for elegant boolean evaluation)
  • IPC_M_DEBUG_ALL has been renamed to IPC_M_DEBUG
  • IPC_M_PING has been removed (VFS protocol now has VFS_IN_PING)
  • console routines in libc have been rewritten for better abstraction
  • additional use for libc-private header files (FILE structure opaque to the client)
  • various cstyle changes (typos, indentation, missing externs in header files, improved comments, etc.)
  • Property mode set to 100644
File size: 10.4 KB
Line 
1/*
2 * Copyright (c) 2009 Lukas Mejdrech
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libpacket
30 * @{
31 */
32
33/** @file
34 * Packet server implementation.
35 */
36
37#include <packet_server.h>
38
39#include <align.h>
40#include <assert.h>
41#include <async.h>
42#include <errno.h>
43#include <fibril_synch.h>
44#include <unistd.h>
45#include <sys/mman.h>
46#include <ipc/packet.h>
47#include <ipc/net.h>
48#include <net/packet.h>
49#include <net/packet_header.h>
50
51#define FREE_QUEUES_COUNT 7
52
53/** The default address length reserved for new packets. */
54#define DEFAULT_ADDR_LEN 32
55
56/** The default prefix reserved for new packets. */
57#define DEFAULT_PREFIX 64
58
59/** The default suffix reserved for new packets. */
60#define DEFAULT_SUFFIX 64
61
62/** Packet server global data. */
63static struct {
64 /** Safety lock. */
65 fibril_mutex_t lock;
66 /** Free packet queues. */
67 packet_t *free[FREE_QUEUES_COUNT];
68
69 /**
70 * Packet length upper bounds of the free packet queues. The maximal
71 * lengths of packets in each queue in the ascending order. The last
72 * queue is not limited.
73 */
74 size_t sizes[FREE_QUEUES_COUNT];
75
76 /** Total packets allocated. */
77 unsigned int count;
78} ps_globals = {
79 .lock = FIBRIL_MUTEX_INITIALIZER(ps_globals.lock),
80 .free = {
81 NULL,
82 NULL,
83 NULL,
84 NULL,
85 NULL,
86 NULL,
87 NULL
88 },
89 .sizes = {
90 PAGE_SIZE,
91 PAGE_SIZE * 2,
92 PAGE_SIZE * 4,
93 PAGE_SIZE * 8,
94 PAGE_SIZE * 16,
95 PAGE_SIZE * 32,
96 PAGE_SIZE * 64
97 },
98 .count = 0
99};
100
101/** Clears and initializes the packet according to the given dimensions.
102 *
103 * @param[in] packet The packet to be initialized.
104 * @param[in] addr_len The source and destination addresses maximal length in
105 * bytes.
106 * @param[in] max_prefix The maximal prefix length in bytes.
107 * @param[in] max_content The maximal content length in bytes.
108 * @param[in] max_suffix The maximal suffix length in bytes.
109 */
110static void
111packet_init(packet_t *packet, size_t addr_len, size_t max_prefix,
112 size_t max_content, size_t max_suffix)
113{
114 /* Clear the packet content */
115 bzero(((void *) packet) + sizeof(packet_t),
116 packet->length - sizeof(packet_t));
117
118 /* Clear the packet header */
119 packet->order = 0;
120 packet->metric = 0;
121 packet->previous = 0;
122 packet->next = 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/** Creates a new packet of dimensions at least as given.
133 *
134 * @param[in] length The total length of the packet, including the header,
135 * the addresses and the data of the packet.
136 * @param[in] addr_len The source and destination addresses maximal length in
137 * bytes.
138 * @param[in] max_prefix The maximal prefix length in bytes.
139 * @param[in] max_content The maximal content length in bytes.
140 * @param[in] max_suffix The maximal suffix length in bytes.
141 * @return The packet of dimensions at least as given.
142 * @return NULL if there is not enough memory left.
143 */
144static packet_t *
145packet_create(size_t length, size_t addr_len, size_t max_prefix,
146 size_t max_content, size_t max_suffix)
147{
148 packet_t *packet;
149 int rc;
150
151 assert(fibril_mutex_is_locked(&ps_globals.lock));
152
153 /* Already locked */
154 packet = (packet_t *) mmap(NULL, length, PROTO_READ | PROTO_WRITE,
155 MAP_SHARED | MAP_ANONYMOUS, 0, 0);
156 if (packet == MAP_FAILED)
157 return NULL;
158
159 ps_globals.count++;
160 packet->packet_id = ps_globals.count;
161 packet->length = length;
162 packet_init(packet, addr_len, max_prefix, max_content, max_suffix);
163 packet->magic_value = PACKET_MAGIC_VALUE;
164 rc = pm_add(packet);
165 if (rc != EOK) {
166 munmap(packet, packet->length);
167 return NULL;
168 }
169
170 return packet;
171}
172
173/** Return the packet of dimensions at least as given.
174 *
175 * Try to reuse free packets first.
176 * Create a new packet aligned to the memory page size if none available.
177 * Lock the global data during its processing.
178 *
179 * @param[in] addr_len The source and destination addresses maximal length in
180 * bytes.
181 * @param[in] max_prefix The maximal prefix length in bytes.
182 * @param[in] max_content The maximal content length in bytes.
183 * @param[in] max_suffix The maximal suffix length in bytes.
184 * @return The packet of dimensions at least as given.
185 * @return NULL if there is not enough memory left.
186 */
187static packet_t *
188packet_get_local(size_t addr_len, size_t max_prefix, size_t max_content,
189 size_t max_suffix)
190{
191 size_t length = ALIGN_UP(sizeof(packet_t) + 2 * addr_len +
192 max_prefix + max_content + max_suffix, PAGE_SIZE);
193
194 fibril_mutex_lock(&ps_globals.lock);
195
196 packet_t *packet;
197 unsigned int index;
198
199 for (index = 0; index < FREE_QUEUES_COUNT; index++) {
200 if ((length > ps_globals.sizes[index]) &&
201 (index < FREE_QUEUES_COUNT - 1))
202 continue;
203
204 packet = ps_globals.free[index];
205 while (packet_is_valid(packet) && (packet->length < length))
206 packet = pm_find(packet->next);
207
208 if (packet_is_valid(packet)) {
209 if (packet == ps_globals.free[index])
210 ps_globals.free[index] = pq_detach(packet);
211 else
212 pq_detach(packet);
213
214 packet_init(packet, addr_len, max_prefix, max_content,
215 max_suffix);
216 fibril_mutex_unlock(&ps_globals.lock);
217
218 return packet;
219 }
220 }
221
222 packet = packet_create(length, addr_len, max_prefix, max_content,
223 max_suffix);
224
225 fibril_mutex_unlock(&ps_globals.lock);
226
227 return packet;
228}
229
230/** Release the packet and returns it to the appropriate free packet queue.
231 *
232 * @param[in] packet The packet to be released.
233 *
234 */
235static void packet_release(packet_t *packet)
236{
237 int index;
238 int result;
239
240 assert(fibril_mutex_is_locked(&ps_globals.lock));
241
242 for (index = 0; (index < FREE_QUEUES_COUNT - 1) &&
243 (packet->length > ps_globals.sizes[index]); index++) {
244 ;
245 }
246
247 result = pq_add(&ps_globals.free[index], packet, packet->length,
248 packet->length);
249 assert(result == EOK);
250}
251
252/** Releases the packet queue.
253 *
254 * @param[in] packet_id The first packet identifier.
255 * @return EOK on success.
256 * @return ENOENT if there is no such packet.
257 */
258static int packet_release_wrapper(packet_id_t packet_id)
259{
260 packet_t *packet;
261
262 packet = pm_find(packet_id);
263 if (!packet_is_valid(packet))
264 return ENOENT;
265
266 fibril_mutex_lock(&ps_globals.lock);
267 pq_destroy(packet, packet_release);
268 fibril_mutex_unlock(&ps_globals.lock);
269
270 return EOK;
271}
272
273/** Shares the packet memory block.
274 * @param[in] packet The packet to be shared.
275 * @return EOK on success.
276 * @return EINVAL if the packet is not valid.
277 * @return EINVAL if the calling module does not accept the memory.
278 * @return ENOMEM if the desired and actual sizes differ.
279 * @return Other error codes as defined for the
280 * async_share_in_finalize() function.
281 */
282static int packet_reply(packet_t *packet)
283{
284 ipc_callid_t callid;
285 size_t size;
286
287 if (!packet_is_valid(packet))
288 return EINVAL;
289
290 if (!async_share_in_receive(&callid, &size)) {
291 async_answer_0(callid, EINVAL);
292 return EINVAL;
293 }
294
295 if (size != packet->length) {
296 async_answer_0(callid, ENOMEM);
297 return ENOMEM;
298 }
299
300 return async_share_in_finalize(callid, packet,
301 PROTO_READ | PROTO_WRITE);
302}
303
304/** Processes the packet server message.
305 *
306 * @param[in] callid The message identifier.
307 * @param[in] call The message parameters.
308 * @param[out] answer The message answer parameters.
309 * @param[out] answer_count The last parameter for the actual answer in the
310 * answer parameter.
311 * @return EOK on success.
312 * @return ENOMEM if there is not enough memory left.
313 * @return ENOENT if there is no such packet as in the packet
314 * message parameter.
315 * @return ENOTSUP if the message is not known.
316 * @return Other error codes as defined for the
317 * packet_release_wrapper() function.
318 */
319int
320packet_server_message(ipc_callid_t callid, ipc_call_t *call, ipc_call_t *answer,
321 size_t *answer_count)
322{
323 packet_t *packet;
324
325 *answer_count = 0;
326
327 if (!IPC_GET_IMETHOD(*call))
328 return EOK;
329
330 switch (IPC_GET_IMETHOD(*call)) {
331 case NET_PACKET_CREATE_1:
332 packet = packet_get_local(DEFAULT_ADDR_LEN, DEFAULT_PREFIX,
333 IPC_GET_CONTENT(*call), DEFAULT_SUFFIX);
334 if (!packet)
335 return ENOMEM;
336 *answer_count = 2;
337 IPC_SET_ARG1(*answer, (sysarg_t) packet->packet_id);
338 IPC_SET_ARG2(*answer, (sysarg_t) packet->length);
339 return EOK;
340
341 case NET_PACKET_CREATE_4:
342 packet = packet_get_local(
343 ((DEFAULT_ADDR_LEN < IPC_GET_ADDR_LEN(*call)) ?
344 IPC_GET_ADDR_LEN(*call) : DEFAULT_ADDR_LEN),
345 DEFAULT_PREFIX + IPC_GET_PREFIX(*call),
346 IPC_GET_CONTENT(*call),
347 DEFAULT_SUFFIX + IPC_GET_SUFFIX(*call));
348 if (!packet)
349 return ENOMEM;
350 *answer_count = 2;
351 IPC_SET_ARG1(*answer, (sysarg_t) packet->packet_id);
352 IPC_SET_ARG2(*answer, (sysarg_t) packet->length);
353 return EOK;
354
355 case NET_PACKET_GET:
356 packet = pm_find(IPC_GET_ID(*call));
357 if (!packet_is_valid(packet))
358 return ENOENT;
359 return packet_reply(packet);
360
361 case NET_PACKET_GET_SIZE:
362 packet = pm_find(IPC_GET_ID(*call));
363 if (!packet_is_valid(packet))
364 return ENOENT;
365 IPC_SET_ARG1(*answer, (sysarg_t) packet->length);
366 *answer_count = 1;
367 return EOK;
368
369 case NET_PACKET_RELEASE:
370 return packet_release_wrapper(IPC_GET_ID(*call));
371 }
372
373 return ENOTSUP;
374}
375
376/** @}
377 */
Note: See TracBrowser for help on using the repository browser.