1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
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.
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.
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
26 #include "nfs41_ops.h"
27 #include "nfs41_callback.h"
29 #include "daemon_debug.h"
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 */
37 /* predicate for nfs41_slot_table.cond */
38 static int slot_table_avail(
39 IN
const nfs41_slot_table
*table
)
41 return table
->num_used
< table
->max_slots
;
44 /* session slot mechanism */
45 static void init_slot_table(nfs41_slot_table
*table
)
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;
54 table
->highest_used
= table
->num_used
= 0;
55 table
->target_delay
= 0;
57 /* wake any threads waiting on a slot */
58 if (slot_table_avail(table
))
59 WakeAllConditionVariable(&table
->cond
);
60 LeaveCriticalSection(&table
->lock
);
63 static void resize_slot_table(
64 IN nfs41_slot_table
*table
,
65 IN
uint32_t target_highest_slotid
)
67 if (target_highest_slotid
>= NFS41_MAX_NUM_SLOTS
)
68 target_highest_slotid
= NFS41_MAX_NUM_SLOTS
- 1;
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;
75 if (slot_table_avail(table
))
76 WakeAllConditionVariable(&table
->cond
);
80 void nfs41_session_bump_seq(
81 IN nfs41_session
*session
,
83 IN
uint32_t target_highest_slotid
)
85 nfs41_slot_table
*table
= &session
->table
;
87 AcquireSRWLockShared(&session
->client
->session_lock
);
88 EnterCriticalSection(&table
->lock
);
90 if (slotid
< NFS41_MAX_NUM_SLOTS
)
91 table
->seq_nums
[slotid
]++;
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
);
98 LeaveCriticalSection(&table
->lock
);
99 ReleaseSRWLockShared(&session
->client
->session_lock
);
102 void nfs41_session_free_slot(
103 IN nfs41_session
*session
,
106 nfs41_slot_table
*table
= &session
->table
;
108 AcquireSRWLockShared(&session
->client
->session_lock
);
109 EnterCriticalSection(&table
->lock
);
111 /* flag the slot as unused */
112 if (slotid
< NFS41_MAX_NUM_SLOTS
&& table
->used_slots
[slotid
]) {
113 table
->used_slots
[slotid
] = 0;
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
--;
121 dprintf(3, "freeing slot#=%d used=%d highest=%d\n",
122 slotid
, table
->num_used
, table
->highest_used
);
124 /* wake any threads waiting on a slot */
125 if (slot_table_avail(table
))
126 WakeAllConditionVariable(&table
->cond
);
128 LeaveCriticalSection(&table
->lock
);
129 ReleaseSRWLockShared(&session
->client
->session_lock
);
132 void nfs41_session_get_slot(
133 IN nfs41_session
*session
,
136 OUT
uint32_t *highest
)
138 nfs41_slot_table
*table
= &session
->table
;
141 AcquireSRWLockShared(&session
->client
->session_lock
);
142 EnterCriticalSection(&table
->lock
);
144 /* wait for an available slot */
145 while (!slot_table_avail(table
))
146 SleepConditionVariableCS(&table
->cond
, &table
->lock
, INFINITE
);
148 for (i
= 0; i
< table
->max_slots
; i
++) {
149 if (table
->used_slots
[i
])
152 table
->used_slots
[i
] = 1;
154 if (i
> table
->highest_used
)
155 table
->highest_used
= i
;
158 *seqid
= table
->seq_nums
[i
];
159 *highest
= table
->highest_used
;
162 LeaveCriticalSection(&table
->lock
);
163 ReleaseSRWLockShared(&session
->client
->session_lock
);
165 dprintf(2, "session %p: using slot#=%d with seq#=%d highest=%d\n",
166 session
, *slot
, *seqid
, *highest
);
169 int nfs41_session_recall_slot(
170 IN nfs41_session
*session
,
171 IN OUT
uint32_t target_highest_slotid
)
173 nfs41_slot_table
*table
= &session
->table
;
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
);
185 int nfs41_session_bad_slot(
186 IN nfs41_session
*session
,
187 IN OUT nfs41_sequence_args
*args
)
189 nfs41_slot_table
*table
= &session
->table
;
190 int status
= NFS4ERR_BADSLOT
;
192 if (args
->sa_slotid
== 0) {
193 eprintf("server bug detected: NFS4ERR_BADSLOT for slotid=0\n");
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
;
203 LeaveCriticalSection(&table
->lock
);
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
);
214 void nfs41_session_sequence(
215 nfs41_sequence_args
*args
,
216 nfs41_session
*session
,
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
;
226 /* session renewal */
227 static unsigned int WINAPI
renew_session(void *args
)
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;
234 dprintf(1, "Creating renew_session thread: %p\n", session
->renew_thread
);
236 dprintf(1, "Going to sleep for %dmsecs\n", sleep_time
);
238 status
= nfs41_send_sequence(session
);
240 dprintf(1, "renewal thread: nfs41_send_sequence failed %d\n", status
);
245 /* session creation */
246 static int session_alloc(
247 IN nfs41_client
*client
,
248 OUT nfs41_session
**session_out
)
250 nfs41_session
*session
;
251 int status
= NO_ERROR
;
253 session
= calloc(1, sizeof(nfs41_session
));
254 if (session
== NULL
) {
255 status
= GetLastError();
258 session
->client
= client
;
259 session
->renew_thread
= INVALID_HANDLE_VALUE
;
260 session
->isValidState
= FALSE
;
262 InitializeCriticalSection(&session
->table
.lock
);
263 InitializeConditionVariable(&session
->table
.cond
);
265 init_slot_table(&session
->table
);
267 //initialize session lock
268 InitializeSRWLock(&client
->session_lock
);
270 /* initialize the back channel */
271 nfs41_callback_session_init(session
);
273 *session_out
= session
;
278 int nfs41_session_create(
279 IN nfs41_client
*client
,
280 IN nfs41_session
**session_out
)
282 nfs41_session
*session
;
285 status
= session_alloc(client
, &session
);
287 eprintf("session_alloc() failed with %d\n", status
);
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
);
297 status
= nfs41_create_session(client
, session
, TRUE
);
299 eprintf("nfs41_create_session failed %d\n", status
);
300 status
= ERROR_BAD_NET_RESP
;
304 AcquireSRWLockExclusive(&session
->client
->session_lock
);
305 client
->session
= session
;
306 session
->isValidState
= TRUE
;
307 ReleaseSRWLockExclusive(&session
->client
->session_lock
);
308 *session_out
= session
;
313 nfs41_session_free(session
);
317 /* session renewal */
318 int nfs41_session_renew(
319 IN nfs41_session
*session
)
323 AcquireSRWLockExclusive(&session
->client
->session_lock
);
324 session
->cb_session
.cb_seqnum
= 0;
325 init_slot_table(&session
->table
);
327 status
= nfs41_create_session(session
->client
, session
, FALSE
);
328 ReleaseSRWLockExclusive(&session
->client
->session_lock
);
332 int nfs41_session_set_lease(
333 IN nfs41_session
*session
,
334 IN
uint32_t lease_time
)
336 int status
= NO_ERROR
;
339 if (valid_handle(session
->renew_thread
)) {
340 eprintf("nfs41_session_set_lease(): session "
341 "renewal thread already started!\n");
345 if (lease_time
== 0) {
346 eprintf("nfs41_session_set_lease(): invalid lease_time=0\n");
347 status
= ERROR_INVALID_PARAMETER
;
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
);
363 void nfs41_session_free(
364 IN nfs41_session
*session
)
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
);
374 if (session
->isValidState
) {
375 session
->client
->rpc
->is_valid_session
= FALSE
;
376 nfs41_destroy_session(session
);
378 DeleteCriticalSection(&session
->table
.lock
);
379 ReleaseSRWLockExclusive(&session
->client
->session_lock
);