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 2011 Nexenta Systems, Inc. All rights reserved. 23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/kmem.h> 28 #include <sys/ddi.h> 29 #include <sys/sunddi.h> 30 #include <sys/cmn_err.h> 31 #include <sys/door.h> 32 #include <smbsrv/smb_kproto.h> 33 #include <smbsrv/smb_door.h> 34 35 static int smb_kdoor_send(smb_server_t *, smb_doorarg_t *); 36 static int smb_kdoor_receive(smb_server_t *, smb_doorarg_t *); 37 static int smb_kdoor_upcall_private(smb_server_t *, smb_doorarg_t *); 38 static int smb_kdoor_encode(smb_doorarg_t *); 39 static int smb_kdoor_decode(smb_doorarg_t *); 40 static void smb_kdoor_sethdr(smb_doorarg_t *, uint32_t); 41 static boolean_t smb_kdoor_chkhdr(smb_doorarg_t *, smb_doorhdr_t *); 42 static void smb_kdoor_free(door_arg_t *); 43 44 void 45 smb_kdoor_init(smb_server_t *sv) 46 { 47 sv->sv_kdoor_id = -1; 48 mutex_init(&sv->sv_kdoor_mutex, NULL, MUTEX_DEFAULT, NULL); 49 cv_init(&sv->sv_kdoor_cv, NULL, CV_DEFAULT, NULL); 50 } 51 52 void 53 smb_kdoor_fini(smb_server_t *sv) 54 { 55 smb_kdoor_close(sv); 56 cv_destroy(&sv->sv_kdoor_cv); 57 mutex_destroy(&sv->sv_kdoor_mutex); 58 } 59 60 /* 61 * Open the door. If the door is already open, close it first 62 * because the door-id has probably changed. 63 */ 64 int 65 smb_kdoor_open(smb_server_t *sv, int door_id) 66 { 67 int rc; 68 69 smb_kdoor_close(sv); 70 71 mutex_enter(&sv->sv_kdoor_mutex); 72 sv->sv_kdoor_ncall = 0; 73 74 if (sv->sv_kdoor_hd == NULL) { 75 sv->sv_kdoor_id = door_id; 76 sv->sv_kdoor_hd = door_ki_lookup(door_id); 77 } 78 79 rc = (sv->sv_kdoor_hd == NULL) ? -1 : 0; 80 mutex_exit(&sv->sv_kdoor_mutex); 81 return (rc); 82 } 83 84 /* 85 * Close the door. 86 */ 87 void 88 smb_kdoor_close(smb_server_t *sv) 89 { 90 mutex_enter(&sv->sv_kdoor_mutex); 91 92 if (sv->sv_kdoor_hd != NULL) { 93 while (sv->sv_kdoor_ncall > 0) 94 cv_wait(&sv->sv_kdoor_cv, &sv->sv_kdoor_mutex); 95 96 door_ki_rele(sv->sv_kdoor_hd); 97 sv->sv_kdoor_hd = NULL; 98 sv->sv_kdoor_id = -1; 99 } 100 101 mutex_exit(&sv->sv_kdoor_mutex); 102 } 103 104 /* 105 * Wrapper to handle door call reference counting. 106 */ 107 int 108 smb_kdoor_upcall(smb_server_t *sv, uint32_t cmd, 109 void *req_data, xdrproc_t req_xdr, 110 void *rsp_data, xdrproc_t rsp_xdr) 111 { 112 smb_doorarg_t da; 113 int rc; 114 115 bzero(&da, sizeof (smb_doorarg_t)); 116 da.da_opcode = cmd; 117 da.da_opname = smb_doorhdr_opname(cmd); 118 da.da_req_xdr = req_xdr; 119 da.da_rsp_xdr = rsp_xdr; 120 da.da_req_data = req_data; 121 da.da_rsp_data = rsp_data; 122 123 if ((req_data == NULL && req_xdr != NULL) || 124 (rsp_data == NULL && rsp_xdr != NULL)) { 125 cmn_err(CE_WARN, "smb_kdoor_upcall[%s]: invalid param", 126 da.da_opname); 127 return (-1); 128 } 129 130 if (rsp_data != NULL && rsp_xdr != NULL) 131 da.da_flags = SMB_DF_ASYNC; 132 133 if ((da.da_event = smb_event_create(sv, SMB_EVENT_TIMEOUT)) == NULL) 134 return (-1); 135 136 mutex_enter(&sv->sv_kdoor_mutex); 137 138 if (sv->sv_kdoor_hd == NULL) { 139 mutex_exit(&sv->sv_kdoor_mutex); 140 141 if (smb_kdoor_open(sv, sv->sv_kdoor_id) != 0) { 142 smb_event_destroy(da.da_event); 143 return (-1); 144 } 145 146 mutex_enter(&sv->sv_kdoor_mutex); 147 } 148 149 sv->sv_kdoor_ncall++; 150 mutex_exit(&sv->sv_kdoor_mutex); 151 152 if (da.da_flags & SMB_DF_ASYNC) { 153 if ((rc = smb_kdoor_send(sv, &da)) == 0) { 154 if (smb_event_wait(da.da_event) != 0) 155 rc = -1; 156 else 157 rc = smb_kdoor_receive(sv, &da); 158 } 159 } else { 160 if ((rc = smb_kdoor_encode(&da)) == 0) { 161 if ((rc = smb_kdoor_upcall_private(sv, &da)) == 0) 162 rc = smb_kdoor_decode(&da); 163 } 164 smb_kdoor_free(&da.da_arg); 165 } 166 167 smb_event_destroy(da.da_event); 168 169 mutex_enter(&sv->sv_kdoor_mutex); 170 if ((--sv->sv_kdoor_ncall) == 0) 171 cv_signal(&sv->sv_kdoor_cv); 172 mutex_exit(&sv->sv_kdoor_mutex); 173 return (rc); 174 } 175 176 /* 177 * Send the request half of the consumer's door call. 178 */ 179 static int 180 smb_kdoor_send(smb_server_t *sv, smb_doorarg_t *outer_da) 181 { 182 smb_doorarg_t da; 183 int rc; 184 185 bcopy(outer_da, &da, sizeof (smb_doorarg_t)); 186 da.da_rsp_xdr = NULL; 187 da.da_rsp_data = NULL; 188 189 if (smb_kdoor_encode(&da) != 0) 190 return (-1); 191 192 if ((rc = smb_kdoor_upcall_private(sv, &da)) == 0) 193 rc = smb_kdoor_decode(&da); 194 195 smb_kdoor_free(&da.da_arg); 196 return (rc); 197 } 198 199 /* 200 * Get the response half for the consumer's door call. 201 */ 202 static int 203 smb_kdoor_receive(smb_server_t *sv, smb_doorarg_t *outer_da) 204 { 205 smb_doorarg_t da; 206 int rc; 207 208 bcopy(outer_da, &da, sizeof (smb_doorarg_t)); 209 da.da_opcode = SMB_DR_ASYNC_RESPONSE; 210 da.da_opname = smb_doorhdr_opname(da.da_opcode); 211 da.da_flags &= ~SMB_DF_ASYNC; 212 da.da_req_xdr = NULL; 213 da.da_req_data = NULL; 214 215 if (smb_kdoor_encode(&da) != 0) 216 return (-1); 217 218 if ((rc = smb_kdoor_upcall_private(sv, &da)) == 0) 219 rc = smb_kdoor_decode(&da); 220 221 smb_kdoor_free(&da.da_arg); 222 return (rc); 223 } 224 225 /* 226 * We use a copy of the door arg because doorfs may change data_ptr 227 * and we want to detect that when freeing the door buffers. After 228 * this call, response data must be referenced via rbuf and rsize. 229 */ 230 static int 231 smb_kdoor_upcall_private(smb_server_t *sv, smb_doorarg_t *da) 232 { 233 door_arg_t door_arg; 234 int i; 235 int rc; 236 237 bcopy(&da->da_arg, &door_arg, sizeof (door_arg_t)); 238 239 for (i = 0; i < SMB_DOOR_CALL_RETRIES; ++i) { 240 if (smb_server_is_stopping(sv)) 241 return (-1); 242 243 if ((rc = door_ki_upcall_limited(sv->sv_kdoor_hd, &door_arg, 244 NULL, SIZE_MAX, 0)) == 0) 245 break; 246 247 if (rc != EAGAIN && rc != EINTR) 248 return (-1); 249 } 250 251 if (rc != 0 || door_arg.data_size == 0 || door_arg.rsize == 0) 252 return (-1); 253 254 da->da_arg.rbuf = door_arg.data_ptr; 255 da->da_arg.rsize = door_arg.rsize; 256 return (0); 257 } 258 259 static int 260 smb_kdoor_encode(smb_doorarg_t *da) 261 { 262 XDR xdrs; 263 char *buf; 264 uint32_t len; 265 266 len = xdr_sizeof(smb_doorhdr_xdr, &da->da_hdr); 267 if (da->da_req_xdr != NULL) 268 len += xdr_sizeof(da->da_req_xdr, da->da_req_data); 269 270 smb_kdoor_sethdr(da, len); 271 272 buf = kmem_zalloc(len, KM_SLEEP); 273 xdrmem_create(&xdrs, buf, len, XDR_ENCODE); 274 275 if (!smb_doorhdr_xdr(&xdrs, &da->da_hdr)) { 276 cmn_err(CE_WARN, "smb_kdoor_encode[%s]: header encode failed", 277 da->da_opname); 278 kmem_free(buf, len); 279 xdr_destroy(&xdrs); 280 return (-1); 281 } 282 283 if (da->da_req_xdr != NULL) { 284 if (!da->da_req_xdr(&xdrs, da->da_req_data)) { 285 cmn_err(CE_WARN, "smb_kdoor_encode[%s]: encode failed", 286 da->da_opname); 287 kmem_free(buf, len); 288 xdr_destroy(&xdrs); 289 return (-1); 290 } 291 } 292 293 da->da_arg.data_ptr = buf; 294 da->da_arg.data_size = len; 295 da->da_arg.desc_ptr = NULL; 296 da->da_arg.desc_num = 0; 297 da->da_arg.rbuf = buf; 298 da->da_arg.rsize = len; 299 300 xdr_destroy(&xdrs); 301 return (0); 302 } 303 304 /* 305 * Decode the response in rbuf and rsize. 306 */ 307 static int 308 smb_kdoor_decode(smb_doorarg_t *da) 309 { 310 XDR xdrs; 311 smb_doorhdr_t hdr; 312 char *rbuf = da->da_arg.rbuf; 313 uint32_t rsize = da->da_arg.rsize; 314 315 if (rbuf == NULL || rsize == 0) { 316 cmn_err(CE_WARN, "smb_kdoor_decode[%s]: invalid param", 317 da->da_opname); 318 return (-1); 319 } 320 321 xdrmem_create(&xdrs, rbuf, rsize, XDR_DECODE); 322 323 if (!smb_doorhdr_xdr(&xdrs, &hdr)) { 324 cmn_err(CE_WARN, "smb_kdoor_decode[%s]: header decode failed", 325 da->da_opname); 326 xdr_destroy(&xdrs); 327 return (-1); 328 } 329 330 if (!smb_kdoor_chkhdr(da, &hdr)) { 331 xdr_destroy(&xdrs); 332 return (-1); 333 } 334 335 if (hdr.dh_datalen != 0 && da->da_rsp_xdr != NULL) { 336 if (!da->da_rsp_xdr(&xdrs, da->da_rsp_data)) { 337 cmn_err(CE_WARN, "smb_kdoor_decode[%s]: decode failed", 338 da->da_opname); 339 xdr_destroy(&xdrs); 340 return (-1); 341 } 342 } 343 344 xdr_destroy(&xdrs); 345 return (0); 346 } 347 348 static void 349 smb_kdoor_sethdr(smb_doorarg_t *da, uint32_t datalen) 350 { 351 smb_doorhdr_t *hdr = &da->da_hdr; 352 353 bzero(hdr, sizeof (smb_doorhdr_t)); 354 hdr->dh_magic = SMB_DOOR_HDR_MAGIC; 355 hdr->dh_flags = da->da_flags | SMB_DF_SYSSPACE; 356 hdr->dh_op = da->da_opcode; 357 hdr->dh_txid = smb_event_txid(da->da_event); 358 hdr->dh_datalen = datalen; 359 hdr->dh_door_rc = SMB_DOP_NOT_CALLED; 360 } 361 362 static boolean_t 363 smb_kdoor_chkhdr(smb_doorarg_t *da, smb_doorhdr_t *hdr) 364 { 365 if ((hdr->dh_magic != SMB_DOOR_HDR_MAGIC) || 366 (hdr->dh_op != da->da_hdr.dh_op) || 367 (hdr->dh_txid != da->da_hdr.dh_txid)) { 368 cmn_err(CE_WARN, "smb_kdoor_chkhdr[%s]: invalid header", 369 da->da_opname); 370 return (B_FALSE); 371 } 372 373 switch (hdr->dh_door_rc) { 374 case SMB_DOP_SUCCESS: 375 break; 376 377 /* SMB_DOP_EMPTYBUF is a "normal" error (silent). */ 378 case SMB_DOP_EMPTYBUF: 379 return (B_FALSE); 380 381 default: 382 cmn_err(CE_WARN, "smb_kdoor_chkhdr[%s]: call failed: %u", 383 da->da_opname, hdr->dh_door_rc); 384 return (B_FALSE); 385 } 386 387 return (B_TRUE); 388 } 389 390 /* 391 * Free both the argument and result door buffers regardless of the status 392 * of the up-call. The doorfs allocates a new buffer if the result buffer 393 * passed by the client is too small. 394 */ 395 static void 396 smb_kdoor_free(door_arg_t *arg) 397 { 398 if (arg->rbuf != NULL && arg->rbuf != arg->data_ptr) 399 kmem_free(arg->rbuf, arg->rsize); 400 401 if (arg->data_ptr != NULL) 402 kmem_free(arg->data_ptr, arg->data_size); 403 } 404