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