Whamcloud - gitweb
b=19856
authornathan <nathan>
Tue, 28 Jul 2009 19:43:06 +0000 (19:43 +0000)
committernathan <nathan>
Tue, 28 Jul 2009 19:43:06 +0000 (19:43 +0000)
i=adilger
i=rread
LustreNetLink (LNL) kernel<->userspace communications path
HSM copytool llapi based on lnl
(commit added files)

libcfs/ChangeLog
libcfs/include/libcfs/libcfs_kernelcomm.h [new file with mode: 0644]
libcfs/include/libcfs/posix/posix-kernelcomm.h [new file with mode: 0644]
libcfs/libcfs/linux/linux-kernelcomm.c [new file with mode: 0644]
libcfs/libcfs/ulinux/Makefile.am [new file with mode: 0644]
libcfs/libcfs/ulinux/ulinux-kernelcomm.c [new file with mode: 0644]
lustre/ChangeLog
lustre/tests/copytool.c [new file with mode: 0644]

index a783bef..a7b9e08 100644 (file)
@@ -1,4 +1,14 @@
-2008-07-15  Robert Read  <rread@sun.com>
+tbd  Sun Microsystems, Inc.
+        * version 2.0.0
 
-       * new libcfs module
+Severity   : enhancement
+Bugzilla   : 19856
+Description: Add LustreNetLink, a kernel-userspace communcation path.  Add
+       ulinux dir for Linux userspace tools.
 
+-------------------------------------------------------------------------------
+
+2008-07-15  Sun Microsystems, Inc.
+        * version 1.8.0
+
+new libcfs module
diff --git a/libcfs/include/libcfs/libcfs_kernelcomm.h b/libcfs/include/libcfs/libcfs_kernelcomm.h
new file mode 100644 (file)
index 0000000..8da8c96
--- /dev/null
@@ -0,0 +1,115 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright  2009 Sun Microsystems, Inc. All rights reserved
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Author: Nathan Rutman <nathan.rutman@sun.com>
+ *
+ * libcfs/include/libcfs/libcfs_kernelcomm.h
+ *
+ * Kernel <-> userspace communication routines.  We'll use a shorthand term
+ * "lnl" (Lustre NetLink) for this interface name for all arches, even though
+ * an implemtation may not use NetLink.
+ * The definitions below are used in the kernel and userspace.
+ *
+ */
+
+#ifndef __LIBCFS_KERNELCOMM_H__
+#define __LIBCFS_KERNELCOMM_H__
+
+#ifndef __LIBCFS_LIBCFS_H__
+#error Do not #include this file directly. #include <libcfs/libcfs.h> instead
+#endif
+
+/* LNL message header.
+ * All current and future LNL messages should use this header.
+ * To avoid having to include Lustre headers from libcfs, define this here.
+ */
+struct lnl_hdr {
+        __u16 lnl_magic;
+        __u8  lnl_transport;  /* Each new Lustre feature should use a different
+                                 transport */
+        __u8  padding1;
+        __u16 lnl_msgtype;    /* Message type or opcode, transport-specific */
+        __u16 lnl_msglen;
+} __attribute__((aligned(sizeof(__u64))));
+
+#define LNL_MAGIC  0x191C /*Lustre9etLinC */
+
+/* lnl_msgtype values are defined in each transport */
+enum lnl_transport_type {
+        LNL_TRANSPORT_GENERIC = 1,
+        LNL_TRANSPORT_HSM     = 2,
+};
+
+enum lnl_generic_message_type {
+        LNL_MSG_SHUTDOWN = 1,
+};
+
+/* LNL Broadcast Groups. This determines which userspace process hears which
+ * messages.  Mutliple transports may be used within a group, or multiple
+ * groups may use the same transport.  Broadcast
+ * groups need not be used if e.g. a PID is specified instead.
+ * Leaving group 0 unassigned at the moment.
+ */
+#define LNL_GRP_HSM           0x02
+#define LNL_GRP_CNT              2
+
+
+#if defined(HAVE_NETLINK) && defined (__KERNEL__)
+extern int libcfs_klnl_start(int transport);
+extern int libcfs_klnl_stop(int transport, int group);
+extern int libcfs_klnl_msg_put(int pid, int group, void *payload);
+#else
+static inline int libcfs_klnl_start(int transport) {
+        return -ENOSYS;
+}
+static inline int libcfs_klnl_stop(int transport, int group) {
+        return 0;
+}
+static inline int libcfs_klnl_msg_put(int pid, int group, void *payload) {
+        return -ENOSYS;
+}
+#endif
+
+/*
+ * NetLink socket number, see include/linux/netlink.h
+ * All LNL users share a single netlink socket.  This actually is NetLink
+ * specific, but is not to be used outside of the Linux implementation
+ * (linux-kernelcomm.c and posix-kernelcomm.c).
+ */
+#define LNL_SOCKET 26
+
+
+#endif /* __LIBCFS_KERNELCOMM_H__ */
+
diff --git a/libcfs/include/libcfs/posix/posix-kernelcomm.h b/libcfs/include/libcfs/posix/posix-kernelcomm.h
new file mode 100644 (file)
index 0000000..7275631
--- /dev/null
@@ -0,0 +1,59 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright  2009 Sun Microsystems, Inc. All rights reserved
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Author: Nathan Rutman <nathan.rutman@sun.com>
+ *
+ * libcfs/include/libcfs/posix-kernelcomm.h
+ *
+ * kernel - userspace communications.
+ */
+
+#ifndef __LIBCFS_POSIX_KERNELCOMM_H__
+#define __LIBCFS_POSIX_KERNELCOMM_H__
+
+#ifndef __LIBCFS_LIBCFS_H__
+#error Do not #include this file directly. #include <libcfs/libcfs.h> instead
+#endif
+
+typedef int lustre_netlink;
+int libcfs_ulnl_start(lustre_netlink *l, int groups);
+int libcfs_ulnl_stop(lustre_netlink *l);
+struct lnl_hdr;
+int libcfs_ulnl_msg_get(lustre_netlink *l, int maxsize, int transport,
+                        struct lnl_hdr **lnlhh);
+int libcfs_ulnl_msg_free(struct lnl_hdr **lnlhh);
+
+#endif
+
diff --git a/libcfs/libcfs/linux/linux-kernelcomm.c b/libcfs/libcfs/linux/linux-kernelcomm.c
new file mode 100644 (file)
index 0000000..9845b6c
--- /dev/null
@@ -0,0 +1,206 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright  2009 Sun Microsystems, Inc. All rights reserved
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Author: Nathan Rutman <nathan.rutman@sun.com>
+ *
+ * Kernel <-> userspace communication routines.  We'll use a shorthand term
+ * "lnl" (Lustre NetLink) for the interface names for all arches (even though
+ * implemtation may not use NetLink).
+ * For Linux, we use Netlink sockets.
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+
+/* This is the kernel side.
+ * See libcfs/ulinux/ulinux-kernelcomm.c for the user side.
+ */
+
+#if defined(HAVE_NETLINK) && defined(__KERNEL__)
+
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <net/netlink.h>
+
+#include <libcfs/libcfs.h>
+
+/* Single Netlink Message type to send all Lustre messages */
+#define LNL_MSG 26
+
+static struct sock *lnl_socket = NULL;
+static atomic_t lnl_start_count = ATOMIC_INIT(0);
+static spinlock_t lnl_lock = SPIN_LOCK_UNLOCKED;
+
+/** Start the netlink socket for this transport
+ * @param transport lnl_transport
+ */
+int libcfs_klnl_start(int transport)
+{
+        int rc = 0;
+        ENTRY;
+
+        /* If anyone needs it, we can add per-transport incoming message
+           callbacks.  Add the callback as a param here.  Store the transport
+           and callback in a table. Include a generalized incoming msg
+           callback here to dispatch messages to the appropriate
+           per-transport callback. */
+
+        spin_lock(&lnl_lock);
+        if (atomic_inc_return(&lnl_start_count) > 1)
+                GOTO(out, rc = 0);
+
+        lnl_socket = netlink_kernel_create(LNL_SOCKET, LNL_GRP_CNT,
+                                           NULL /* incoming cb */,
+                                           THIS_MODULE);
+        if (lnl_socket == NULL) {
+                CERROR("Cannot open socket %d\n", LNL_SOCKET);
+                atomic_dec(&lnl_start_count);
+                GOTO(out, rc = -ENODEV);
+        }
+
+out:
+        spin_unlock(&lnl_lock);
+        RETURN(rc);
+}
+EXPORT_SYMBOL(libcfs_klnl_start);
+
+static void send_shutdown_msg(int transport, int group) {
+        struct lnl_hdr lh;
+
+        lh.lnl_magic = LNL_MAGIC;
+        lh.lnl_transport = LNL_TRANSPORT_GENERIC;
+        lh.lnl_msgtype = LNL_MSG_SHUTDOWN;
+        lh.lnl_msglen = sizeof(lh);
+
+        libcfs_klnl_msg_put(0, group, &lh);
+}
+
+/* This should be called once per (started) transport
+ * @param transport lnl_transport
+ * @param group Broadcast group for shutdown message */
+int libcfs_klnl_stop(int transport, int group)
+{
+        if (group)
+                send_shutdown_msg(transport, group);
+
+        spin_lock(&lnl_lock);
+
+        if (atomic_dec_and_test(&lnl_start_count)) {
+                sock_release(lnl_socket->sk_socket);
+                lnl_socket = NULL;
+        }
+
+        spin_unlock(&lnl_lock);
+        return 0;
+}
+EXPORT_SYMBOL(libcfs_klnl_stop);
+
+static struct sk_buff *netlink_make_msg(int pid, int seq, void *payload,
+                                        int size)
+{
+        struct sk_buff  *skb;
+        struct nlmsghdr *nlh;
+        int             len = NLMSG_SPACE(size);
+        void            *data;
+
+        skb = nlmsg_new(len, GFP_KERNEL);
+        if (!skb)
+                return NULL;
+
+        nlh = nlmsg_put(skb, pid, seq, LNL_MSG, size, 0);
+        if (!nlh) {
+                nlmsg_free(skb);
+                return NULL;
+        }
+
+        data = nlmsg_data(nlh);
+        memcpy(data, payload, size);
+        return skb;
+}
+
+/**
+ * libcfs_klnl_msg_put - send an message from kernel to userspace
+ * @param pid Process id to send message to for unicast messages; must be 0 for
+ *   broadcast
+ * @param group Broadcast group; 0 for unicast messages
+ * @param payload Payload data.  First field of payload is always struct lnl_hdr
+ *
+ * Allocates an skb, builds the netlink message, and sends it to the pid.
+ */
+int libcfs_klnl_msg_put(int pid, int group, void *payload)
+{
+        struct lnl_hdr *lnlh = (struct lnl_hdr *)payload;
+        struct sk_buff  *skb;
+        int rc;
+
+        if (lnl_socket == NULL) {
+                CERROR("LustreNetLink: not running\n");
+                return -ENOSYS;
+        }
+
+        if (lnlh->lnl_magic != LNL_MAGIC) {
+                CERROR("LustreNetLink: bad magic %x\n", lnlh->lnl_magic);
+                return -ENOSYS;
+        }
+
+        if ((pid != 0) && (group != 0)) {
+                CERROR("LustreNetLink: pid=%d or group=%d must be 0\n",
+                       pid, group);
+                return -EINVAL;
+        }
+
+        skb = netlink_make_msg(pid, 0, payload, lnlh->lnl_msglen);
+        if (!skb)
+                return -ENOMEM;
+
+        if (pid)
+                rc = nlmsg_unicast(lnl_socket, skb, pid);
+        else
+                rc = nlmsg_multicast(lnl_socket, skb, 0, group);
+
+        CDEBUG(0, "Sent message pid=%d, group=%d, rc=%d\n", pid, group, rc);
+
+        if (rc < 0)
+                CWARN("message send failed (%d) [pid=%d,group=%d]\n", rc,
+                      pid, group);
+
+        return rc;
+}
+EXPORT_SYMBOL(libcfs_klnl_msg_put);
+
+
+#endif
+
diff --git a/libcfs/libcfs/ulinux/Makefile.am b/libcfs/libcfs/ulinux/Makefile.am
new file mode 100644 (file)
index 0000000..0541235
--- /dev/null
@@ -0,0 +1,2 @@
+EXTRA_DIST := ulinux-kernelcomm.c
+
diff --git a/libcfs/libcfs/ulinux/ulinux-kernelcomm.c b/libcfs/libcfs/ulinux/ulinux-kernelcomm.c
new file mode 100644 (file)
index 0000000..8d84316
--- /dev/null
@@ -0,0 +1,180 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright  2009 Sun Microsystems, Inc. All rights reserved
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Author: Nathan Rutman <nathan.rutman@sun.com>
+ *
+ * Kernel - userspace communication routines.  We'll use a shorthand term
+ * "lnl" (Lustre NetLink) for the interface names for all arches (even though
+ * implemtation may not use NetLink).
+ * For Linux, we use Netlink sockets.
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+/* This is the userspace side.
+ * See libcfs/linux/linux-kernelcomm.c for the kernel side.
+ */
+
+#ifdef HAVE_NETLINK
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+
+#include <libcfs/libcfs.h>
+
+/** Start the userspace side of a LNL pipe.
+ * @param link Private descriptor for pipe/socket.
+ * @param groups LNL broadcast group to listen to
+ *          (can be null for unicast to this pid)
+ */
+int libcfs_ulnl_start(lustre_netlink *link, int groups)
+{
+        struct sockaddr_nl src_addr;
+        int sock;
+        int rc = 0;
+
+        sock = socket(PF_NETLINK, SOCK_RAW, LNL_SOCKET);
+        if (sock < 0)
+                return -errno;
+
+        memset(&src_addr, 0, sizeof(src_addr));
+        src_addr.nl_family = AF_NETLINK;
+        src_addr.nl_pid = getpid();  /* self pid */
+        src_addr.nl_groups = groups;
+        rc = bind(sock, (struct sockaddr*)&src_addr, sizeof(src_addr));
+        if (rc < 0) {
+                close(sock);
+                return -errno;
+        }
+        *link = sock;
+        return 0;
+}
+
+int libcfs_ulnl_stop(lustre_netlink *link)
+{
+        return close(*link);
+}
+
+/** Read a message from the netlink layer.
+ *
+ * @param link Private descriptor for pipe/socket.
+ * @param maxsize Maximum message size allowed
+ * @param transport Only listen to messages on this transport
+ *      (and the generic transport)
+ * @param lnlhh Handle to the new LNL message
+ */
+int libcfs_ulnl_msg_get(lustre_netlink *link, int maxsize, int transport,
+                        struct lnl_hdr **lnlhh)
+{
+        struct iovec iov;
+        struct sockaddr_nl dest_addr;
+        struct msghdr msg;
+        struct nlmsghdr *nlh = NULL;
+        struct lnl_hdr *lnlh;
+        int rc = 0;
+
+        nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(maxsize));
+        if (!nlh)
+                return -ENOMEM;
+
+        memset(nlh, 0, NLMSG_SPACE(maxsize));
+        iov.iov_base = (void *)nlh;
+        iov.iov_len = NLMSG_SPACE(maxsize);
+
+        memset(&dest_addr, 0, sizeof(dest_addr));
+        msg.msg_name = (void *)&dest_addr;
+        msg.msg_namelen = sizeof(dest_addr);
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+
+        CDEBUG(0, "Waiting for message from kernel on pid %d\n", getpid());
+
+        while (1) {
+                /* Read message from kernel */
+                rc = recvmsg(*link, &msg, 0);
+                if (rc <= 0) {
+                        perror("recv");
+                        rc = -errno;
+                        break;
+                }
+                lnlh = (struct lnl_hdr *)NLMSG_DATA(nlh);
+                CDEBUG(0, " Received message mg=%x t=%d m=%d l=%d\n",
+                       lnlh->lnl_magic, lnlh->lnl_transport, lnlh->lnl_msgtype,
+                       lnlh->lnl_msglen);
+                if (lnlh->lnl_magic != LNL_MAGIC) {
+                        CERROR("bad message magic %x != %x\n",
+                               lnlh->lnl_magic, LNL_MAGIC);
+                        rc = -EPROTO;
+                        break;
+                }
+                if (lnlh->lnl_transport == transport ||
+                    lnlh->lnl_transport == LNL_TRANSPORT_GENERIC) {
+                        *lnlhh = lnlh;
+                        return 0;
+                }
+                /* Ignore messages on other transports */
+        }
+        free(nlh);
+        return rc;
+}
+
+/* Free a message returned by the above fn */
+int libcfs_ulnl_msg_free(struct lnl_hdr **lnlhh)
+{
+        /* compute nlmsdghdr offset */
+        char *p = (char *)NLMSG_DATA(0);
+
+        free((void *)((char *)*lnlhh - p));
+        *lnlhh = NULL;
+        return 0;
+}
+
+#else /* HAVE_NETLINK */
+int libcfs_ulnl_start(lustre_netlink *link, int groups) {
+        return -ENOSYS;
+}
+int libcfs_ulnl_stop(lustre_netlink *link) {
+        return 0;
+}
+
+int libcfs_ulnl_msg_get(lustre_netlink *link, int maxsize, int transport,
+                        struct lnl_hdr **lnlhh) {
+        return -ENOSYS;
+}
+int libcfs_ulnl_msg_free(struct lnl_hdr **lnlhh) {
+        return -ENOSYS;
+}
+#endif /* HAVE_NETLINK */
+
index a5dd055..38ec1a2 100644 (file)
@@ -14,6 +14,10 @@ tbd  Sun Microsystems, Inc.
        * File join has been disabled in this release, refer to Bugzilla 16929.
 
 Severity   : enhancement
+Bugzilla   : 19856
+Description: Add LustreNetLink, a kernel-userspace communcation path.
+       
+Severity   : enhancement
 Bugzilla   : 19847
 Description: Update kernel to SLES10 SP2 2.6.16.60-0.39.3.
 
diff --git a/lustre/tests/copytool.c b/lustre/tests/copytool.c
new file mode 100644 (file)
index 0000000..a74fac3
--- /dev/null
@@ -0,0 +1,108 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright  2009 Sun Microsystems, Inc. All rights reserved
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Author: Nathan Rutman <nathan.rutman@sun.com>
+ *
+ */
+
+/* HSM copytool example program.
+ * The copytool acts on action requests from Lustre to copy files to and from
+ * an HSM archive system.
+ *
+ * Note: under Linux, until llapi_copytool_fini is called (or the program is
+ * killed), the libcfs module will be referenced and unremovable,
+ * even after Lustre services stop.
+ */
+
+#include <stdio.h>
+#include <libcfs/libcfs.h>
+#include <lustre/lustre_user.h>
+#include <lustre/liblustreapi.h>
+
+int main() {
+        void *ctdata;
+        int archive_nums[] = {1}; /* which archive numbers we care about */
+        int rc;
+
+        rc = llapi_copytool_start(&ctdata, 0, ARRAY_SIZE(archive_nums),
+                                  archive_nums);
+        if (rc < 0) {
+                fprintf(stderr, "Can't start copytool interface: %s\n",
+                        strerror(-rc));
+                return rc;
+        }
+
+        printf("Waiting for message from kernel (pid=%d)\n", getpid());
+
+        while(1) {
+                struct hsm_action_list *hal;
+                struct hsm_action_item *hai;
+                int msgsize, i = 0;
+
+                rc = llapi_copytool_recv(ctdata, &hal, &msgsize);
+                if (rc == -ESHUTDOWN) {
+                        fprintf(stderr, "shutting down");
+                        break;
+                }
+                if (rc < 0) {
+                        fprintf(stderr, "Message receive: %s", strerror(-rc));
+                        break;
+                }
+                if (msgsize == 0)
+                        continue; /* msg not for us */
+
+                printf("Copytool fs=%s archive#=%d item_count=%d\n",
+                       hal->hal_fsname, hal->hal_archive_num, hal->hal_count);
+
+                hai = hai_zero(hal);
+                while (++i <= hal->hal_count) {
+                        printf("Item %d: action %d reclen %d\n", i,
+                               hai->hai_action, hai->hai_len);
+                        printf(" "DFID" gid="LPU64" cookie="LPU64"\n",
+                               PFID(&hai->hai_fid), hai->hai_gid,
+                               hai->hai_cookie);
+                        hai = hai_next(hai);
+                }
+
+                llapi_copytool_free(&hal);
+        }
+
+        llapi_copytool_fini(&ctdata);
+
+        return 0;
+}
+
+
+