xref: /reactos/base/services/nfsd/nfs41_session.c (revision 40462c92)
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