diff --git a/daemon/callback_server.c b/daemon/callback_server.c index d454087..ec79e67 100644 --- a/daemon/callback_server.c +++ b/daemon/callback_server.c @@ -245,6 +245,21 @@ out: return res->status; } +/* OP_CB_NOTIFY_DEVICEID */ +static enum_t handle_cb_notify_deviceid( + IN nfs41_rpc_clnt *rpc_clnt, + IN struct cb_notify_deviceid_args *args, + OUT struct cb_notify_deviceid_res *res) +{ + uint32_t i; + for (i = 0; i < args->change_count; i++) { + pnfs_file_device_notify(rpc_clnt->client->devices, + &args->change_list[i]); + } + res->status = NFS4_OK; + return res->status; +} + static void replay_cache_write( IN nfs41_cb_session *session, IN OPTIONAL struct cb_compound_args *args, @@ -516,7 +531,7 @@ static void handle_cb_compound(nfs41_rpc_clnt *rpc_clnt, cb_req *req, struct cb_ break; case OP_CB_NOTIFY_DEVICEID: dprintf(1, "OP_CB_NOTIFY_DEVICEID\n"); - res->status = NFS4ERR_NOTSUPP; + res->status = NFS4_OK; break; case OP_CB_ILLEGAL: dprintf(1, "OP_CB_ILLEGAL\n"); @@ -573,4 +588,4 @@ int nfs41_handle_callback(void *rpc_clnt, void *cb, struct cb_compound_res **rep } out: return status; -} \ No newline at end of file +} diff --git a/daemon/callback_xdr.c b/daemon/callback_xdr.c index 2c6913d..dc27155 100644 --- a/daemon/callback_xdr.c +++ b/daemon/callback_xdr.c @@ -31,6 +31,8 @@ /* common types */ +bool_t xdr_bitmap4(XDR *xdr, bitmap4 *bitmap); + static bool_t common_stateid(XDR *xdr, stateid4 *stateid) { return xdr_u_int32_t(xdr, &stateid->seqid) @@ -50,6 +52,12 @@ static bool_t common_fsid(XDR *xdr, nfs41_fsid *fsid) && xdr_u_int64_t(xdr, &fsid->minor); } +static bool_t common_notify4(XDR *xdr, struct notify4 *notify) +{ + return xdr_bitmap4(xdr, ¬ify->mask) + && xdr_bytes(xdr, ¬ify->list, ¬ify->len, NFS4_OPAQUE_LIMIT); +} + /* OP_CB_LAYOUTRECALL */ static bool_t op_cb_layoutrecall_file(XDR *xdr, struct cb_recall_file *args) { @@ -409,12 +417,86 @@ out: } /* OP_CB_NOTIFY_DEVICEID */ -static bool_t op_cb_notify_deviceid_args(XDR *xdr, struct cb_notify_deviceid_args *res) +static bool_t cb_notify_deviceid_change(XDR *xdr, struct notify_deviceid4 *change) { bool_t result; - result = xdr_u_int32_t(xdr, &res->target_highest_slotid); - if (!result) { CBX_ERR("notify_deviceid.target_highest_slotid"); goto out; } + result = xdr_u_int32_t(xdr, (uint32_t*)&change->layouttype); + if (!result) { CBX_ERR("notify_deviceid.change.layouttype"); goto out; } + + result = xdr_opaque(xdr, (char*)change->deviceid, PNFS_DEVICEID_SIZE); + if (!result) { CBX_ERR("notify_deviceid.change.deviceid"); goto out; } + + result = xdr_bool(xdr, &change->immediate); + if (!result) { CBX_ERR("notify_deviceid.change.immediate"); goto out; } +out: + return result; +} + +static bool_t cb_notify_deviceid_delete(XDR *xdr, struct notify_deviceid4 *change) +{ + bool_t result; + + result = xdr_u_int32_t(xdr, (uint32_t*)&change->layouttype); + if (!result) { CBX_ERR("notify_deviceid.delete.layouttype"); goto out; } + + result = xdr_opaque(xdr, (char*)change->deviceid, PNFS_DEVICEID_SIZE); + if (!result) { CBX_ERR("notify_deviceid.delete.deviceid"); goto out; } +out: + return result; +} + +static bool_t op_cb_notify_deviceid_args(XDR *xdr, struct cb_notify_deviceid_args *args) +{ + XDR notify_xdr; + uint32_t i, j, c; + bool_t result; + + /* decode the generic notify4 list */ + result = xdr_array(xdr, (char**)&args->notify_list, + &args->notify_count, CB_COMPOUND_MAX_OPERATIONS, + sizeof(struct notify4), (xdrproc_t)common_notify4); + if (!result) { CBX_ERR("notify_deviceid.notify_list"); goto out; } + + switch (xdr->x_op) { + case XDR_FREE: + free(args->change_list); + case XDR_ENCODE: + return TRUE; + } + + /* count the number of device changes */ + args->change_count = 0; + for (i = 0; i < args->notify_count; i++) + args->change_count += args->notify_list[i].mask.count; + + args->change_list = calloc(args->change_count, sizeof(struct notify_deviceid4)); + if (args->change_list == NULL) + return FALSE; + + c = 0; + for (i = 0; i < args->notify_count; i++) { + struct notify4 *notify = &args->notify_list[i]; + + /* decode the device notifications out of the opaque buffer */ + xdrmem_create(¬ify_xdr, notify->list, notify->len, XDR_DECODE); + + for (j = 0; j < notify->mask.count; j++) { + struct notify_deviceid4 *change = &args->change_list[c++]; + change->type = notify->mask.arr[j]; + + switch (change->type) { + case NOTIFY_DEVICEID4_CHANGE: + result = cb_notify_deviceid_change(¬ify_xdr, change); + if (!result) { CBX_ERR("notify_deviceid.change"); goto out; } + break; + case NOTIFY_DEVICEID4_DELETE: + result = cb_notify_deviceid_delete(¬ify_xdr, change); + if (!result) { CBX_ERR("notify_deviceid.delete"); goto out; } + break; + } + } + } out: return result; } diff --git a/daemon/nfs41_callback.h b/daemon/nfs41_callback.h index 4706183..2438c8c 100644 --- a/daemon/nfs41_callback.h +++ b/daemon/nfs41_callback.h @@ -194,8 +194,26 @@ struct cb_notify_lock_res { }; /* OP_CB_NOTIFY_DEVICEID */ +enum notify_deviceid_type4 { + NOTIFY_DEVICEID4_CHANGE = 1, + NOTIFY_DEVICEID4_DELETE = 2 +}; +struct notify_deviceid4 { + unsigned char deviceid[16]; + enum notify_deviceid_type4 type; + enum pnfs_layout_type layouttype; + bool_t immediate; +}; +struct notify4 { + bitmap4 mask; + char *list; + uint32_t len; +}; struct cb_notify_deviceid_args { - uint32_t target_highest_slotid; + struct notify4 *notify_list; + uint32_t notify_count; + struct notify_deviceid4 *change_list; + uint32_t change_count; }; struct cb_notify_deviceid_res { @@ -211,6 +229,7 @@ union cb_op_args { struct cb_recall_slot_args recall_slot; struct cb_sequence_args sequence; struct cb_recall_args recall; + struct cb_notify_deviceid_args notify_deviceid; }; struct cb_argop { enum_t opnum; @@ -234,6 +253,7 @@ union cb_op_res { struct cb_recall_slot_res recall_slot; struct cb_sequence_res sequence; struct cb_recall_res recall; + struct cb_notify_deviceid_res notify_deviceid; }; struct cb_resop { enum_t opnum; diff --git a/daemon/nfs41_xdr.c b/daemon/nfs41_xdr.c index 2c2270c..26c958e 100644 --- a/daemon/nfs41_xdr.c +++ b/daemon/nfs41_xdr.c @@ -47,7 +47,7 @@ static __inline int unexpected_op(uint32_t op, uint32_t expected) } /* typedef uint32_t bitmap4<> */ -static bool_t xdr_bitmap4( +bool_t xdr_bitmap4( XDR *xdr, bitmap4 *bitmap) { diff --git a/daemon/pnfs.h b/daemon/pnfs.h index 0a94e48..c397511 100644 --- a/daemon/pnfs.h +++ b/daemon/pnfs.h @@ -317,6 +317,12 @@ enum pnfs_status pnfs_file_device_get( void pnfs_file_device_put( IN pnfs_file_device *device); +struct notify_deviceid4; /* from nfs41_callback.h */ +enum notify_deviceid_type4; +enum pnfs_status pnfs_file_device_notify( + IN struct pnfs_file_device_list *devices, + IN const struct notify_deviceid4 *change); + enum pnfs_status pnfs_data_server_client( IN struct __nfs41_root *root, IN pnfs_data_server *server, diff --git a/daemon/pnfs_device.c b/daemon/pnfs_device.c index 1363871..cb12bb9 100644 --- a/daemon/pnfs_device.c +++ b/daemon/pnfs_device.c @@ -26,6 +26,7 @@ #include #include "nfs41_ops.h" +#include "nfs41_callback.h" #include "daemon_debug.h" @@ -420,3 +421,44 @@ enum pnfs_status pnfs_file_device_io_unit( io->length = pattern->offset_end - offset; return status; } + + +/* 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; +}