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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/param.h> 30 #include <sys/ddi.h> 31 #include <sys/sunddi.h> 32 #include <sys/time.h> 33 #include <sys/varargs.h> 34 #include <sys/modctl.h> 35 #include <sys/pathname.h> 36 #include <sys/vnode.h> 37 #include <sys/socket.h> 38 #include <sys/ksocket.h> 39 #undef mem_free /* XXX Remove this after we convert everything to kmem_alloc */ 40 41 #include <smbsrv/smb_vops.h> 42 #include <smbsrv/smb.h> 43 #include <smbsrv/smb_kproto.h> 44 #include <smbsrv/smb_kstat.h> 45 46 static kmem_cache_t *smb_txr_cache = NULL; 47 48 /* 49 * smb_net_init 50 * 51 * This function initializes the resources necessary to access the 52 * network. It assumes it won't be called simultaneously by multiple 53 * threads. 54 * 55 * Return Value 56 * 57 * 0 Initialization successful 58 * ENOMEM Initialization failed 59 */ 60 void 61 smb_net_init(void) 62 { 63 64 if (smb_txr_cache != NULL) 65 return; 66 67 smb_txr_cache = kmem_cache_create(SMBSRV_KSTAT_TXRCACHE, 68 sizeof (smb_txreq_t), 8, NULL, NULL, NULL, NULL, NULL, 0); 69 } 70 71 /* 72 * smb_net_fini 73 * 74 * This function releases the resources allocated by smb_net_init(). It 75 * assumes it won't be called simultaneously by multiple threads. 76 * This function can safely be called even if smb_net_init() hasn't been 77 * called previously. 78 * 79 * Return Value 80 * 81 * None 82 */ 83 void 84 smb_net_fini(void) 85 { 86 if (smb_txr_cache) { 87 kmem_cache_destroy(smb_txr_cache); 88 smb_txr_cache = NULL; 89 } 90 } 91 92 /* 93 * SMB Network Socket API 94 * 95 * smb_socreate: Creates an socket based on domain/type. 96 * smb_soshutdown: Disconnect a socket created with smb_socreate 97 * smb_sodestroy: Release resources associated with a socket 98 * smb_sosend: Send the contents of a buffer on a socket 99 * smb_sorecv: Receive data into a buffer from a socket 100 * smb_iov_sosend: Send the contents of an iovec on a socket 101 * smb_iov_sorecv: Receive data into an iovec from a socket 102 */ 103 104 ksocket_t 105 smb_socreate(int domain, int type, int protocol) 106 { 107 ksocket_t sock; 108 int err = 0; 109 110 err = ksocket_socket(&sock, domain, type, protocol, KSOCKET_SLEEP, 111 CRED()); 112 113 if (err != 0) 114 return (NULL); 115 else 116 return (sock); 117 } 118 119 /* 120 * smb_soshutdown will disconnect the socket and prevent subsequent PDU 121 * reception and transmission. The sonode still exists but its state 122 * gets modified to indicate it is no longer connected. Calls to 123 * smb_sorecv/smb_iov_sorecv will return so smb_soshutdown can be used 124 * regain control of a thread stuck in smb_sorecv. 125 */ 126 void 127 smb_soshutdown(ksocket_t so) 128 { 129 (void) ksocket_shutdown(so, SHUT_RDWR, CRED()); 130 } 131 132 /* 133 * smb_sodestroy releases all resources associated with a socket previously 134 * created with smb_socreate. The socket must be shutdown using smb_soshutdown 135 * before the socket is destroyed with smb_sodestroy, otherwise undefined 136 * behavior will result. 137 */ 138 void 139 smb_sodestroy(ksocket_t so) 140 { 141 (void) ksocket_close(so, CRED()); 142 } 143 144 int 145 smb_sorecv(ksocket_t so, void *msg, size_t len) 146 { 147 size_t recvd; 148 int err; 149 150 ASSERT(so != NULL); 151 ASSERT(len != 0); 152 153 if ((err = ksocket_recv(so, msg, len, MSG_WAITALL, &recvd, 154 CRED())) != 0) { 155 return (err); 156 } 157 158 /* Successful receive */ 159 return ((recvd == len) ? 0 : -1); 160 } 161 162 /* 163 * smb_net_txl_constructor 164 * 165 * Transmit list constructor 166 */ 167 void 168 smb_net_txl_constructor(smb_txlst_t *txl) 169 { 170 ASSERT(txl->tl_magic != SMB_TXLST_MAGIC); 171 172 mutex_init(&txl->tl_mutex, NULL, MUTEX_DEFAULT, NULL); 173 list_create(&txl->tl_list, sizeof (smb_txreq_t), 174 offsetof(smb_txreq_t, tr_lnd)); 175 txl->tl_active = B_FALSE; 176 txl->tl_magic = SMB_TXLST_MAGIC; 177 } 178 179 /* 180 * smb_net_txl_destructor 181 * 182 * Transmit list destructor 183 */ 184 void 185 smb_net_txl_destructor(smb_txlst_t *txl) 186 { 187 ASSERT(txl->tl_magic == SMB_TXLST_MAGIC); 188 189 txl->tl_magic = 0; 190 list_destroy(&txl->tl_list); 191 mutex_destroy(&txl->tl_mutex); 192 } 193 194 /* 195 * smb_net_txr_alloc 196 * 197 * Transmit buffer allocator 198 */ 199 smb_txreq_t * 200 smb_net_txr_alloc(void) 201 { 202 smb_txreq_t *txr; 203 204 txr = kmem_cache_alloc(smb_txr_cache, KM_SLEEP); 205 txr->tr_len = 0; 206 bzero(&txr->tr_lnd, sizeof (txr->tr_lnd)); 207 txr->tr_magic = SMB_TXREQ_MAGIC; 208 return (txr); 209 } 210 211 /* 212 * smb_net_txr_free 213 * 214 * Transmit buffer deallocator 215 */ 216 void 217 smb_net_txr_free(smb_txreq_t *txr) 218 { 219 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC); 220 ASSERT(!list_link_active(&txr->tr_lnd)); 221 222 txr->tr_magic = 0; 223 kmem_cache_free(smb_txr_cache, txr); 224 } 225 226 /* 227 * smb_net_txr_send 228 * 229 * This routine puts the transmit buffer passed in on the wire. If another 230 * thread is already draining the transmit list, the transmit buffer is 231 * queued and the routine returns immediately. 232 */ 233 int 234 smb_net_txr_send(ksocket_t so, smb_txlst_t *txl, smb_txreq_t *txr) 235 { 236 list_t local; 237 int rc = 0; 238 size_t sent = 0; 239 size_t len; 240 241 ASSERT(txl->tl_magic == SMB_TXLST_MAGIC); 242 243 mutex_enter(&txl->tl_mutex); 244 list_insert_tail(&txl->tl_list, txr); 245 if (txl->tl_active) { 246 mutex_exit(&txl->tl_mutex); 247 return (0); 248 } 249 txl->tl_active = B_TRUE; 250 251 list_create(&local, sizeof (smb_txreq_t), 252 offsetof(smb_txreq_t, tr_lnd)); 253 254 while (!list_is_empty(&txl->tl_list)) { 255 list_move_tail(&local, &txl->tl_list); 256 mutex_exit(&txl->tl_mutex); 257 while ((txr = list_head(&local)) != NULL) { 258 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC); 259 list_remove(&local, txr); 260 261 len = txr->tr_len; 262 rc = ksocket_send(so, txr->tr_buf, txr->tr_len, 263 MSG_WAITALL, &sent, CRED()); 264 smb_net_txr_free(txr); 265 if ((rc == 0) && (sent == len)) 266 continue; 267 268 if (rc == 0) 269 rc = -1; 270 271 while ((txr = list_head(&local)) != NULL) { 272 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC); 273 list_remove(&local, txr); 274 smb_net_txr_free(txr); 275 } 276 break; 277 } 278 mutex_enter(&txl->tl_mutex); 279 if (rc == 0) 280 continue; 281 282 while ((txr = list_head(&txl->tl_list)) != NULL) { 283 ASSERT(txr->tr_magic == SMB_TXREQ_MAGIC); 284 list_remove(&txl->tl_list, txr); 285 smb_net_txr_free(txr); 286 } 287 break; 288 } 289 txl->tl_active = B_FALSE; 290 mutex_exit(&txl->tl_mutex); 291 return (rc); 292 } 293