| /* |
| * CDDL HEADER START |
| * |
| * 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. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * 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] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2007 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| #include <sys/ib/clients/rds/rds.h> |
| #include <sys/ib/clients/rds/rds_kstat.h> |
| |
| #include <inet/ipclassifier.h> |
| |
| struct rds_kstat_s rds_kstat = { |
| {"rds_nports", KSTAT_DATA_ULONG}, |
| {"rds_nsessions", KSTAT_DATA_ULONG}, |
| {"rds_tx_bytes", KSTAT_DATA_ULONG}, |
| {"rds_tx_pkts", KSTAT_DATA_ULONG}, |
| {"rds_tx_errors", KSTAT_DATA_ULONG}, |
| {"rds_rx_bytes", KSTAT_DATA_ULONG}, |
| {"rds_rx_pkts", KSTAT_DATA_ULONG}, |
| {"rds_rx_pkts_pending", KSTAT_DATA_ULONG}, |
| {"rds_rx_errors", KSTAT_DATA_ULONG}, |
| {"rds_tx_acks", KSTAT_DATA_ULONG}, |
| {"rds_post_recv_buf_called", KSTAT_DATA_ULONG}, |
| {"rds_stalls_triggered", KSTAT_DATA_ULONG}, |
| {"rds_stalls_sent", KSTAT_DATA_ULONG}, |
| {"rds_unstalls_triggered", KSTAT_DATA_ULONG}, |
| {"rds_unstalls_sent", KSTAT_DATA_ULONG}, |
| {"rds_stalls_recvd", KSTAT_DATA_ULONG}, |
| {"rds_unstalls_recvd", KSTAT_DATA_ULONG}, |
| {"rds_stalls_ignored", KSTAT_DATA_ULONG}, |
| {"rds_enobufs", KSTAT_DATA_ULONG}, |
| {"rds_ewouldblocks", KSTAT_DATA_ULONG}, |
| {"rds_failovers", KSTAT_DATA_ULONG}, |
| {"rds_port_quota", KSTAT_DATA_ULONG}, |
| {"rds_port_quota_adjusted", KSTAT_DATA_ULONG}, |
| }; |
| |
| kstat_t *rds_kstatsp; |
| static kmutex_t rds_kstat_mutex; |
| |
| |
| struct kmem_cache *rds_alloc_cache; |
| |
| uint_t rds_bind_fanout_size = RDS_BIND_FANOUT_SIZE; |
| rds_bf_t *rds_bind_fanout; |
| |
| void |
| rds_increment_kstat(kstat_named_t *ksnp, boolean_t lock, uint_t num) |
| { |
| if (lock) |
| mutex_enter(&rds_kstat_mutex); |
| ksnp->value.ul += num; |
| if (lock) |
| mutex_exit(&rds_kstat_mutex); |
| } |
| |
| void |
| rds_decrement_kstat(kstat_named_t *ksnp, boolean_t lock, uint_t num) |
| { |
| if (lock) |
| mutex_enter(&rds_kstat_mutex); |
| ksnp->value.ul -= num; |
| if (lock) |
| mutex_exit(&rds_kstat_mutex); |
| } |
| |
| void |
| rds_set_kstat(kstat_named_t *ksnp, boolean_t lock, ulong_t num) |
| { |
| if (lock) |
| mutex_enter(&rds_kstat_mutex); |
| ksnp->value.ul = num; |
| if (lock) |
| mutex_exit(&rds_kstat_mutex); |
| } |
| |
| ulong_t |
| rds_get_kstat(kstat_named_t *ksnp, boolean_t lock) |
| { |
| ulong_t value; |
| |
| if (lock) |
| mutex_enter(&rds_kstat_mutex); |
| value = ksnp->value.ul; |
| if (lock) |
| mutex_exit(&rds_kstat_mutex); |
| |
| return (value); |
| } |
| |
| |
| void |
| rds_fini() |
| { |
| int i; |
| |
| for (i = 0; i < rds_bind_fanout_size; i++) { |
| mutex_destroy(&rds_bind_fanout[i].rds_bf_lock); |
| } |
| kmem_free(rds_bind_fanout, rds_bind_fanout_size * sizeof (rds_bf_t)); |
| |
| kmem_cache_destroy(rds_alloc_cache); |
| kstat_delete(rds_kstatsp); |
| } |
| |
| |
| void |
| rds_init() |
| { |
| rds_alloc_cache = kmem_cache_create("rds_alloc_cache", |
| sizeof (rds_t), 0, NULL, NULL, NULL, NULL, NULL, 0); |
| rds_hash_init(); |
| /* |
| * kstats |
| */ |
| rds_kstatsp = kstat_create("rds", 0, |
| "rds_kstat", "misc", KSTAT_TYPE_NAMED, |
| sizeof (rds_kstat) / sizeof (kstat_named_t), |
| KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE); |
| if (rds_kstatsp != NULL) { |
| rds_kstatsp->ks_lock = &rds_kstat_mutex; |
| rds_kstatsp->ks_data = (void *)&rds_kstat; |
| kstat_install(rds_kstatsp); |
| } |
| } |
| |
| #define UINT_32_BITS 31 |
| void |
| rds_hash_init() |
| { |
| int i; |
| |
| if (rds_bind_fanout_size & (rds_bind_fanout_size - 1)) { |
| /* Not a power of two. Round up to nearest power of two */ |
| for (i = 0; i < UINT_32_BITS; i++) { |
| if (rds_bind_fanout_size < (1 << i)) |
| break; |
| } |
| rds_bind_fanout_size = 1 << i; |
| } |
| rds_bind_fanout = kmem_zalloc(rds_bind_fanout_size * |
| sizeof (rds_bf_t), KM_SLEEP); |
| for (i = 0; i < rds_bind_fanout_size; i++) { |
| mutex_init(&rds_bind_fanout[i].rds_bf_lock, NULL, MUTEX_DEFAULT, |
| NULL); |
| } |
| } |
| |
| void |
| rds_free(rds_t *rds) |
| { |
| ASSERT(rds->rds_refcnt == 0); |
| ASSERT(MUTEX_HELD(&rds->rds_lock)); |
| crfree(rds->rds_cred); |
| kmem_cache_free(rds_alloc_cache, rds); |
| } |
| |
| rds_t * |
| rds_create(void *rds_ulpd, cred_t *credp) |
| { |
| rds_t *rds; |
| |
| /* User must supply a credential. */ |
| if (credp == NULL) |
| return (NULL); |
| rds = kmem_cache_alloc(rds_alloc_cache, KM_SLEEP); |
| if (rds == NULL) { |
| return (NULL); |
| } |
| |
| bzero(rds, sizeof (rds_t)); |
| mutex_init(&rds->rds_lock, NULL, MUTEX_DEFAULT, NULL); |
| cv_init(&rds->rds_refcv, NULL, CV_DEFAULT, NULL); |
| rds->rds_cred = credp; |
| rds->rds_ulpd = rds_ulpd; |
| rds->rds_zoneid = getzoneid(); |
| crhold(credp); |
| rds->rds_refcnt++; |
| return (rds); |
| } |
| |
| |
| /* |
| * Hash list removal routine for rds_t structures. |
| */ |
| void |
| rds_bind_hash_remove(rds_t *rds, boolean_t caller_holds_lock) |
| { |
| rds_t *rdsnext; |
| kmutex_t *lockp; |
| |
| if (rds->rds_ptpbhn == NULL) |
| return; |
| |
| /* |
| * Extract the lock pointer in case there are concurrent |
| * hash_remove's for this instance. |
| */ |
| ASSERT(rds->rds_port != 0); |
| if (!caller_holds_lock) { |
| lockp = &rds_bind_fanout[RDS_BIND_HASH(rds->rds_port)]. |
| rds_bf_lock; |
| ASSERT(lockp != NULL); |
| mutex_enter(lockp); |
| } |
| |
| if (rds->rds_ptpbhn != NULL) { |
| rdsnext = rds->rds_bind_hash; |
| if (rdsnext != NULL) { |
| rdsnext->rds_ptpbhn = rds->rds_ptpbhn; |
| rds->rds_bind_hash = NULL; |
| } |
| *rds->rds_ptpbhn = rdsnext; |
| rds->rds_ptpbhn = NULL; |
| } |
| |
| RDS_DEC_REF_CNT(rds); |
| |
| if (!caller_holds_lock) { |
| mutex_exit(lockp); |
| } |
| } |
| |
| void |
| rds_bind_hash_insert(rds_bf_t *rdsbf, rds_t *rds) |
| { |
| rds_t **rdsp; |
| rds_t *rdsnext; |
| |
| ASSERT(MUTEX_HELD(&rdsbf->rds_bf_lock)); |
| if (rds->rds_ptpbhn != NULL) { |
| rds_bind_hash_remove(rds, B_TRUE); |
| } |
| |
| rdsp = &rdsbf->rds_bf_rds; |
| rdsnext = rdsp[0]; |
| |
| if (rdsnext != NULL) { |
| rdsnext->rds_ptpbhn = &rds->rds_bind_hash; |
| } |
| rds->rds_bind_hash = rdsnext; |
| rds->rds_ptpbhn = rdsp; |
| rdsp[0] = rds; |
| RDS_INCR_REF_CNT(rds); |
| |
| } |
| |
| /* |
| * Everything is in network byte order |
| */ |
| /* ARGSUSED */ |
| rds_t * |
| rds_fanout(ipaddr_t local_addr, ipaddr_t rem_addr, |
| in_port_t local_port, in_port_t rem_port, zoneid_t zoneid) |
| { |
| rds_t *rds; |
| rds_bf_t *rdsbf; |
| |
| rdsbf = &rds_bind_fanout[RDS_BIND_HASH(local_port)]; |
| mutex_enter(&rdsbf->rds_bf_lock); |
| rds = rdsbf->rds_bf_rds; |
| while (rds != NULL) { |
| if (!(rds->rds_flags & RDS_CLOSING)) { |
| if ((RDS_MATCH(rds, local_port, local_addr)) && |
| ((local_addr != INADDR_LOOPBACK) || |
| (rds->rds_zoneid == zoneid))) { |
| RDS_INCR_REF_CNT(rds); |
| break; |
| } |
| } |
| rds = rds->rds_bind_hash; |
| } |
| mutex_exit(&rdsbf->rds_bf_lock); |
| return (rds); |
| } |
| |
| boolean_t |
| rds_islocal(ipaddr_t addr) |
| { |
| ire_t *ire; |
| ip_stack_t *ipst; |
| |
| ipst = netstack_find_by_zoneid(GLOBAL_ZONEID)->netstack_ip; |
| ASSERT(ipst != NULL); |
| |
| ire = ire_ctable_lookup(addr, NULL, IRE_LOCAL | IRE_LOOPBACK | |
| IRE_BROADCAST, NULL, ALL_ZONES, NULL, MATCH_IRE_TYPE, ipst); |
| netstack_rele(ipst->ips_netstack); |
| if (ire == NULL) |
| return (B_FALSE); |
| ire_refrele(ire); |
| return (B_TRUE); |
| } |