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