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