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/CDDL.txt 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/CDDL.txt. 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 // 23 // Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 // Use is subject to license terms. 25 // 26 27 #pragma ident "@(#)cl_flock.cc 1.8 08/05/20 SMI" 28 29 #include <sys/flock_impl.h> 30 #include <sys/flock.h> 31 #include <sys/disp.h> 32 33 #include <sys/list_def.h> 34 35 #include <orb/infrastructure/clusterproc.h> 36 37 #include "../version.h" 38 #include PXFS_IDL(pxfs) 39 #include <pxfs/lib/pxfs_debug.h> 40 #include <pxfs/server/cl_flock.h> 41 #include <pxfs/server/fobj_impl.h> 42 #include <pxfs/server/frange_lock.h> 43 44 //lint -e1512 45 // 46 // Warning(1512) destructor for base class is not virtual -- In a 47 // final pass through all the classes, we have found a class that is 48 // the base class of a derivation and has a destructor but the 49 // destructor is not virtual. It is conventional for inherited classes 50 // to have virtual destructors so that is it safe to 'delete' a 51 // pointer to a base class. 52 // 53 54 static void cl_flk_handle_waking_locks(void *); 55 static void cl_flk_state_notify_lock_woken(lock_descriptor_t *); 56 static fobj_ii *get_fobj_ii(lock_descriptor_t *); 57 58 typedef SList<lock_descriptor_t> locks_list; 59 60 // 61 // This list stores the previously blocked lock requests that have now been 62 // granted and await the client being notified. After this notification is 63 // delivered, the lock is pushed to the processed_list 64 // 65 static locks_list granted_list; 66 67 // Protects the 'granted_list' 68 static os::mutex_t granted_list_lock; 69 70 // 71 // The locking order is: graph_lock, granted_list_lock, processed_list_lock 72 // 73 74 // 75 // The thread that delivers client notifications sleeps on this cv. Whenever 76 // a lock is added to the 'granted_list', this cv is prodded 77 // 78 static os::condvar_t sleep_cond; 79 80 // 81 // This procedure is run by a separate thread, and processes all the 82 // locks that come in the 'granted_list'. Processing a lock consists of 83 // notifying its client that it has been woken up, and placing it on the 84 // 'processed_list'. When the 'granted_list' is empty, the thread goes 85 // to sleep, and is signalled awake when a new entry is placed in the 86 // 'granted_list'. 87 // 88 void 89 cl_flk_handle_waking_locks(void *) 90 { 91 lock_descriptor_t *lock; 92 PXFS_VER::pxfs_llm_callback_ptr lock_cb; 93 PXFS_VER::pxfs_llm_callback_ptr lock_cb_dup; 94 Environment e; 95 lock_context_t *lock_contextp; 96 frange_lock *fr_lockp; 97 98 granted_list_lock.lock(); 99 for (;;) { 100 101 sleep_cond.wait(&granted_list_lock); 102 103 #ifdef _FAULT_INJECTION 104 if (fault_triggered(FAULTNUM_PXFS_GRANTEDLOCK_S_O, NULL, 105 NULL)) { 106 granted_list_lock.unlock(); 107 FAULTPT_PXFS(FAULTNUM_PXFS_GRANTEDLOCK_S_O, 108 FaultFunctions::generic); 109 granted_list_lock.lock(); 110 } 111 #endif 112 113 while ((lock = granted_list.reapfirst()) != NULL) { 114 115 // get the callback object 116 lock_contextp = CL_FLK_GET_CONTEXT(lock); 117 lock_cb = lock_contextp->cb_obj_v1; 118 ASSERT(!CORBA::is_nil(lock_cb)); 119 120 fr_lockp = ((fobj_ii *)(lock_contextp->fobj_abstract_p)) 121 ->get_frange_lock(); 122 ASSERT(fr_lockp != NULL); 123 124 // 125 // Grab the 'processed_list_lock' before dropping the 126 // 'granted_list_lock', to handle the race between this 127 // procedure and cl_flk_cancel_blocked_request(). 128 // 129 fr_lockp->lock_processed_list(); 130 granted_list_lock.unlock(); 131 132 fr_lockp->add_to_processed_list(lock); 133 134 // Duplicate the callback object, and then drop the 135 // `processed_list_lock' 136 lock_cb_dup = 137 PXFS_VER::pxfs_llm_callback::_duplicate(lock_cb); 138 139 fr_lockp->unlock_processed_list(); 140 141 // invoke the callback function. 142 lock_cb_dup->wakeup(e); 143 144 // Release the duplicated reference. 145 CORBA::release(lock_cb_dup); 146 147 // We ignore exceptions since there is nothing to do 148 // if the client crashed 149 e.clear(); 150 151 granted_list_lock.lock(); 152 } 153 } 154 } 155 156 // 157 // Remove from the granted list and the LLM all locks associated with 158 // 'fobj_iip'. 159 // 160 void 161 remove_granted_locks(fobj_ii *fobj_iip) 162 { 163 lock_descriptor_t *lock; 164 165 frange_lock *fr_lockp = fobj_iip->get_frange_lock(); 166 ASSERT(fr_lockp != NULL); 167 168 granted_list_lock.lock(); 169 granted_list.atfirst(); 170 while ((lock = granted_list.get_current()) != NULL) { 171 if (get_fobj_ii(lock) != fobj_iip) { 172 granted_list.advance(); 173 continue; 174 } 175 (void) granted_list.erase(lock); 176 granted_list_lock.unlock(); 177 PXFS_DBPRINTF( 178 PXFS_TRACE_FLK, 179 PXFS_GREEN, 180 ("(%p) Removing granted lock (%p)\n", 181 fobj_iip, lock)); 182 (void) fr_lockp->process_pxfs_lock(lock, true); 183 granted_list_lock.lock(); 184 granted_list.atfirst(); 185 } 186 granted_list_lock.unlock(); 187 } 188 189 // 190 // This function adds the lock provided to the 'granted_list' of locks. 191 // This routine is called (indirectly) by the local lock manager when a 192 // blocked PXFS thread is granted. 193 // 194 void 195 cl_flk_state_notify_lock_woken(lock_descriptor_t *lock) 196 { 197 granted_list_lock.lock(); 198 199 // Add the lock to the list 200 granted_list.prepend(lock); 201 202 // Signal awake the thread that makes the callbacks 203 sleep_cond.signal(); 204 205 granted_list_lock.unlock(); 206 } 207 208 // 209 // This routine is invoked when a sleeping PXFS lock is going to be 210 // deleted. It checks to see if the lock is already on one of the 211 // sleeping PXFS lists, and if it is, it wakes it up. 212 // 213 void 214 cl_flk_state_notify_woken_lock_deleted(lock_descriptor_t *lock) 215 { 216 PXFS_VER::pxfs_llm_callback_ptr lock_cb; 217 bool found; 218 lock_context_t *lock_contextp; 219 220 frange_lock *fr_lockp = get_fobj_ii(lock)-> 221 get_frange_lock(); 222 ASSERT(fr_lockp != NULL); 223 224 // 225 // The lock has beed woken up previously - this means that it is on one 226 // of the PXFS lists unless this call is made as a result of 227 // fobj_ii::remove_all_locks(). 228 // The order of searching the lists has to be 'granted_list', 229 // 'processed_list', because of the implementation of 230 // cl_flk_handle_waking_locks() 231 // 232 granted_list_lock.lock(); 233 found = granted_list.erase(lock); 234 granted_list_lock.unlock(); 235 if (!found) { 236 (void) fr_lockp->remove_from_processed_list(lock); 237 } 238 239 // Relase the CORBA reference 240 lock_contextp = CL_FLK_GET_CONTEXT(lock); 241 lock_cb = lock_contextp->cb_obj_v1; 242 243 // A woken-up lock must have a valid callback object associated with it 244 ASSERT(!CORBA::is_nil(lock_cb)); 245 246 fr_lockp->remove_cb_object(lock_cb); 247 248 // Drop the reference to the callback object. 249 CORBA::release(lock_cb); 250 } 251 252 // 253 // Helper function that returns the fobj that the given PXFS lock belongs to. 254 // 255 fobj_ii * 256 get_fobj_ii(lock_descriptor_t *lock) 257 { 258 lock_context_t *fobj_lock_infop = CL_FLK_GET_CONTEXT(lock); 259 260 return ((fobj_ii *)fobj_lock_infop->fobj_abstract_p); 261 } 262 263 // 264 // This function is called by the local lock manager whenever the 265 // state of a PXFS lock changes. It dispatches the notifications to the 266 // appropriate function. 267 // 268 void 269 flk_state_transition_notify(lock_descriptor_t *lock, int old_state, 270 int new_state) 271 { 272 ASSERT(IS_PXFS(lock)); 273 274 frange_lock *fr_lockp; 275 276 PXFS_DBPRINTF( 277 PXFS_TRACE_FLK, 278 PXFS_GREEN, 279 ("(%p) state transition (%d - %d): (%d) (%d, %d)\n", 280 CL_FLK_GET_CONTEXT(lock)->fobj_abstract_p, old_state, new_state, 281 (int)lock->l_type, (int)lock->l_start, (int)lock->l_end)); 282 283 switch (new_state) { 284 case FLK_ACTIVE_STATE: 285 fr_lockp = get_fobj_ii(lock)->get_frange_lock(); 286 ASSERT(fr_lockp != NULL); 287 288 // Lock has become active - notify the fobj 289 fr_lockp->lock_active(lock); 290 return; 291 292 case FLK_GRANTED_STATE: 293 case FLK_INTERRUPTED_STATE: 294 case FLK_CANCELLED_STATE: 295 // Lock has been woken up. 296 if ((old_state == FLK_GRANTED_STATE) || 297 (old_state == FLK_CANCELLED_STATE) || 298 (old_state == FLK_INTERRUPTED_STATE)) { 299 // Lock has been woken up before - it is already in the 300 // PXFS lists. 301 return; 302 } 303 cl_flk_state_notify_lock_woken(lock); 304 return; 305 306 case FLK_DEAD_STATE: 307 308 if ((old_state == FLK_GRANTED_STATE) || 309 (old_state == FLK_CANCELLED_STATE) || 310 (old_state == FLK_INTERRUPTED_STATE)) { 311 // A woken-up lock is being deleted 312 cl_flk_state_notify_woken_lock_deleted(lock); 313 314 } else if (old_state == FLK_ACTIVE_STATE) { 315 fr_lockp = get_fobj_ii(lock)->get_frange_lock(); 316 ASSERT(fr_lockp != NULL); 317 318 // Active lock has been deleted 319 fr_lockp->lock_deleted(lock); 320 } 321 322 // The other cases of a lock transitioning to the 'DEAD' state 323 // are not of interest to PXFS 324 325 return; 326 327 default: 328 break; 329 } 330 } 331 332 // 333 // Creates the thread that notifies sleeping clients. 334 // 335 int 336 cl_flk_init() 337 { 338 // 339 // Create a lwp in the scheduling class SYS. 340 // 341 if ((clnewlwp(cl_flk_handle_waking_locks, NULL, MINCLSYSPRI, 342 NULL, NULL)) != 0) { 343 cmn_err(CE_WARN, "cl_flk_init: cannot create thread"); 344 return (-1); 345 } 346 return (0); 347 } 348