1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/ib/clients/rds/rds.h>
  27 #include <sys/ib/clients/rds/rds_kstat.h>
  28 
  29 #include <inet/ipclassifier.h>
  30 
  31 struct rds_kstat_s rds_kstat = {
  32         {"rds_nports",                  KSTAT_DATA_ULONG},
  33         {"rds_nsessions",               KSTAT_DATA_ULONG},
  34         {"rds_tx_bytes",                KSTAT_DATA_ULONG},
  35         {"rds_tx_pkts",                 KSTAT_DATA_ULONG},
  36         {"rds_tx_errors",               KSTAT_DATA_ULONG},
  37         {"rds_rx_bytes",                KSTAT_DATA_ULONG},
  38         {"rds_rx_pkts",                 KSTAT_DATA_ULONG},
  39         {"rds_rx_pkts_pending",         KSTAT_DATA_ULONG},
  40         {"rds_rx_errors",               KSTAT_DATA_ULONG},
  41         {"rds_tx_acks",                 KSTAT_DATA_ULONG},
  42         {"rds_post_recv_buf_called",    KSTAT_DATA_ULONG},
  43         {"rds_stalls_triggered",        KSTAT_DATA_ULONG},
  44         {"rds_stalls_sent",             KSTAT_DATA_ULONG},
  45         {"rds_unstalls_triggered",      KSTAT_DATA_ULONG},
  46         {"rds_unstalls_sent",           KSTAT_DATA_ULONG},
  47         {"rds_stalls_recvd",            KSTAT_DATA_ULONG},
  48         {"rds_unstalls_recvd",          KSTAT_DATA_ULONG},
  49         {"rds_stalls_ignored",          KSTAT_DATA_ULONG},
  50         {"rds_enobufs",                 KSTAT_DATA_ULONG},
  51         {"rds_ewouldblocks",            KSTAT_DATA_ULONG},
  52         {"rds_failovers",               KSTAT_DATA_ULONG},
  53         {"rds_port_quota",              KSTAT_DATA_ULONG},
  54         {"rds_port_quota_adjusted",     KSTAT_DATA_ULONG},
  55 };
  56 
  57 kstat_t *rds_kstatsp;
  58 static kmutex_t rds_kstat_mutex;
  59 
  60 
  61 struct  kmem_cache      *rds_alloc_cache;
  62 
  63 uint_t  rds_bind_fanout_size = RDS_BIND_FANOUT_SIZE;
  64 rds_bf_t *rds_bind_fanout;
  65 
  66 void
  67 rds_increment_kstat(kstat_named_t *ksnp, boolean_t lock, uint_t num)
  68 {
  69         if (lock)
  70                 mutex_enter(&rds_kstat_mutex);
  71         ksnp->value.ul += num;
  72         if (lock)
  73                 mutex_exit(&rds_kstat_mutex);
  74 }
  75 
  76 void
  77 rds_decrement_kstat(kstat_named_t *ksnp, boolean_t lock, uint_t num)
  78 {
  79         if (lock)
  80                 mutex_enter(&rds_kstat_mutex);
  81         ksnp->value.ul -= num;
  82         if (lock)
  83                 mutex_exit(&rds_kstat_mutex);
  84 }
  85 
  86 void
  87 rds_set_kstat(kstat_named_t *ksnp, boolean_t lock, ulong_t num)
  88 {
  89         if (lock)
  90                 mutex_enter(&rds_kstat_mutex);
  91         ksnp->value.ul = num;
  92         if (lock)
  93                 mutex_exit(&rds_kstat_mutex);
  94 }
  95 
  96 ulong_t
  97 rds_get_kstat(kstat_named_t *ksnp, boolean_t lock)
  98 {
  99         ulong_t value;
 100 
 101         if (lock)
 102                 mutex_enter(&rds_kstat_mutex);
 103         value = ksnp->value.ul;
 104         if (lock)
 105                 mutex_exit(&rds_kstat_mutex);
 106 
 107         return (value);
 108 }
 109 
 110 
 111 void
 112 rds_fini()
 113 {
 114         int     i;
 115 
 116         for (i = 0; i < rds_bind_fanout_size; i++) {
 117                 mutex_destroy(&rds_bind_fanout[i].rds_bf_lock);
 118         }
 119         kmem_free(rds_bind_fanout, rds_bind_fanout_size * sizeof (rds_bf_t));
 120 
 121         kmem_cache_destroy(rds_alloc_cache);
 122         kstat_delete(rds_kstatsp);
 123 }
 124 
 125 
 126 void
 127 rds_init()
 128 {
 129         rds_alloc_cache = kmem_cache_create("rds_alloc_cache",
 130             sizeof (rds_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
 131         rds_hash_init();
 132         /*
 133          * kstats
 134          */
 135         rds_kstatsp = kstat_create("rds", 0,
 136             "rds_kstat", "misc", KSTAT_TYPE_NAMED,
 137             sizeof (rds_kstat) / sizeof (kstat_named_t),
 138             KSTAT_FLAG_VIRTUAL | KSTAT_FLAG_WRITABLE);
 139         if (rds_kstatsp != NULL) {
 140                 rds_kstatsp->ks_lock = &rds_kstat_mutex;
 141                 rds_kstatsp->ks_data = (void *)&rds_kstat;
 142                 kstat_install(rds_kstatsp);
 143         }
 144 }
 145 
 146 #define UINT_32_BITS 31
 147 void
 148 rds_hash_init()
 149 {
 150         int i;
 151 
 152         if (rds_bind_fanout_size & (rds_bind_fanout_size - 1)) {
 153                 /* Not a power of two. Round up to nearest power of two */
 154                 for (i = 0; i < UINT_32_BITS; i++) {
 155                         if (rds_bind_fanout_size < (1 << i))
 156                                 break;
 157                 }
 158                 rds_bind_fanout_size = 1 << i;
 159         }
 160         rds_bind_fanout = kmem_zalloc(rds_bind_fanout_size *
 161             sizeof (rds_bf_t), KM_SLEEP);
 162         for (i = 0; i < rds_bind_fanout_size; i++) {
 163                 mutex_init(&rds_bind_fanout[i].rds_bf_lock, NULL, MUTEX_DEFAULT,
 164                     NULL);
 165         }
 166 }
 167 
 168 void
 169 rds_free(rds_t *rds)
 170 {
 171         ASSERT(rds->rds_refcnt == 0);
 172         ASSERT(MUTEX_HELD(&rds->rds_lock));
 173         crfree(rds->rds_cred);
 174         kmem_cache_free(rds_alloc_cache, rds);
 175 }
 176 
 177 rds_t *
 178 rds_create(void *rds_ulpd, cred_t *credp)
 179 {
 180         rds_t   *rds;
 181 
 182         /* User must supply a credential. */
 183         if (credp == NULL)
 184                 return (NULL);
 185         rds = kmem_cache_alloc(rds_alloc_cache, KM_SLEEP);
 186         if (rds == NULL) {
 187                 return (NULL);
 188         }
 189 
 190         bzero(rds, sizeof (rds_t));
 191         mutex_init(&rds->rds_lock, NULL, MUTEX_DEFAULT, NULL);
 192         cv_init(&rds->rds_refcv, NULL, CV_DEFAULT, NULL);
 193         rds->rds_cred = credp;
 194         rds->rds_ulpd = rds_ulpd;
 195         rds->rds_zoneid = getzoneid();
 196         crhold(credp);
 197         rds->rds_refcnt++;
 198         return (rds);
 199 }
 200 
 201 
 202 /*
 203  * Hash list removal routine for rds_t structures.
 204  */
 205 void
 206 rds_bind_hash_remove(rds_t *rds, boolean_t caller_holds_lock)
 207 {
 208         rds_t   *rdsnext;
 209         kmutex_t *lockp;
 210 
 211         if (rds->rds_ptpbhn == NULL)
 212                 return;
 213 
 214         /*
 215          * Extract the lock pointer in case there are concurrent
 216          * hash_remove's for this instance.
 217          */
 218         ASSERT(rds->rds_port != 0);
 219         if (!caller_holds_lock) {
 220                 lockp = &rds_bind_fanout[RDS_BIND_HASH(rds->rds_port)].
 221                     rds_bf_lock;
 222                 ASSERT(lockp != NULL);
 223                 mutex_enter(lockp);
 224         }
 225 
 226         if (rds->rds_ptpbhn != NULL) {
 227                 rdsnext = rds->rds_bind_hash;
 228                 if (rdsnext != NULL) {
 229                         rdsnext->rds_ptpbhn = rds->rds_ptpbhn;
 230                         rds->rds_bind_hash = NULL;
 231                 }
 232                 *rds->rds_ptpbhn = rdsnext;
 233                 rds->rds_ptpbhn = NULL;
 234         }
 235 
 236         RDS_DEC_REF_CNT(rds);
 237 
 238         if (!caller_holds_lock) {
 239                 mutex_exit(lockp);
 240         }
 241 }
 242 
 243 void
 244 rds_bind_hash_insert(rds_bf_t *rdsbf, rds_t *rds)
 245 {
 246         rds_t   **rdsp;
 247         rds_t   *rdsnext;
 248 
 249         ASSERT(MUTEX_HELD(&rdsbf->rds_bf_lock));
 250         if (rds->rds_ptpbhn != NULL) {
 251                 rds_bind_hash_remove(rds, B_TRUE);
 252         }
 253 
 254         rdsp = &rdsbf->rds_bf_rds;
 255         rdsnext = rdsp[0];
 256 
 257         if (rdsnext != NULL) {
 258                 rdsnext->rds_ptpbhn = &rds->rds_bind_hash;
 259         }
 260         rds->rds_bind_hash = rdsnext;
 261         rds->rds_ptpbhn = rdsp;
 262         rdsp[0] = rds;
 263         RDS_INCR_REF_CNT(rds);
 264 
 265 }
 266 
 267 /*
 268  * Everything is in network byte order
 269  */
 270 /* ARGSUSED */
 271 rds_t *
 272 rds_fanout(ipaddr_t local_addr, ipaddr_t rem_addr,
 273     in_port_t local_port, in_port_t rem_port, zoneid_t zoneid)
 274 {
 275         rds_t   *rds;
 276         rds_bf_t *rdsbf;
 277 
 278         rdsbf = &rds_bind_fanout[RDS_BIND_HASH(local_port)];
 279         mutex_enter(&rdsbf->rds_bf_lock);
 280         rds = rdsbf->rds_bf_rds;
 281         while (rds != NULL) {
 282                 if (!(rds->rds_flags & RDS_CLOSING)) {
 283                         if ((RDS_MATCH(rds, local_port, local_addr)) &&
 284                             ((local_addr != INADDR_LOOPBACK) ||
 285                             (rds->rds_zoneid == zoneid))) {
 286                                 RDS_INCR_REF_CNT(rds);
 287                                 break;
 288                         }
 289                 }
 290                 rds = rds->rds_bind_hash;
 291         }
 292         mutex_exit(&rdsbf->rds_bf_lock);
 293         return (rds);
 294 }
 295 
 296 boolean_t
 297 rds_islocal(ipaddr_t addr)
 298 {
 299         ip_stack_t *ipst;
 300 
 301         ipst = netstack_find_by_zoneid(GLOBAL_ZONEID)->netstack_ip;
 302         ASSERT(ipst != NULL);
 303         if (ip_laddr_verify_v4(addr, ALL_ZONES, ipst, B_FALSE) == IPVL_BAD) {
 304                 netstack_rele(ipst->ips_netstack);
 305                 return (B_FALSE);
 306         }
 307         netstack_rele(ipst->ips_netstack);
 308         return (B_TRUE);
 309 }