stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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, Version 1.0 only |
| 6 | * (the "License"). You may not use this file except in compliance |
| 7 | * with the License. |
| 8 | * |
| 9 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| 10 | * or http://www.opensolaris.org/os/licensing. |
| 11 | * See the License for the specific language governing permissions |
| 12 | * and limitations under the License. |
| 13 | * |
| 14 | * When distributing Covered Code, include this CDDL HEADER in each |
| 15 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| 16 | * If applicable, add the following below this CDDL HEADER, with the |
| 17 | * fields enclosed by brackets "[]" replaced with your own identifying |
| 18 | * information: Portions Copyright [yyyy] [name of copyright owner] |
| 19 | * |
| 20 | * CDDL HEADER END |
| 21 | */ |
| 22 | /* |
| 23 | * Copyright 2004 Sun Microsystems, Inc. All rights reserved. |
| 24 | * Use is subject to license terms. |
| 25 | */ |
| 26 | |
| 27 | #pragma ident "%Z%%M% %I% %E% SMI" |
| 28 | |
| 29 | #include <sys/types.h> |
| 30 | #include <sys/kmem.h> |
| 31 | #include <sys/bitmap.h> |
| 32 | #include <sys/stream.h> |
| 33 | #include <sys/strsubr.h> |
| 34 | #define _SUN_TPI_VERSION 2 |
| 35 | #include <sys/tihdr.h> |
| 36 | #include <sys/suntpi.h> |
| 37 | |
| 38 | /* |
| 39 | * Hash table parameters for tpi_provinfo_table. |
| 40 | */ |
| 41 | #define TPI_HASH_BITS 4 |
| 42 | #define TPI_NHASH (1 << TPI_HASH_BITS) |
| 43 | |
| 44 | /* |
| 45 | * Use the first element in the key for the hash. |
| 46 | */ |
| 47 | #define TPI_HASH(p) ((((uintptr_t *)p)[0] >> tpi_hashshift) % TPI_NHASH) |
| 48 | /* |
| 49 | * SAMESTR is a very confusing name. LAST_QUEUE is introduced for readability. |
| 50 | */ |
| 51 | #define LAST_QUEUE(q) (!SAMESTR(q)) |
| 52 | |
| 53 | static tpi_provinfo_t *tpi_provinfo_table[TPI_NHASH]; |
| 54 | static kmutex_t tpi_provinfo_lock; |
| 55 | static int tpi_hashshift; |
| 56 | |
| 57 | /* |
| 58 | * In most cases there is some transport provider (like tcp or udp) below |
| 59 | * transport user (like timod or sockets). However, it is possible to construct |
| 60 | * stream without transport provider (e.g. by pushing timod into FIFO). It is |
| 61 | * hardly of any use, but this condition was observed with sparcv9 abi tests. |
| 62 | * To count for such special case, a special tpi_nullprov static data is |
| 63 | * provided to cache information about such degenerated null-transport case. |
| 64 | */ |
| 65 | static tpi_provinfo_t tpi_nullprov; /* Placeholder for null transport */ |
| 66 | |
| 67 | /* |
| 68 | * Initialise the TPI support routines. Called from strinit(). |
| 69 | */ |
| 70 | void |
| 71 | tpi_init() |
| 72 | { |
| 73 | mutex_init(&tpi_provinfo_lock, NULL, MUTEX_DEFAULT, NULL); |
| 74 | |
| 75 | /* |
| 76 | * Calculate the right shift for hashing a tpi_provinfo_t. |
| 77 | */ |
| 78 | tpi_hashshift = highbit(sizeof (tpi_provinfo_t)); |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | * Generate a downstream signature given the write-side queue. It |
| 83 | * passes back the size of the generated key in *keylenp. This routine |
| 84 | * cannot multithread as it returns a pointer to a static data item. |
| 85 | * |
| 86 | * There is no way (in the current module loading infrastructure) to |
| 87 | * _absolutely_ guarantee that the key below uniquely identifies an |
| 88 | * arrangement of modules and drivers. A module _might_ be unloaded and |
| 89 | * another module _might_ be loaded such that the qi_minfo is at _exactly_ |
| 90 | * same kernel address, and then it _might_ be placed in a transport |
| 91 | * provider stream in exactly the same configuration (modules above and |
| 92 | * below all identical) - but it would take quite a few coincidences |
| 93 | * and modules loading and unloading does not usually happen n times a |
| 94 | * second... |
| 95 | */ |
| 96 | static void * |
| 97 | tpi_makekey(queue_t *q, size_t *keylenp) |
| 98 | { |
| 99 | static uintptr_t *key = NULL; |
| 100 | int i; |
| 101 | |
| 102 | ASSERT(q != NULL); |
| 103 | ASSERT(MUTEX_HELD(&tpi_provinfo_lock)); |
| 104 | |
| 105 | /* assert this queue is write queue and qprocson() is called before */ |
| 106 | ASSERT((q->q_flag & QREADR) == 0); |
| 107 | ASSERT(q->q_next != NULL); |
| 108 | |
| 109 | /* |
| 110 | * This can be global because tpi_makekey is called with |
| 111 | * tpi_provinfo_lock. |
| 112 | */ |
| 113 | if (key == NULL) |
| 114 | key = kmem_alloc((nstrpush + 1) * sizeof (uintptr_t), KM_SLEEP); |
| 115 | |
| 116 | ASSERT(key != NULL); |
| 117 | |
| 118 | /* |
| 119 | * Go down q_next to the driver, but no further. We use the qi_minfo |
| 120 | * because we can find in from the queue and it is a stable part of |
| 121 | * any driver/module infrastructure. |
| 122 | */ |
| 123 | for (i = 0; !LAST_QUEUE(q) && (q = q->q_next) != NULL; ++i) { |
| 124 | ASSERT(i < nstrpush + 1); |
| 125 | key[i] = (uintptr_t)q->q_qinfo->qi_minfo; |
| 126 | } |
| 127 | |
| 128 | /* |
| 129 | * Allocate the actual key with the proper length, and pass it |
| 130 | * all back. |
| 131 | */ |
| 132 | *keylenp = i * sizeof (uintptr_t); |
| 133 | return ((void *)key); |
| 134 | } |
| 135 | |
| 136 | /* |
| 137 | * Find an existing provider entry given a queue pointer, or allocate a |
| 138 | * new empty entry if not found. Because this routine calls kmem_alloc |
| 139 | * with KM_SLEEP, and because it traverses the q_next pointers of a stream |
| 140 | * it must be called with a proper user context and within a perimeter |
| 141 | * which protects the STREAM e.g. an open routine. This routine always |
| 142 | * returns a valid pointer. |
| 143 | */ |
| 144 | tpi_provinfo_t * |
| 145 | tpi_findprov(queue_t *q) |
| 146 | { |
| 147 | void *key; |
| 148 | size_t keylen; |
| 149 | tpi_provinfo_t **tpp; |
| 150 | |
| 151 | mutex_enter(&tpi_provinfo_lock); |
| 152 | |
| 153 | /* |
| 154 | * Must hold tpi_provinfo_lock since tpi_makekey() returns a pointer |
| 155 | * to static data. |
| 156 | */ |
| 157 | key = tpi_makekey(WR(q), &keylen); |
| 158 | |
| 159 | if (keylen == 0) { |
| 160 | /* there is nothing below us, return special nullprov entry */ |
| 161 | mutex_exit(&tpi_provinfo_lock); |
| 162 | return (&tpi_nullprov); |
| 163 | } |
| 164 | |
| 165 | /* |
| 166 | * Look for an existing entry, or the place to put a new one. |
| 167 | */ |
| 168 | for (tpp = &tpi_provinfo_table[TPI_HASH(key)]; *tpp != NULL; |
| 169 | tpp = &(*tpp)->tpi_next) { |
| 170 | if ((*tpp)->tpi_keylen == keylen && |
| 171 | bcmp((*tpp)->tpi_key, key, keylen) == 0) { |
| 172 | mutex_exit(&tpi_provinfo_lock); |
| 173 | return (*tpp); |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | /* |
| 178 | * Allocate and fill in the new tpi_provinfo_t. |
| 179 | */ |
| 180 | *tpp = kmem_zalloc(sizeof (tpi_provinfo_t), KM_SLEEP); |
| 181 | (*tpp)->tpi_key = kmem_alloc(keylen, KM_SLEEP); |
| 182 | bcopy(key, (*tpp)->tpi_key, keylen); |
| 183 | (*tpp)->tpi_keylen = keylen; |
| 184 | mutex_init(&(*tpp)->tpi_lock, NULL, MUTEX_DEFAULT, NULL); |
| 185 | |
| 186 | mutex_exit(&tpi_provinfo_lock); |
| 187 | return (*tpp); |
| 188 | } |
| 189 | |
| 190 | /* |
| 191 | * Allocate a TPI ACK reusing the old message if possible. |
| 192 | */ |
| 193 | mblk_t * |
| 194 | tpi_ack_alloc(mblk_t *mp, size_t size, uchar_t db_type, t_scalar_t prim) |
| 195 | { |
| 196 | mblk_t *omp = mp; |
| 197 | |
| 198 | if ((mp = reallocb(mp, size, 0)) == NULL) { |
| 199 | freemsg(omp); |
| 200 | return (NULL); |
| 201 | } |
| 202 | if (mp->b_cont != NULL) { |
| 203 | freemsg(mp->b_cont); |
| 204 | mp->b_cont = NULL; |
| 205 | } |
| 206 | mp->b_datap->db_type = db_type; |
| 207 | mp->b_wptr = mp->b_rptr + size; |
| 208 | ((union T_primitives *)mp->b_rptr)->type = prim; |
| 209 | return (mp); |
| 210 | } |