xref: /reactos/base/services/nfsd/pnfs_device.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 <strsafe.h>
24 #include <stdio.h>
25 
26 #include "nfs41_ops.h"
27 #include "nfs41_callback.h"
28 #include "daemon_debug.h"
29 
30 
31 #define FDLVL 2 /* dprintf level for file device logging */
32 
33 
34 /* pnfs_file_device_list */
35 struct pnfs_file_device_list {
36     struct list_entry       head;
37     CRITICAL_SECTION        lock;
38 };
39 
40 #define device_entry(pos) list_container(pos, pnfs_file_device, entry)
41 
42 
43 static enum pnfs_status file_device_create(
44     IN const unsigned char *deviceid,
45     IN struct pnfs_file_device_list *devices,
46     OUT pnfs_file_device **device_out)
47 {
48     enum pnfs_status status = PNFS_SUCCESS;
49     pnfs_file_device *device;
50 
51     device = calloc(1, sizeof(pnfs_file_device));
52     if (device == NULL) {
53         status = PNFSERR_RESOURCES;
54         goto out;
55     }
56 
57     memcpy(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE);
58     device->devices = devices;
59     InitializeCriticalSection(&device->device.lock);
60     *device_out = device;
61 out:
62     return status;
63 }
64 
65 static void file_device_free(
66     IN pnfs_file_device *device)
67 {
68     free(device->servers.arr);
69     free(device->stripes.arr);
70     DeleteCriticalSection(&device->device.lock);
71     free(device);
72 }
73 
74 static int deviceid_compare(
75     const struct list_entry *entry,
76     const void *deviceid)
77 {
78     const pnfs_file_device *device = device_entry(entry);
79     return memcmp(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE);
80 }
81 
82 static enum pnfs_status file_device_find_or_create(
83     IN const unsigned char *deviceid,
84     IN struct pnfs_file_device_list *devices,
85     OUT pnfs_file_device **device_out)
86 {
87     struct list_entry *entry;
88     enum pnfs_status status;
89 
90     dprintf(FDLVL, "--> pnfs_file_device_find_or_create()\n");
91 
92     EnterCriticalSection(&devices->lock);
93 
94     /* search for an existing device */
95     entry = list_search(&devices->head, deviceid, deviceid_compare);
96     if (entry == NULL) {
97         /* create a new device */
98         pnfs_file_device *device;
99         status = file_device_create(deviceid, devices, &device);
100         if (status == PNFS_SUCCESS) {
101             /* add it to the list */
102             list_add_tail(&devices->head, &device->entry);
103             *device_out = device;
104 
105             dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
106                 "returning new device %p\n", device);
107         } else {
108             dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
109                 "returning %s\n", pnfs_error_string(status));
110         }
111     } else {
112         *device_out = device_entry(entry);
113         status = PNFS_SUCCESS;
114 
115         dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
116             "returning existing device %p\n", *device_out);
117     }
118 
119     LeaveCriticalSection(&devices->lock);
120     return status;
121 }
122 
123 
124 enum pnfs_status pnfs_file_device_list_create(
125     OUT struct pnfs_file_device_list **devices_out)
126 {
127     enum pnfs_status status = PNFS_SUCCESS;
128     struct pnfs_file_device_list *devices;
129 
130     devices = calloc(1, sizeof(struct pnfs_file_device_list));
131     if (devices == NULL) {
132         status = PNFSERR_RESOURCES;
133         goto out;
134     }
135 
136     list_init(&devices->head);
137     InitializeCriticalSection(&devices->lock);
138 
139     *devices_out = devices;
140 out:
141     return status;
142 }
143 
144 void pnfs_file_device_list_free(
145     IN struct pnfs_file_device_list *devices)
146 {
147     struct list_entry *entry, *tmp;
148 
149     EnterCriticalSection(&devices->lock);
150 
151     list_for_each_tmp(entry, tmp, &devices->head)
152         file_device_free(device_entry(entry));
153 
154     LeaveCriticalSection(&devices->lock);
155     DeleteCriticalSection(&devices->lock);
156     free(devices);
157 }
158 
159 void pnfs_file_device_list_invalidate(
160     IN struct pnfs_file_device_list *devices)
161 {
162     struct list_entry *entry, *tmp;
163     pnfs_file_device *device;
164 
165     dprintf(FDLVL, "--> pnfs_file_device_list_invalidate()\n");
166 
167     EnterCriticalSection(&devices->lock);
168 
169     list_for_each_tmp(entry, tmp, &devices->head) {
170         device = device_entry(entry);
171         EnterCriticalSection(&device->device.lock);
172         /* if there are layouts still using the device, flag it
173          * as revoked and clean up on last reference */
174         if (device->device.layout_count) {
175             device->device.status |= PNFS_DEVICE_REVOKED;
176             LeaveCriticalSection(&device->device.lock);
177         } else {
178             LeaveCriticalSection(&device->device.lock);
179             /* no layouts are using it, so it's safe to free */
180             list_remove(entry);
181             file_device_free(device);
182         }
183     }
184 
185     LeaveCriticalSection(&devices->lock);
186 
187     dprintf(FDLVL, "<-- pnfs_file_device_list_invalidate()\n");
188 }
189 
190 
191 /* pnfs_file_device */
192 enum pnfs_status pnfs_file_device_get(
193     IN nfs41_session *session,
194     IN struct pnfs_file_device_list *devices,
195     IN unsigned char *deviceid,
196     OUT pnfs_file_device **device_out)
197 {
198     pnfs_file_device *device;
199     enum pnfs_status status;
200     enum nfsstat4 nfsstat;
201 
202     dprintf(FDLVL, "--> pnfs_file_device_get()\n");
203 
204     status = file_device_find_or_create(deviceid, devices, &device);
205     if (status)
206         goto out;
207 
208     EnterCriticalSection(&device->device.lock);
209 
210     /* don't give out a device that's been revoked */
211     if (device->device.status & PNFS_DEVICE_REVOKED)
212         status = PNFSERR_NO_DEVICE;
213     else if (device->device.status & PNFS_DEVICE_GRANTED)
214         status = PNFS_SUCCESS;
215     else {
216         nfsstat = pnfs_rpc_getdeviceinfo(session, deviceid, device);
217         if (nfsstat == NFS4_OK) {
218             device->device.status = PNFS_DEVICE_GRANTED;
219             status = PNFS_SUCCESS;
220 
221             dprintf(FDLVL, "Received device info:\n");
222             dprint_device(FDLVL, device);
223         } else {
224             status = PNFSERR_NO_DEVICE;
225 
226             eprintf("pnfs_rpc_getdeviceinfo() failed with %s\n",
227                 nfs_error_string(nfsstat));
228         }
229     }
230 
231     if (status == PNFS_SUCCESS) {
232         device->device.layout_count++;
233         dprintf(FDLVL, "pnfs_file_device_get() -> %u\n",
234             device->device.layout_count);
235         *device_out = device;
236     }
237 
238     LeaveCriticalSection(&device->device.lock);
239 out:
240     dprintf(FDLVL, "<-- pnfs_file_device_get() returning %s\n",
241         pnfs_error_string(status));
242     return status;
243 }
244 
245 void pnfs_file_device_put(
246     IN pnfs_file_device *device)
247 {
248     uint32_t count;
249     EnterCriticalSection(&device->device.lock);
250     count = --device->device.layout_count;
251     dprintf(FDLVL, "pnfs_file_device_put() -> %u\n", count);
252 
253     /* if the device was revoked, remove/free the device on last reference */
254     if (count == 0 && device->device.status & PNFS_DEVICE_REVOKED) {
255         EnterCriticalSection(&device->devices->lock);
256         list_remove(&device->entry);
257         LeaveCriticalSection(&device->devices->lock);
258 
259         LeaveCriticalSection(&device->device.lock);
260 
261         file_device_free(device);
262         dprintf(FDLVL, "revoked file device freed after last reference\n");
263     } else {
264         LeaveCriticalSection(&device->device.lock);
265     }
266 }
267 
268 static enum pnfs_status data_client_status(
269     IN pnfs_data_server *server,
270     OUT nfs41_client **client_out)
271 {
272     enum pnfs_status status = PNFSERR_NOT_CONNECTED;
273 
274     if (server->client) {
275         dprintf(FDLVL, "pnfs_data_server_client() returning "
276             "existing client %llu\n", server->client->clnt_id);
277         *client_out = server->client;
278         status = PNFS_SUCCESS;
279     }
280     return status;
281 }
282 
283 enum pnfs_status pnfs_data_server_client(
284     IN nfs41_root *root,
285     IN pnfs_data_server *server,
286     IN uint32_t default_lease,
287     OUT nfs41_client **client_out)
288 {
289     int status;
290     enum pnfs_status pnfsstat;
291 
292     dprintf(FDLVL, "--> pnfs_data_server_client('%s')\n",
293         server->addrs.arr[0].uaddr);
294 
295     /* if we've already created the client, return it */
296     AcquireSRWLockShared(&server->lock);
297     pnfsstat = data_client_status(server, client_out);
298     ReleaseSRWLockShared(&server->lock);
299 
300     if (!pnfsstat)
301         goto out;
302 
303     AcquireSRWLockExclusive(&server->lock);
304 
305     pnfsstat = data_client_status(server, client_out);
306     if (pnfsstat) {
307         status = nfs41_root_mount_addrs(root, &server->addrs, 1, default_lease,
308             &server->client);
309         if (status) {
310             dprintf(FDLVL, "data_client_create('%s') failed with %d\n",
311                 server->addrs.arr[0].uaddr, status);
312         } else {
313             *client_out = server->client;
314             pnfsstat = PNFS_SUCCESS;
315 
316             dprintf(FDLVL, "pnfs_data_server_client() returning new client "
317                 "%llu\n", server->client->clnt_id);
318         }
319     }
320 
321     ReleaseSRWLockExclusive(&server->lock);
322 out:
323     return pnfsstat;
324 }
325 
326 
327 /* CB_NOTIFY_DEVICEID */
328 enum pnfs_status pnfs_file_device_notify(
329     IN struct pnfs_file_device_list *devices,
330     IN const struct notify_deviceid4 *change)
331 {
332     struct list_entry *entry;
333     enum pnfs_status status = PNFSERR_NO_DEVICE;
334 
335     dprintf(FDLVL, "--> pnfs_file_device_notify(%u, %0llX:%0llX)\n",
336         change->type, change->deviceid);
337 
338     if (change->layouttype != PNFS_LAYOUTTYPE_FILE) {
339         status = PNFSERR_NOT_SUPPORTED;
340         goto out;
341     }
342 
343     EnterCriticalSection(&devices->lock);
344 
345     entry = list_search(&devices->head, change->deviceid, deviceid_compare);
346     if (entry) {
347         dprintf(FDLVL, "found file device %p\n", device_entry(entry));
348 
349         if (change->type == NOTIFY_DEVICEID4_CHANGE) {
350             /* if (change->immediate) ... */
351             dprintf(FDLVL, "CHANGE (%u)\n", change->immediate);
352         } else if (change->type == NOTIFY_DEVICEID4_DELETE) {
353             /* This notification MUST NOT be sent if the client
354              * has a layout that refers to the device ID. */
355             dprintf(FDLVL, "DELETE\n");
356         }
357         status = PNFS_SUCCESS;
358     }
359 
360     LeaveCriticalSection(&devices->lock);
361 out:
362     dprintf(FDLVL, "<-- pnfs_file_device_notify() returning %s\n",
363         pnfs_error_string(status));
364     return status;
365 }
366