1 /* NFSv4.1 client for Windows 2 * Copyright � 2012 The Regents of the University of Michigan 3 * 4 * Olga Kornievskaia <aglo@umich.edu> 5 * Casey Bodley <cbodley@umich.edu> 6 * 7 * This library is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU Lesser General Public License as published by 9 * the Free Software Foundation; either version 2.1 of the License, or (at 10 * your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, but 13 * without any warranty; without even the implied warranty of merchantability 14 * or fitness for a particular purpose. See the GNU Lesser General Public 15 * License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with this library; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 */ 21 22 #include <windows.h> 23 #include <process.h> 24 #include <stdio.h> 25 26 #include "nfs41_ops.h" 27 #include "nfs41_callback.h" 28 #include "util.h" 29 #include "daemon_debug.h" 30 31 32 /* after a CB_RECALL_SLOT or NFS4ERR_BADSLOT, wait a short time for the 33 * SEQUENCE.target_highest_slotid to catch up before updating max_slots again */ 34 #define MAX_SLOTS_DELAY 2000 /* in milliseconds */ 35 36 37 /* predicate for nfs41_slot_table.cond */ 38 static int slot_table_avail( 39 IN const nfs41_slot_table *table) 40 { 41 return table->num_used < table->max_slots; 42 } 43 44 /* session slot mechanism */ 45 static void init_slot_table(nfs41_slot_table *table) 46 { 47 uint32_t i; 48 EnterCriticalSection(&table->lock); 49 table->max_slots = NFS41_MAX_NUM_SLOTS; 50 for (i = 0; i < NFS41_MAX_NUM_SLOTS; i++) { 51 table->seq_nums[i] = 1; 52 table->used_slots[i] = 0; 53 } 54 table->highest_used = table->num_used = 0; 55 table->target_delay = 0; 56 57 /* wake any threads waiting on a slot */ 58 if (slot_table_avail(table)) 59 WakeAllConditionVariable(&table->cond); 60 LeaveCriticalSection(&table->lock); 61 } 62 63 static void resize_slot_table( 64 IN nfs41_slot_table *table, 65 IN uint32_t target_highest_slotid) 66 { 67 if (target_highest_slotid >= NFS41_MAX_NUM_SLOTS) 68 target_highest_slotid = NFS41_MAX_NUM_SLOTS - 1; 69 70 if (table->max_slots != target_highest_slotid + 1) { 71 dprintf(2, "updated max_slots %u to %u\n", 72 table->max_slots, target_highest_slotid + 1); 73 table->max_slots = target_highest_slotid + 1; 74 75 if (slot_table_avail(table)) 76 WakeAllConditionVariable(&table->cond); 77 } 78 } 79 80 void nfs41_session_bump_seq( 81 IN nfs41_session *session, 82 IN uint32_t slotid, 83 IN uint32_t target_highest_slotid) 84 { 85 nfs41_slot_table *table = &session->table; 86 87 AcquireSRWLockShared(&session->client->session_lock); 88 EnterCriticalSection(&table->lock); 89 90 if (slotid < NFS41_MAX_NUM_SLOTS) 91 table->seq_nums[slotid]++; 92 93 /* adjust max_slots in response to changes in target_highest_slotid, 94 * but not immediately after a CB_RECALL_SLOT or NFS4ERR_BADSLOT error */ 95 if (table->target_delay <= GetTickCount64()) 96 resize_slot_table(table, target_highest_slotid); 97 98 LeaveCriticalSection(&table->lock); 99 ReleaseSRWLockShared(&session->client->session_lock); 100 } 101 102 void nfs41_session_free_slot( 103 IN nfs41_session *session, 104 IN uint32_t slotid) 105 { 106 nfs41_slot_table *table = &session->table; 107 108 AcquireSRWLockShared(&session->client->session_lock); 109 EnterCriticalSection(&table->lock); 110 111 /* flag the slot as unused */ 112 if (slotid < NFS41_MAX_NUM_SLOTS && table->used_slots[slotid]) { 113 table->used_slots[slotid] = 0; 114 table->num_used--; 115 } 116 /* update highest_used if necessary */ 117 if (slotid == table->highest_used) { 118 while (table->highest_used && !table->used_slots[table->highest_used]) 119 table->highest_used--; 120 } 121 dprintf(3, "freeing slot#=%d used=%d highest=%d\n", 122 slotid, table->num_used, table->highest_used); 123 124 /* wake any threads waiting on a slot */ 125 if (slot_table_avail(table)) 126 WakeAllConditionVariable(&table->cond); 127 128 LeaveCriticalSection(&table->lock); 129 ReleaseSRWLockShared(&session->client->session_lock); 130 } 131 132 void nfs41_session_get_slot( 133 IN nfs41_session *session, 134 OUT uint32_t *slot, 135 OUT uint32_t *seqid, 136 OUT uint32_t *highest) 137 { 138 nfs41_slot_table *table = &session->table; 139 uint32_t i; 140 141 AcquireSRWLockShared(&session->client->session_lock); 142 EnterCriticalSection(&table->lock); 143 144 /* wait for an available slot */ 145 while (!slot_table_avail(table)) 146 SleepConditionVariableCS(&table->cond, &table->lock, INFINITE); 147 148 for (i = 0; i < table->max_slots; i++) { 149 if (table->used_slots[i]) 150 continue; 151 152 table->used_slots[i] = 1; 153 table->num_used++; 154 if (i > table->highest_used) 155 table->highest_used = i; 156 157 *slot = i; 158 *seqid = table->seq_nums[i]; 159 *highest = table->highest_used; 160 break; 161 } 162 LeaveCriticalSection(&table->lock); 163 ReleaseSRWLockShared(&session->client->session_lock); 164 165 dprintf(2, "session %p: using slot#=%d with seq#=%d highest=%d\n", 166 session, *slot, *seqid, *highest); 167 } 168 169 int nfs41_session_recall_slot( 170 IN nfs41_session *session, 171 IN OUT uint32_t target_highest_slotid) 172 { 173 nfs41_slot_table *table = &session->table; 174 175 AcquireSRWLockShared(&session->client->session_lock); 176 EnterCriticalSection(&table->lock); 177 resize_slot_table(table, target_highest_slotid); 178 table->target_delay = GetTickCount64() + MAX_SLOTS_DELAY; 179 LeaveCriticalSection(&table->lock); 180 ReleaseSRWLockShared(&session->client->session_lock); 181 182 return NFS4_OK; 183 } 184 185 int nfs41_session_bad_slot( 186 IN nfs41_session *session, 187 IN OUT nfs41_sequence_args *args) 188 { 189 nfs41_slot_table *table = &session->table; 190 int status = NFS4ERR_BADSLOT; 191 192 if (args->sa_slotid == 0) { 193 eprintf("server bug detected: NFS4ERR_BADSLOT for slotid=0\n"); 194 goto out; 195 } 196 197 /* avoid using any slots >= bad_slotid */ 198 EnterCriticalSection(&table->lock); 199 if (table->max_slots > args->sa_slotid) { 200 resize_slot_table(table, args->sa_slotid); 201 table->target_delay = GetTickCount64() + MAX_SLOTS_DELAY; 202 } 203 LeaveCriticalSection(&table->lock); 204 205 /* get a new slot */ 206 nfs41_session_free_slot(session, args->sa_slotid); 207 nfs41_session_get_slot(session, &args->sa_slotid, 208 &args->sa_sequenceid, &args->sa_highest_slotid); 209 status = NFS4_OK; 210 out: 211 return status; 212 } 213 214 void nfs41_session_sequence( 215 nfs41_sequence_args *args, 216 nfs41_session *session, 217 bool_t cachethis) 218 { 219 nfs41_session_get_slot(session, &args->sa_slotid, 220 &args->sa_sequenceid, &args->sa_highest_slotid); 221 args->sa_sessionid = session->session_id; 222 args->sa_cachethis = cachethis; 223 } 224 225 226 /* session renewal */ 227 static unsigned int WINAPI renew_session(void *args) 228 { 229 int status = NO_ERROR; 230 nfs41_session *session = (nfs41_session *)args; 231 /* sleep for 2/3 of lease_time */ 232 const uint32_t sleep_time = (2 * session->lease_time*1000)/3; 233 234 dprintf(1, "Creating renew_session thread: %p\n", session->renew_thread); 235 while(1) { 236 dprintf(1, "Going to sleep for %dmsecs\n", sleep_time); 237 Sleep(sleep_time); 238 status = nfs41_send_sequence(session); 239 if (status) 240 dprintf(1, "renewal thread: nfs41_send_sequence failed %d\n", status); 241 } 242 return status; 243 } 244 245 /* session creation */ 246 static int session_alloc( 247 IN nfs41_client *client, 248 OUT nfs41_session **session_out) 249 { 250 nfs41_session *session; 251 int status = NO_ERROR; 252 253 session = calloc(1, sizeof(nfs41_session)); 254 if (session == NULL) { 255 status = GetLastError(); 256 goto out; 257 } 258 session->client = client; 259 session->renew_thread = INVALID_HANDLE_VALUE; 260 session->isValidState = FALSE; 261 262 InitializeCriticalSection(&session->table.lock); 263 InitializeConditionVariable(&session->table.cond); 264 265 init_slot_table(&session->table); 266 267 //initialize session lock 268 InitializeSRWLock(&client->session_lock); 269 270 /* initialize the back channel */ 271 nfs41_callback_session_init(session); 272 273 *session_out = session; 274 out: 275 return status; 276 } 277 278 int nfs41_session_create( 279 IN nfs41_client *client, 280 IN nfs41_session **session_out) 281 { 282 nfs41_session *session; 283 int status; 284 285 status = session_alloc(client, &session); 286 if (status) { 287 eprintf("session_alloc() failed with %d\n", status); 288 goto out; 289 } 290 291 AcquireSRWLockShared(&client->exid_lock); 292 if (client->rpc->needcb) 293 session->flags |= CREATE_SESSION4_FLAG_CONN_BACK_CHAN; 294 session->flags |= CREATE_SESSION4_FLAG_PERSIST; 295 ReleaseSRWLockShared(&client->exid_lock); 296 297 status = nfs41_create_session(client, session, TRUE); 298 if (status) { 299 eprintf("nfs41_create_session failed %d\n", status); 300 status = ERROR_BAD_NET_RESP; 301 goto out_err; 302 } 303 304 AcquireSRWLockExclusive(&session->client->session_lock); 305 client->session = session; 306 session->isValidState = TRUE; 307 ReleaseSRWLockExclusive(&session->client->session_lock); 308 *session_out = session; 309 out: 310 return status; 311 312 out_err: 313 nfs41_session_free(session); 314 goto out; 315 } 316 317 /* session renewal */ 318 int nfs41_session_renew( 319 IN nfs41_session *session) 320 { 321 int status; 322 323 AcquireSRWLockExclusive(&session->client->session_lock); 324 session->cb_session.cb_seqnum = 0; 325 init_slot_table(&session->table); 326 327 status = nfs41_create_session(session->client, session, FALSE); 328 ReleaseSRWLockExclusive(&session->client->session_lock); 329 return status; 330 } 331 332 int nfs41_session_set_lease( 333 IN nfs41_session *session, 334 IN uint32_t lease_time) 335 { 336 int status = NO_ERROR; 337 uint32_t thread_id; 338 339 if (valid_handle(session->renew_thread)) { 340 eprintf("nfs41_session_set_lease(): session " 341 "renewal thread already started!\n"); 342 goto out; 343 } 344 345 if (lease_time == 0) { 346 eprintf("nfs41_session_set_lease(): invalid lease_time=0\n"); 347 status = ERROR_INVALID_PARAMETER; 348 goto out; 349 } 350 351 session->lease_time = lease_time; 352 session->renew_thread = (HANDLE)_beginthreadex(NULL, 353 0, renew_session, session, 0, &thread_id); 354 if (!valid_handle(session->renew_thread)) { 355 status = GetLastError(); 356 eprintf("_beginthreadex failed %d\n", status); 357 goto out; 358 } 359 out: 360 return status; 361 } 362 363 void nfs41_session_free( 364 IN nfs41_session *session) 365 { 366 AcquireSRWLockExclusive(&session->client->session_lock); 367 if (valid_handle(session->renew_thread)) { 368 dprintf(1, "nfs41_session_free: terminating session renewal thread\n"); 369 if (!TerminateThread(session->renew_thread, NO_ERROR)) 370 eprintf("failed to terminate renewal thread %p\n", 371 session->renew_thread); 372 } 373 374 if (session->isValidState) { 375 session->client->rpc->is_valid_session = FALSE; 376 nfs41_destroy_session(session); 377 } 378 DeleteCriticalSection(&session->table.lock); 379 ReleaseSRWLockExclusive(&session->client->session_lock); 380 free(session); 381 } 382