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/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_door_svc.h> 33 #include <smbsrv/smb_common_door.h> 34 35 #define SMB_KDOOR_RETRIES 3 36 37 static char *smb_kdoor_upcall(char *, size_t, door_desc_t *, uint_t, size_t *); 38 39 door_handle_t smb_kdoor_clnt_hd = NULL; 40 static int smb_kdoor_clnt_id = -1; 41 static uint64_t smb_kdoor_clnt_ncall = 0; 42 static kmutex_t smb_kdoor_clnt_mutex; 43 static kcondvar_t smb_kdoor_clnt_cv; 44 45 void 46 smb_kdoor_clnt_init(void) 47 { 48 mutex_init(&smb_kdoor_clnt_mutex, NULL, MUTEX_DEFAULT, NULL); 49 cv_init(&smb_kdoor_clnt_cv, NULL, CV_DEFAULT, NULL); 50 } 51 52 void 53 smb_kdoor_clnt_fini(void) 54 { 55 smb_kdoor_clnt_close(); 56 cv_destroy(&smb_kdoor_clnt_cv); 57 mutex_destroy(&smb_kdoor_clnt_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_clnt_open(int door_id) 66 { 67 int rc; 68 69 smb_kdoor_clnt_close(); 70 71 mutex_enter(&smb_kdoor_clnt_mutex); 72 smb_kdoor_clnt_ncall = 0; 73 74 if (smb_kdoor_clnt_hd == NULL) { 75 smb_kdoor_clnt_id = door_id; 76 smb_kdoor_clnt_hd = door_ki_lookup(door_id); 77 } 78 79 rc = (smb_kdoor_clnt_hd == NULL) ? -1 : 0; 80 mutex_exit(&smb_kdoor_clnt_mutex); 81 return (rc); 82 } 83 84 /* 85 * Close the door. 86 */ 87 void 88 smb_kdoor_clnt_close(void) 89 { 90 mutex_enter(&smb_kdoor_clnt_mutex); 91 92 if (smb_kdoor_clnt_hd != NULL) { 93 while (smb_kdoor_clnt_ncall > 0) 94 cv_wait(&smb_kdoor_clnt_cv, &smb_kdoor_clnt_mutex); 95 96 door_ki_rele(smb_kdoor_clnt_hd); 97 smb_kdoor_clnt_hd = NULL; 98 } 99 100 mutex_exit(&smb_kdoor_clnt_mutex); 101 } 102 103 /* 104 * smb_kdoor_clnt_upcall 105 * 106 * Wrapper to handle door call reference counting. 107 */ 108 char * 109 smb_kdoor_clnt_upcall(char *argp, size_t arg_size, door_desc_t *dp, 110 uint_t desc_num, size_t *rbufsize) 111 { 112 char *rbufp; 113 114 if (argp == NULL) 115 return (NULL); 116 117 mutex_enter(&smb_kdoor_clnt_mutex); 118 119 if (smb_kdoor_clnt_hd == NULL) { 120 mutex_exit(&smb_kdoor_clnt_mutex); 121 122 if (smb_kdoor_clnt_open(smb_kdoor_clnt_id) != 0) 123 return (NULL); 124 125 mutex_enter(&smb_kdoor_clnt_mutex); 126 } 127 128 ++smb_kdoor_clnt_ncall; 129 mutex_exit(&smb_kdoor_clnt_mutex); 130 131 rbufp = smb_kdoor_upcall(argp, arg_size, dp, desc_num, rbufsize); 132 133 mutex_enter(&smb_kdoor_clnt_mutex); 134 --smb_kdoor_clnt_ncall; 135 cv_signal(&smb_kdoor_clnt_cv); 136 mutex_exit(&smb_kdoor_clnt_mutex); 137 return (rbufp); 138 } 139 140 /* 141 * On success, the result buffer is returned, with rbufsize set to the 142 * size of the result buffer. Otherwise, a NULL pointer is returned. 143 */ 144 static char * 145 smb_kdoor_upcall(char *argp, size_t arg_size, door_desc_t *dp, 146 uint_t desc_num, size_t *rbufsize) 147 { 148 door_arg_t door_arg; 149 int i; 150 int rc; 151 152 door_arg.data_ptr = argp; 153 door_arg.data_size = arg_size; 154 door_arg.desc_ptr = dp; 155 door_arg.desc_num = desc_num; 156 door_arg.rbuf = argp; 157 door_arg.rsize = arg_size; 158 159 for (i = 0; i < SMB_KDOOR_RETRIES; ++i) { 160 if ((rc = door_ki_upcall_limited(smb_kdoor_clnt_hd, &door_arg, 161 NULL, SIZE_MAX, 0)) == 0) 162 break; 163 164 if (rc != EAGAIN && rc != EINTR) 165 return (NULL); 166 } 167 168 if (rc != 0) 169 return (NULL); 170 171 rc = smb_dr_get_res_stat(door_arg.data_ptr, door_arg.rsize); 172 if (rc != SMB_DR_OP_SUCCESS) 173 return (NULL); 174 175 *rbufsize = door_arg.rsize; 176 return (door_arg.data_ptr); 177 } 178 179 /* 180 * smb_kdoor_clnt_free 181 * 182 * This function should be invoked to free both the argument/result door buffer 183 * regardless of the status of the up-call. 184 * 185 * The doorfs allocates a new buffer if the result buffer passed by the client 186 * is too small. This function will deallocate that buffer as well. 187 */ 188 void 189 smb_kdoor_clnt_free(char *argp, size_t arg_size, char *rbufp, size_t rbuf_size) 190 { 191 if (argp) 192 kmem_free(argp, arg_size); 193 194 if (rbufp && rbufp != argp) 195 kmem_free(rbufp, rbuf_size); 196 } 197