~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
cvs/emstar/link/liblink/link_passthru.c


  1 /*
  2  *
  3  * Copyright (c) 2003 The Regents of the University of California.  All 
  4  * 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  *
 13  * - Neither the name of the University nor the names of its
 14  *   contributors may be used to endorse or promote products derived
 15  *   from this software without specific prior written permission.
 16  *
 17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
 18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 19  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 20  * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
 21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 25  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28  *
 29  */
 30 
 31 char link_passthru_c_cvsid[] = "$Id: link_passthru.c,v 1.14 2006-02-28 21:08:13 thanos Exp $";
 32 
 33 #include "liblink_i.h"
 34 
 35 /*
 36  * To get the queueing/back-pressure semantics right, we need to keep
 37  * our own packet queue here in passthru.  When a writer from above hands
 38  * us a packet, we queue it to allow for the case where more than one
 39  * output packet is generated from a single input packet.  the output
 40  * packets are then fed to the underlying link device as quickly as the
 41  * device will take them.  (We set the input queue length of our 
 42  * underlying device to be quite small.)  We unblock to receive the next
 43  * message after the queue drains.
 44  */
 45 
 46 QUEUE_INST(link_pass,_,packets,packet_t,link_pass_ctx_t);
 47 
 48 int link_pass_is_blocked(link_pass_ctx_t *ctx)
 49 {
 50   // ctx->lower can be null and this leads to segfaults...
 51   if (ctx->lower==NULL) {
 52       return 0;
 53   }
 54 
 55   return (lu_writable_cb_get_enable(ctx->lower) ||
 56           ctx->blocking_requested || ctx->withhold_unblock ||
 57           link_pass_top(ctx)) ? 1 : 0;
 58 }
 59 
 60 /*
 61  * unblock the upper if 
 62  *   - the server did not request blocking
 63  *   - the writable handler is not pending
 64  *   - we are not temporarily withholding blocking
 65  *   - the internal queue is empty
 66  */
 67 
 68 static
 69 void link_pass_maybe_unblock(link_pass_ctx_t *ctx)
 70 {
 71   if (!link_pass_is_blocked(ctx))
 72     lp_unblock(ctx->upper, 0);
 73 }
 74 
 75 
 76 /*
 77  * enables the client to request blocking..
 78  */
 79 
 80 void link_pass_block(link_pass_ctx_t *ctx)
 81 {
 82   ctx->blocking_requested = 1;
 83 }
 84 
 85 
 86 void link_pass_unblock(link_pass_ctx_t *ctx)
 87 {
 88   ctx->blocking_requested = 0;
 89   link_pass_maybe_unblock(ctx);
 90 }
 91 
 92 
 93 int link_pass_blocking_requested(link_pass_ctx_t *ctx)
 94 {
 95   return ctx->blocking_requested ? 1 : 0;
 96 }
 97 
 98 
 99 /*
100  * This callback is called whenever a packet arrives up from the
101  * underlying datalink interfaces
102  */
103 static 
104 int link_pass_receiver(lu_context_t *lu, link_pkt_t *link_pkt, ssize_t data_len)
105                        
106 {
107   link_pass_ctx_t *lp = (link_pass_ctx_t *) lu_data(lu);
108 
109   /* call our handler */
110   if (lp->opts.pkt_from_lower)
111     lp->opts.pkt_from_lower(lp, link_pkt, data_len);
112   
113   free(link_pkt);
114   return EVENT_RENEW;
115 }
116 
117 
118 /* receipt forwarder .. */
119 static 
120 int link_pass_receipt_handler(lu_context_t *lu, link_pkt_t *link_pkt, ssize_t data_len)
121                        
122 {
123   link_pass_ctx_t *lp = (link_pass_ctx_t *) lu_data(lu);
124 
125   /* if it's a receipt, just pass it up */
126   if (link_pkt->ext_type == MAC_CTRL_RECEIPT) {
127     lp_receive(lp->upper, link_pkt, data_len);
128   }
129 
130   free(link_pkt);
131   return EVENT_RENEW;
132 }
133 
134 
135 static
136 int link_pass_do_send(link_pass_ctx_t *ctx)
137 {
138   int retval;
139   buf_t *next;
140   packet_t *top;
141 
142  repeat:
143   /* stop now if our writable callback is pending.. */
144   if (lu_writable_cb_get_enable(ctx->lower))
145     return LP_BLOCKED;
146 
147   /* grab the top */
148   top = link_pass_top(ctx);
149 
150   /* if there's nothing to write, maybe unblock and return 0 */
151   if (top == NULL) {
152     link_pass_maybe_unblock(ctx);    
153     return 0;
154   }
155   
156   /* ok, try to send */
157   next = top->buf;
158   retval = lu_send(ctx->lower, (link_pkt_t *)next->buf, 
159                    next->len - sizeof(link_pkt_t));
160 
161   if (retval < 0) {
162     if (errno == EAGAIN) {
163       lu_writable_cb_set_enable(ctx->lower, 1);
164       return LP_BLOCKED;
165     }
166   }
167   
168   /* succeeded.. pop and continue */
169   link_pass_pop(ctx);
170   buf_free(next);
171   free(top);
172   goto repeat;
173 }
174 
175 
176 int link_pass_to_lower(link_pass_ctx_t *ctx, buf_t *buf)
177 {
178   /* queue it! */
179   packet_t *next = g_new0(packet_t, 1);
180   next->buf = buf;
181   link_pass_push(ctx, next);
182   return link_pass_do_send(ctx);
183 }
184 
185 
186 int link_pass_to_upper(link_pass_ctx_t *ctx, buf_t *buf)
187 {
188   int retval = -1;
189 
190   if (ctx) {
191     lp_receive(ctx->upper, (link_pkt_t*)buf->buf, buf->len-sizeof(link_pkt_t));
192     retval = 0;
193   }
194     
195   buf_free(buf);
196   return retval;
197 }
198 
199 
200 static
201 int link_pass_writable(lu_context_t *lu)
202 {
203   link_pass_ctx_t *ctx = (link_pass_ctx_t *) lu_data(lu);
204   
205   /* we're writable now.. */
206   lu_writable_cb_set_enable(lu, 0);
207 
208   /* try to send some more -- will reactivate handler if needed */
209   link_pass_do_send(ctx);
210   
211   return EVENT_RENEW;
212 }
213 
214 
215 /*
216  *  configure lower link when link is opened.. 
217  */
218 static 
219 int link_pass_configure(lu_context_t *lu)
220 {
221   link_pass_ctx_t *ctx = (link_pass_ctx_t *)lu_data(lu);
222   lu_set_promisc(lu, lp_get_promisc_state(ctx->upper));
223 
224   if (ctx->opts.configure_lower) 
225     ctx->opts.configure_lower(ctx);
226   
227   return EVENT_RENEW;
228 }
229 
230 
231 /*
232  * Callback when the queue of packets written to our local device file
233  * is serviced
234  */
235 static 
236 int link_pass_send(lp_context_t *lp, link_pkt_t *link_pkt,
237                    int data_len, int loop_needed)
238 {
239   link_pass_ctx_t *ctx = (link_pass_ctx_t *)lp_data(lp);
240 
241   /* call our handler */
242   if (ctx->opts.pkt_from_upper) {
243 
244     /* 
245      * sending normally will trigger unblocking if the send succeeds.
246      * however, it may be the case that the client is planning to 
247      * block at the "application level".  so we delay the "maybe_unblock"
248      * until after the client's call returns 
249      */
250 
251     ctx->withhold_unblock = 1;
252     if (ctx->opts.pkt_from_upper(ctx, link_pkt, data_len) == LP_BLOCKED)
253       link_pass_block(ctx);
254     ctx->withhold_unblock = 0;
255 
256     link_pass_maybe_unblock(ctx);
257   }
258 
259   /* loop if needed */
260   if (loop_needed) {
261     struct timeval now;
262     gettimeofday(&now, NULL);
263     link_pkt->rcv_time = now;
264     link_pkt->src.id = lp->status.if_id;
265     lp_loop_receive(lp, link_pkt, data_len);
266   }
267 
268   /* block if there is data waiting.. */
269   return link_pass_is_blocked(ctx) ? LP_BLOCKED : 0;
270 }
271 
272 static 
273 int link_pass_enqueue(lp_context_t *lp, link_pkt_t *link_pkt,
274                       int data_len)
275 {
276   link_pass_ctx_t *ctx = (link_pass_ctx_t *)lp_data(lp);
277   int retval = 0;
278 
279   /* call our handler */
280   if (ctx->opts.enqueue_pkt_from_upper)
281     retval = ctx->opts.enqueue_pkt_from_upper(ctx, link_pkt, data_len);
282 
283   return retval;
284 }
285 
286 
287 /* pass down promiscuous mode */
288 static
289 void link_pass_promisc(lp_context_t *lp, int mode)
290 {
291   link_pass_ctx_t *ctx = (link_pass_ctx_t *)lp_data(lp);
292   lu_set_promisc(ctx->lower, mode);
293 }
294 
295 
296 /*
297  * proxy ioctl downwards
298  */
299 static int link_pass_ioctl(lp_context_t *lp, int cmd, void *arg)
300 {
301   link_pass_ctx_t *ctx = (link_pass_ctx_t *) lp_data(lp);  
302   int retval = lu_ioctl(ctx->lower, cmd, arg);
303   if (retval >= 0) return retval;
304   else return -errno;
305 }
306 
307 
308 /*
309  *  status device handling / providing
310  */
311 
312 static
313 int link_pass_status_notify(lu_context_t *lu)
314 {
315   link_pass_ctx_t *ctx = (link_pass_ctx_t *) lu_data(lu);
316 
317   if (ctx->opts.status_notify)
318     ctx->opts.status_notify(ctx);
319 
320   else {
321     /* if no callback specified, do mtu-adjust */
322     link_status_t new_stat;
323     if (lu_status(lu, &new_stat) < 0) {
324       elog(LOG_WARNING, "Can't get status?");
325       goto out;
326     }
327 
328     /* handle common cases */
329     if (ctx->opts.mtu_adjust)
330       new_stat.MTU += ctx->opts.mtu_adjust;
331     else if (ctx->opts.new_fixed_mtu) 
332       new_stat.MTU = ctx->opts.new_fixed_mtu;
333     
334     link_pass_push_status_to_upper(ctx, &new_stat);
335   }
336 
337  out:  
338   return EVENT_RENEW;
339 }
340 
341 
342 void link_pass_push_status_to_upper(link_pass_ctx_t *ctx, link_status_t *stat)
343 {
344   if (ctx && stat) lp_push_status(ctx->upper, stat);
345 }
346 
347 
348 int link_pass_get_lower_status(link_pass_ctx_t *ctx, link_status_t *stat)
349 {
350   return (ctx && ctx->lower) ? lu_status(ctx->lower, stat) : -1;
351 }
352 
353 
354 void link_pass_abort_queue(link_pass_ctx_t *ctx)
355 {
356   packet_t *p;
357 
358   /* pop & clear */
359   while ((p = link_pass_pop(ctx))) {
360     buf_free(p->buf);
361     free(p);
362   }
363 
364   /* maybe unblock */
365   link_pass_maybe_unblock(ctx);
366 }
367 
368 
369 static
370 int link_pass_command(lp_context_t *lp, parser_state_t *cmd_input)
371 {
372   link_pass_ctx_t *ctx = (link_pass_ctx_t *)lp_data(lp);
373   buf_t *buf = buf_new();
374   int retval = EVENT_RENEW;
375 
376   if (ctx->opts.process_command) 
377     ctx->opts.process_command(ctx, cmd_input, buf);
378   else
379     bufcpy(buf, cmd_input->input, strlen(cmd_input->input));
380 
381   if (buf->len > 0) {
382     if (write_to_file(link_name(&(ctx->opts.uses), LINK_COMMAND_SUBDEV), buf->buf) < 0)
383       retval = EVENT_ERROR(errno);
384   }
385 
386   buf_free(buf);  
387   return retval;
388 }
389 
390 
391 static
392 void link_pass_usage(lp_context_t *lp, buf_t *fill_usage)
393 {
394   link_pass_ctx_t *ctx = (link_pass_ctx_t *)lp_data(lp);
395   if (ctx->opts.usage)
396     ctx->opts.usage(ctx, fill_usage);
397   file_to_buf(fill_usage, link_name(&(ctx->opts.uses), LINK_COMMAND_SUBDEV));
398 }
399 
400 
401 int link_pass_new(link_pass_opts_t *opts, link_pass_ctx_t **ref)
402 {
403   int retval = 0;
404   
405   /* link device state */
406   link_pass_ctx_t *lp = g_new0(link_pass_ctx_t, 1);
407   
408   /* make sure we allocated this sucker */
409   if (lp == NULL) {
410     retval = -ENOMEM;
411     goto done;
412   }
413   
414   /* make sure the options are valid */
415   if (opts == NULL) {
416     elog(LOG_ERR, "invalid link options (null)");
417     retval = -EINVAL;
418     goto done;
419   }
420   
421   /* store ref */
422   lp->ref = ref;
423   
424   /* copy the options struct */
425   lp->opts = *opts;
426 
427   /* don't need to copy the names.. underlying events expose copies */
428 
429   /* register above */
430   {
431     /* set options for the link provider interface */
432     lp_opts_t lp_opts = {
433       description: opts->description,
434       send: link_pass_send,
435       enqueue: link_pass_enqueue,
436       ioctl: link_pass_ioctl,
437       promisc_mode: link_pass_promisc,
438       opts: opts->provides,
439       command_request: link_pass_command,
440       usage: link_pass_usage
441     };
442 
443     /* store data pointer */
444     lp_opts.opts.data = lp;
445     
446     /* try to register the packetdev as a link interface */
447     if (lp_register(&lp_opts, &(lp->upper)) < 0) {
448       elog(LOG_CRIT, "can't create link device %s: %m",
449            link_name(&opts->provides, NULL));
450       retval = -1;
451       goto done;
452     }
453   }
454   
455   /* open below */
456   {
457     lu_opts_t lu_opts = {
458       opts: opts->uses,
459       receive: link_pass_receiver,
460       writable: link_pass_writable,
461       configure: link_pass_configure,
462       status_notify: link_pass_status_notify,
463     };
464 
465     /* swap data pointers */
466     lp->data = opts->uses.data;
467     lu_opts.opts.data = lp;
468 
469     /* try to open the lower link interface */
470     if (lu_open(&lu_opts, &(lp->lower)) < 0) {
471       elog(LOG_CRIT, "can't open link device %s: %m",
472            link_name(&opts->uses, NULL));
473       retval = -1;
474       goto done;
475     }
476 
477     if (lu_opts.opts.pkt_type && !opts->dont_auto_forward_receipts) {
478 
479       /* register additional event to receive receipts only */
480       lu_opts.opts.pkt_type = PKT_TYPE_MAC_CTRL;
481       lu_opts.receive = link_pass_receipt_handler;
482       lu_opts.writable = NULL;
483       lu_opts.configure = NULL;
484       lu_opts.status_notify = NULL;
485 
486       if (lu_open(&lu_opts, &(lp->lower_receipts)) < 0) {
487         elog(LOG_CRIT, "can't open link device %s: %m",
488              link_name(&opts->uses, NULL));
489         retval = -1;
490         goto done;
491       }
492 
493     }
494   }
495 
496 done:
497   
498   /* if we've had an error, close and dealloc */
499   if (retval < 0) {
500     if (lp) {
501       link_pass_destroy(lp);
502       lp = NULL;
503     }
504   }
505   
506   /* write the link provider context (or, a NULL link provider context
507      pointer) to the user's provided reference, if any */
508   if (ref) {
509     if (*ref) {
510       elog(LOG_WARNING, "Destroying link dev before reusing reference");
511       link_pass_destroy(*ref);
512     }
513     *ref = lp;
514   }
515  
516   /* return either success or failure with errno properly set */
517   if (retval < 0) {
518     errno = -retval;
519     return -1;
520   } else {
521     errno = 0;
522     return 0;
523   }
524 }
525 
526 
527 void link_pass_destroy(link_pass_ctx_t *lpt)
528 {
529   if (lpt) {
530     if (lpt->ref)
531       *(lpt->ref) = NULL;
532     
533     lp_destroy(lpt->upper);
534     lu_destroy(lpt->lower);
535     link_pass_abort_queue(lpt);
536     g_free(lpt);
537   }
538 }
539 
540 
541 void *link_pass_data(link_pass_ctx_t *ctx)
542 {
543   return ctx ? ctx->data : NULL;
544 }
545 
546 
547 char *link_pass_name(link_pass_ctx_t *ctx, char *suffix)
548 {
549   return ctx ? lp_name(ctx->upper, suffix) : NULL;
550 }
551 
552 
553 lu_context_t *link_pass_get_lower(link_pass_ctx_t *ctx)
554 {
555     return ctx ? ctx->lower : NULL;
556 }
557 
558 
559 lp_context_t *link_pass_get_upper(link_pass_ctx_t *ctx)
560 {
561     return ctx ? ctx->upper : NULL;
562 }
563 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.