/* NFSv4.1 client for Windows * Copyright © 2012 The Regents of the University of Michigan * * Olga Kornievskaia * Casey Bodley * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, but * without any warranty; without even the implied warranty of merchantability * or fitness for a particular purpose. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA */ #include #include #include #include "nfs41_ops.h" #include "nfs41_callback.h" #include "daemon_debug.h" #define FDLVL 2 /* dprintf level for file device logging */ /* pnfs_file_device_list */ struct pnfs_file_device_list { struct list_entry head; CRITICAL_SECTION lock; }; #define device_entry(pos) list_container(pos, pnfs_file_device, entry) static enum pnfs_status file_device_create( IN const unsigned char *deviceid, IN struct pnfs_file_device_list *devices, OUT pnfs_file_device **device_out) { enum pnfs_status status = PNFS_SUCCESS; pnfs_file_device *device; device = calloc(1, sizeof(pnfs_file_device)); if (device == NULL) { status = PNFSERR_RESOURCES; goto out; } memcpy(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE); device->devices = devices; InitializeCriticalSection(&device->device.lock); *device_out = device; out: return status; } static void file_device_free( IN pnfs_file_device *device) { free(device->servers.arr); free(device->stripes.arr); DeleteCriticalSection(&device->device.lock); free(device); } static int deviceid_compare( const struct list_entry *entry, const void *deviceid) { const pnfs_file_device *device = device_entry(entry); return memcmp(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE); } static enum pnfs_status file_device_find_or_create( IN const unsigned char *deviceid, IN struct pnfs_file_device_list *devices, OUT pnfs_file_device **device_out) { struct list_entry *entry; enum pnfs_status status; dprintf(FDLVL, "--> pnfs_file_device_find_or_create()\n"); EnterCriticalSection(&devices->lock); /* search for an existing device */ entry = list_search(&devices->head, deviceid, deviceid_compare); if (entry == NULL) { /* create a new device */ pnfs_file_device *device; status = file_device_create(deviceid, devices, &device); if (status == PNFS_SUCCESS) { /* add it to the list */ list_add_tail(&devices->head, &device->entry); *device_out = device; dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() " "returning new device %p\n", device); } else { dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() " "returning %s\n", pnfs_error_string(status)); } } else { *device_out = device_entry(entry); status = PNFS_SUCCESS; dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() " "returning existing device %p\n", *device_out); } LeaveCriticalSection(&devices->lock); return status; } enum pnfs_status pnfs_file_device_list_create( OUT struct pnfs_file_device_list **devices_out) { enum pnfs_status status = PNFS_SUCCESS; struct pnfs_file_device_list *devices; devices = calloc(1, sizeof(struct pnfs_file_device_list)); if (devices == NULL) { status = PNFSERR_RESOURCES; goto out; } list_init(&devices->head); InitializeCriticalSection(&devices->lock); *devices_out = devices; out: return status; } void pnfs_file_device_list_free( IN struct pnfs_file_device_list *devices) { struct list_entry *entry, *tmp; EnterCriticalSection(&devices->lock); list_for_each_tmp(entry, tmp, &devices->head) file_device_free(device_entry(entry)); LeaveCriticalSection(&devices->lock); DeleteCriticalSection(&devices->lock); free(devices); } void pnfs_file_device_list_invalidate( IN struct pnfs_file_device_list *devices) { struct list_entry *entry, *tmp; pnfs_file_device *device; dprintf(FDLVL, "--> pnfs_file_device_list_invalidate()\n"); EnterCriticalSection(&devices->lock); list_for_each_tmp(entry, tmp, &devices->head) { device = device_entry(entry); EnterCriticalSection(&device->device.lock); /* if there are layouts still using the device, flag it * as revoked and clean up on last reference */ if (device->device.layout_count) { device->device.status |= PNFS_DEVICE_REVOKED; LeaveCriticalSection(&device->device.lock); } else { LeaveCriticalSection(&device->device.lock); /* no layouts are using it, so it's safe to free */ list_remove(entry); file_device_free(device); } } LeaveCriticalSection(&devices->lock); dprintf(FDLVL, "<-- pnfs_file_device_list_invalidate()\n"); } /* pnfs_file_device */ enum pnfs_status pnfs_file_device_get( IN nfs41_session *session, IN struct pnfs_file_device_list *devices, IN unsigned char *deviceid, OUT pnfs_file_device **device_out) { pnfs_file_device *device; enum pnfs_status status; enum nfsstat4 nfsstat; dprintf(FDLVL, "--> pnfs_file_device_get()\n"); status = file_device_find_or_create(deviceid, devices, &device); if (status) goto out; EnterCriticalSection(&device->device.lock); /* don't give out a device that's been revoked */ if (device->device.status & PNFS_DEVICE_REVOKED) status = PNFSERR_NO_DEVICE; else if (device->device.status & PNFS_DEVICE_GRANTED) status = PNFS_SUCCESS; else { nfsstat = pnfs_rpc_getdeviceinfo(session, deviceid, device); if (nfsstat == NFS4_OK) { device->device.status = PNFS_DEVICE_GRANTED; status = PNFS_SUCCESS; dprintf(FDLVL, "Received device info:\n"); dprint_device(FDLVL, device); } else { status = PNFSERR_NO_DEVICE; eprintf("pnfs_rpc_getdeviceinfo() failed with %s\n", nfs_error_string(nfsstat)); } } if (status == PNFS_SUCCESS) { device->device.layout_count++; dprintf(FDLVL, "pnfs_file_device_get() -> %u\n", device->device.layout_count); *device_out = device; } LeaveCriticalSection(&device->device.lock); out: dprintf(FDLVL, "<-- pnfs_file_device_get() returning %s\n", pnfs_error_string(status)); return status; } void pnfs_file_device_put( IN pnfs_file_device *device) { uint32_t count; EnterCriticalSection(&device->device.lock); count = --device->device.layout_count; dprintf(FDLVL, "pnfs_file_device_put() -> %u\n", count); /* if the device was revoked, remove/free the device on last reference */ if (count == 0 && device->device.status & PNFS_DEVICE_REVOKED) { EnterCriticalSection(&device->devices->lock); list_remove(&device->entry); LeaveCriticalSection(&device->devices->lock); LeaveCriticalSection(&device->device.lock); file_device_free(device); dprintf(FDLVL, "revoked file device freed after last reference\n"); } else { LeaveCriticalSection(&device->device.lock); } } static enum pnfs_status data_client_status( IN pnfs_data_server *server, OUT nfs41_client **client_out) { enum pnfs_status status = PNFSERR_NOT_CONNECTED; if (server->client) { dprintf(FDLVL, "pnfs_data_server_client() returning " "existing client %llu\n", server->client->clnt_id); *client_out = server->client; status = PNFS_SUCCESS; } return status; } enum pnfs_status pnfs_data_server_client( IN nfs41_root *root, IN pnfs_data_server *server, IN uint32_t default_lease, OUT nfs41_client **client_out) { int status; enum pnfs_status pnfsstat; dprintf(FDLVL, "--> pnfs_data_server_client('%s')\n", server->addrs.arr[0].uaddr); /* if we've already created the client, return it */ AcquireSRWLockShared(&server->lock); pnfsstat = data_client_status(server, client_out); ReleaseSRWLockShared(&server->lock); if (!pnfsstat) goto out; AcquireSRWLockExclusive(&server->lock); pnfsstat = data_client_status(server, client_out); if (pnfsstat) { status = nfs41_root_mount_addrs(root, &server->addrs, 1, default_lease, &server->client); if (status) { dprintf(FDLVL, "data_client_create('%s') failed with %d\n", server->addrs.arr[0].uaddr, status); } else { *client_out = server->client; pnfsstat = PNFS_SUCCESS; dprintf(FDLVL, "pnfs_data_server_client() returning new client " "%llu\n", server->client->clnt_id); } } ReleaseSRWLockExclusive(&server->lock); out: return pnfsstat; } /* CB_NOTIFY_DEVICEID */ enum pnfs_status pnfs_file_device_notify( IN struct pnfs_file_device_list *devices, IN const struct notify_deviceid4 *change) { struct list_entry *entry; enum pnfs_status status = PNFSERR_NO_DEVICE; dprintf(FDLVL, "--> pnfs_file_device_notify(%u, %0llX:%0llX)\n", change->type, change->deviceid); if (change->layouttype != PNFS_LAYOUTTYPE_FILE) { status = PNFSERR_NOT_SUPPORTED; goto out; } EnterCriticalSection(&devices->lock); entry = list_search(&devices->head, change->deviceid, deviceid_compare); if (entry) { dprintf(FDLVL, "found file device %p\n", device_entry(entry)); if (change->type == NOTIFY_DEVICEID4_CHANGE) { /* if (change->immediate) ... */ dprintf(FDLVL, "CHANGE (%u)\n", change->immediate); } else if (change->type == NOTIFY_DEVICEID4_DELETE) { /* This notification MUST NOT be sent if the client * has a layout that refers to the device ID. */ dprintf(FDLVL, "DELETE\n"); } status = PNFS_SUCCESS; } LeaveCriticalSection(&devices->lock); out: dprintf(FDLVL, "<-- pnfs_file_device_notify() returning %s\n", pnfs_error_string(status)); return status; }