dls_link.c revision d62bc4badc1c1f1549c961cfb8b420e650e1272b
* The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * See the License for the specific language governing permissions * and limitations under the License. * When distributing Covered Code, include this CDDL HEADER in each * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. #
pragma ident "%Z%%M% %I% %E% SMI" * Data-Link Services Module * Construct a hash key encompassing both DLSAP value and VLAN idenitifier. * Extract the DLSAP value from the hash key. * - Parse the mac header information of the given packet. * - Strip the padding and skip over the header. Note that because some * DLS consumers only check the db_ref count of the first mblk, we * pullup the message into a single mblk. Because the original message * is freed as the result of message pulling up, dls_link_header_info() * is called again to update the mhi_saddr and mhi_daddr pointers in the * mhip. Further, the dls_link_header_info() function ensures that the * size of the pulled message is greater than the MAC header size, * therefore we can directly advance b_rptr to point at the payload. * We choose to use a macro for performance reasons. * Truncate the chain starting at mp such that all packets in the chain * have identical source and destination addresses, saps, and tag types * (see below). It returns a pointer to the mblk following the chain, * NULL if there is no further packet following the processed chain. * The countp argument is set to the number of valid packets in the chain. * Note that the whole MAC header (including the VLAN tag if any) in each * packet will be stripped. * Compare with subsequent headers until we find one that has * differing header information. After checking each packet * strip padding and skip over the header. * The source, destination, sap, vlan id and the MSGNOLOOP * flag must all match in a given subchain. * Note that we don't need to restore the padding. * There are several types of packets. Packets don't match * if they are classified to different type or if they are * VLAN packets but belong to different VLANs: * packet type tagged vid pri * --------------------------------------------------------- * VLAN packets Yes non-zero - * priority tagged Yes zero non-zero * Break the chain at this point and return a pointer to the next * Try to send mp up to the streams of the given sap and vid. Return B_TRUE * if this message is sent to any streams. * Note that this function will copy the message chain and the original * mp will remain valid after this function * Construct a hash key from the VLAN identifier and the * DLSAP that represents dls_impl_t in promiscuous mode. * Search the hash table for dls_impl_t eligible to receive * a packet chain for this DLSAP/VLAN combination. * Find dls_impl_t that will accept the sub-chain. * We have at least one acceptor. * There will normally be at least more dls_impl_t * (since we've yet to check for non-promiscuous * dls_impl_t) so dup the sub-chain. * Release the hold on the dls_impl_t chain now that we have * Wipe the accepted state. * Grab the longest sub-chain we can process as a single * If it is tagged traffic, send it upstream to * all dls_impl_t which are attached to the physical * link and bound to SAP 0x8100. * Don't pass the packets up if they are tagged * - their VID and priority are both zero (invalid * - their sap is ETHERTYPE_VLAN and their VID is * zero as they have already been sent upstreams. * Construct a hash key from the VLAN identifier and the * Search the has table for dls_impl_t eligible to receive * a packet chain for this DLSAP/VLAN combination. * Find the first dls_impl_t that will accept the sub-chain. * If we did not find any dls_impl_t willing to accept the * sub-chain then throw it away. * We have at least one acceptor. * Find the next dls_impl_t that will accept the * If there are no more dls_impl_t that are willing * to accept the sub-chain then we don't need to dup * it before handing it to the current one. * Since there are no more dls_impl_t, we're * There are more dls_impl_t so dup the sub-chain. * Release the hold on the dls_impl_t chain now that we have * If there were no acceptors then add the packet count to the * Try to send mp up to the DLS_SAP_PROMISC listeners. Return B_TRUE if this * message is sent to any streams. * Wipe the accepted state and the receive information of * the first eligible dls_impl_t. * Grab the longest sub-chain we can process as a single * Note that we need to first send to the dls_impl_t * in promiscuous mode in order to avoid the packet reordering * Non promisc case. Two passes: * 1. send tagged packets to ETHERTYPE_VLAN listeners * 2. send packets to listeners bound to the specific SAP. * Construct a hash key from the VLAN identifier and the * Search the has table for dls_impl_t eligible to receive * a packet chain for this DLSAP/VLAN combination. * Find the first dls_impl_t that will accept the sub-chain. * To avoid the extra copymsgchain(), if this * is the first eligible dls_impl_t, remember required * information and send up the message afterwards. * Release the hold on the dls_impl_t chain now that we have * Don't pass the packets up again if: * - First pass is done and the packets are tagged and their: * - VID and priority are both zero (invalid packets). * - their sap is ETHERTYPE_VLAN and their VID is zero * (they have already been sent upstreams). * Send the message up to the first eligible dls_impl_t. * If there were no acceptors then add the packet count to the * Allocate a new dls_link_t structure. * Name the dls_link_t after the MAC interface it represents. * Initialize promiscuous bookkeeping fields. * Free the structure back to the cache. * Module initialization functions. * Create a kmem_cache of dls_link_t structures. * Create a dls_link_t hash table and associated lock. * Destroy the kmem_cache. * Destroy the hash table and associated lock. * Look up a dls_link_t corresponding to the given mac_handle_t * in the global hash table. We need to hold i_dls_link_lock in * order to atomically find and insert a dls_link_t into the * We didn't find anything so we need to create one. * Bump the reference count and hand back the reference. * Check if there are any more references. * There are more references so there's nothing more to do. * Destroy the dls_link_t. * Generate a hash key based on the sap and the VLAN id. * We need dl_lock here because we want to be able to walk * the hash table *and* set the mac rx func atomically. if * these two operations are separate, someone else could * drop the hash lock and this could cause our chosen rx * func to be incorrect. note that we cannot call mac_rx_add * when holding the hash lock because this can cause deadlock. * Search the table for a list head with this key. * Add the dls_impl_t to the head of the list. * Save a pointer to the list head. * Walk the bound dls_impl_t to see if there are any * in promiscuous 'all sap' mode. * If there are then we need to use a receive routine * which will route packets to those dls_impl_t as well * as ones bound to the DLSAP of the packet. /* Replace the existing receive function if there is one. */ * We need dl_lock here because we want to be able to walk * the hash table *and* set the mac rx func atomically. if * these two operations are separate, someone else could * drop the hash lock and this could cause our chosen rx * func to be incorrect. note that we cannot call mac_rx_add * when holding the hash lock because this can cause deadlock. * Poll the hash table entry until all references have been dropped. * We need to drop all locks before sleeping because we don't want * the interrupt handler to block. We set di_removing here to * tell the receive callbacks not to pass up packets anymore. * This is only a hint to quicken the decrease of the refcnt so * the assignment need not be protected by any lock. * Walk the list and remove the dls_impl_t. * The list is empty so remove the hash table entry. * If there are no dls_impl_t then there's no need to register a * receive function with the mac. * Walk the bound dls_impl_t to see if there are any * in promiscuous 'all sap' mode. * If there are then we need to use a receive routine * which will route packets to those dls_impl_t as well * as ones bound to the DLSAP of the packet. * Packets should always be at least 16 bit aligned. * If this is a VLAN-tagged Ethernet packet, then the SAP in the * mac_header_info_t as returned by mac_header_info() is * ETHERTYPE_VLAN. We need to grab the ethertype from the VLAN header. * Pullup the message in order to get the MAC header * infomation. Note that this is a read-only function, * we keep the input packet intact. * The messsage is looped back from the underlying driver.