This patch adds netlink support to dm-netlink. It also adds support for using netlink attributes for the event messages. Signed-off-by: Mike Anderson <andmike@xxxxxxxxxx> drivers/md/dm-netlink.c | 277 ++++++++++++++++++++++++++++++++++++++------- drivers/md/dm-netlink.h | 58 +++++++++ include/linux/dm-netlink.h | 46 +++++++ 3 files changed, 344 insertions(+), 37 deletions(-) Index: sas-2.6-patched/drivers/md/dm-netlink.c =================================================================== --- sas-2.6-patched.orig/drivers/md/dm-netlink.c 2006-01-15 17:39:28.000000000 -0800 +++ sas-2.6-patched/drivers/md/dm-netlink.c 2006-01-15 17:42:48.000000000 -0800 @@ -27,13 +27,16 @@ #include <linux/security.h> #include <net/sock.h> #include <net/netlink.h> +#include <linux/dm-netlink.h> +#include "dm-netlink.h" #define MIN_EVT_SKBS 16 #define HIWAT_EVT_SKBS 32 #define EVT_SKB_SIZE NLMSG_SPACE(128) -struct mempool_zone { +struct mp_zone { mempool_t *pool; + kmem_cache_t *cache; int allocated; int size; int hiwat; @@ -41,55 +44,63 @@ struct mempool_zone { spinlock_t freelock; }; -static struct mempool_zone z_dm_evt; +static struct mp_zone z_dm_evt; -static inline struct list_head *skb_to_lh(struct sk_buff *skb) -{ - return (struct list_head *)&skb->cb; -} - -static void* mempool_zone_alloc_skb(unsigned int gfp_mask, +static void* mp_zone_alloc_dm_evt(unsigned int gfp_mask, void *pool_data) { - struct mempool_zone *zone = pool_data; + struct dm_evt *evt; + struct mp_zone *zone = pool_data; - return alloc_skb(zone->size, gfp_mask); + evt = kmem_cache_alloc(zone->cache, gfp_mask); + if (!evt) + goto out; + + evt->skb = alloc_skb(zone->size, gfp_mask); + if (!evt->skb) + goto cache_out; + return evt; + +cache_out: + kmem_cache_free(zone->cache, evt); +out: + return NULL; } -static void mempool_zone_free_skb(void *element, void *pool_data) +static void mp_zone_free_dm_evt(void *element, void *pool_data) { - kfree_skb(element); + struct dm_evt *evt = element; + struct mp_zone *zone = pool_data; + + kfree_skb(evt->skb); + kmem_cache_free(zone->cache, evt); } static void -mempool_zone_complete(struct mempool_zone *zone, int release_all) +mp_zone_complete(struct mp_zone *zone, int release_all) { unsigned long flags; - struct list_head *lh, *n; + struct dm_evt *evt, *n; spin_lock_irqsave(&zone->freelock, flags); if (zone->allocated) { - list_for_each_safe(lh, n, &zone->freequeue) { - struct sk_buff *skb = - (struct sk_buff *)((char *)lh - - offsetof(struct sk_buff, cb)); - if (skb_shared(skb)) { + list_for_each_entry_safe(evt, n, &zone->freequeue, zlist) { + if (skb_shared(evt->skb)) { if (release_all) - kfree_skb(skb); + kfree_skb(evt->skb); else continue; } - list_del(skb_to_lh(skb)); - mempool_free(skb, zone->pool); + list_del(&evt->zlist); + mempool_free(evt, zone->pool); --zone->allocated; - } } spin_unlock_irqrestore(&zone->freelock, flags); } -static int mempool_zone_init(struct mempool_zone *zp, unsigned size, +static int mp_zone_init(struct mp_zone *zp, unsigned size, int min_nr, unsigned hiwat) { zp->size = size; @@ -98,47 +109,239 @@ static int mempool_zone_init(struct memp INIT_LIST_HEAD(&zp->freequeue); spin_lock_init(&zp->freelock); - zp->pool = mempool_create(min_nr, mempool_zone_alloc_skb, - mempool_zone_free_skb, zp); + zp->pool = mempool_create(min_nr, mp_zone_alloc_dm_evt, + mp_zone_free_dm_evt, zp); if (!zp->pool) return -ENOMEM; return 0; } -static struct sk_buff* mempool_zone_get_skb(struct mempool_zone *zone) +static struct dm_evt* mp_zone_get_dm_evt(struct mp_zone *zone) { - struct sk_buff *skb; + struct dm_evt *evt; unsigned long flags; /* Check for ones we can complete before we alloc */ - mempool_zone_complete(zone, 0); + mp_zone_complete(zone, 0); - skb = mempool_alloc(zone->pool, GFP_ATOMIC); - if (skb) { - skb_get(skb); + evt = mempool_alloc(zone->pool, GFP_ATOMIC); + if (evt) { + skb_get(evt->skb); + INIT_LIST_HEAD(&evt->zlist); spin_lock_irqsave(&z_dm_evt.freelock, flags); - list_add(skb_to_lh(skb), &z_dm_evt.freequeue); + list_add(&evt->zlist, &z_dm_evt.freequeue); ++zone->allocated; spin_unlock_irqrestore(&z_dm_evt.freelock, flags); } - return skb; + return evt; +} + +static struct sock *dm_nl_sock; +static int dm_nl_daemon_pid; + +static u64 dm_evt_seqnum; +static DEFINE_SPINLOCK(sequence_lock); + +static struct dm_evt *dm_nl_build_path_msg(char* dm_name, int type, + int blk_err) +{ + struct dm_evt *evt; + struct nlmsghdr *nlh; + struct dm_nl_msghdr *dm_nlh; + u64 seq; + struct timeval tv; + int err = -ENOBUFS; + + evt = mp_zone_get_dm_evt(&z_dm_evt); + if (!evt) { + printk(KERN_ERR "%s: mp_zone_get_dm_evt %d\n", + __FUNCTION__, err); + err = -ENOMEM; + goto out; + } + + nlh = nlmsg_put(evt->skb, dm_nl_daemon_pid, 0, DM_EVT, + NLMSG_ALIGN(sizeof(*dm_nlh)), 0); + if (!nlh) + goto nla_put_failure; + + dm_nlh = nlmsg_data(nlh); + dm_nlh->type = type; + dm_nlh->version = DM_E_ATTR_MAX; + + spin_lock(&sequence_lock); + seq = ++dm_evt_seqnum; + spin_unlock(&sequence_lock); + do_gettimeofday(&tv); + + NLA_PUT_U64(evt->skb, DM_E_ATTR_SEQNUM, seq); + NLA_PUT_U64(evt->skb, DM_E_ATTR_TSSEC, tv.tv_sec); + NLA_PUT_U64(evt->skb, DM_E_ATTR_TSUSEC, tv.tv_usec); + NLA_PUT_STRING(evt->skb, DM_E_ATTR_DMNAME, dm_name); + + if (blk_err) + NLA_PUT_U32(evt->skb, DM_E_ATTR_BLKERR, blk_err); + + nlmsg_end(evt->skb, nlh); + + return evt; + +nla_put_failure: + printk(KERN_ERR "%s: nla_put_failure\n", + __FUNCTION__); + /* reduce skb users so zone_complete can free */ + kfree_skb(evt->skb); + mp_zone_complete(&z_dm_evt, 0); +out: + return ERR_PTR(err); + +} + +void dm_send_evt(struct dm_evt *evt) +{ + int err; + + if (!dm_nl_sock || !dm_nl_daemon_pid) + return; + + err = nlmsg_unicast(dm_nl_sock, evt->skb, dm_nl_daemon_pid); + if (err < 0) + printk(KERN_ERR "%s: nlmsg_unicast %d\n", __FUNCTION__, + err); +} +EXPORT_SYMBOL(dm_send_evt); + +struct dm_evt *dm_path_fail_evt(char* dm_name, int blk_err) +{ + struct dm_evt *evt; + evt = dm_nl_build_path_msg(dm_name, DM_EVT_FAIL_PATH, blk_err); + + return evt; +} + +EXPORT_SYMBOL(dm_path_fail_evt); + +struct dm_evt *dm_path_reinstate_evt(char* dm_name) +{ + struct dm_evt *evt; + evt = dm_nl_build_path_msg(dm_name, DM_EVT_REINSTATE_PATH, 0); + + return evt; +} + +EXPORT_SYMBOL(dm_path_reinstate_evt); + +#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) + +static void dm_nl_rcv_msg(struct sk_buff *skb) +{ + int pid, flags; + struct nlmsghdr *nlh = (struct nlmsghdr *) skb->data; + + if (skb->len >= NLMSG_SPACE(0)) { + + if (nlh->nlmsg_len < sizeof(*nlh) || + skb->len < nlh->nlmsg_len) { + return; + } + pid = nlh->nlmsg_pid; + flags = nlh->nlmsg_flags; + + if (security_netlink_recv(skb)) + RCV_SKB_FAIL(-EPERM); + + if (dm_nl_daemon_pid) { + if (dm_nl_daemon_pid != pid) { + RCV_SKB_FAIL(-EBUSY); + } + } else { + dm_nl_daemon_pid = pid; + } + + if (flags & NLM_F_ACK) + netlink_ack(skb, nlh, 0); + } +} + +static void dm_nl_rcv(struct sock *sk, int len) +{ + struct sk_buff *skb; + unsigned int qlen; + + for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) { + skb = skb_dequeue(&sk->sk_receive_queue); + dm_nl_rcv_msg(skb); + kfree_skb(skb); + } +} + +static int dm_nl_rcv_nl_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct netlink_notify *n = ptr; + + if (event == NETLINK_URELEASE && + n->protocol == NETLINK_DM && n->pid) { + if ( n->pid == dm_nl_daemon_pid ) { + dm_nl_daemon_pid = 0; + } + mp_zone_complete(&z_dm_evt, 1); + } + + return NOTIFY_DONE; } +static struct notifier_block dm_nl_nl_notifier = { + .notifier_call = dm_nl_rcv_nl_event, +}; + +static struct sock *dm_nl_sock; +static int dm_nl_daemon_pid; + int __init dm_nl_init(void) { int err; - err = mempool_zone_init(&z_dm_evt, EVT_SKB_SIZE, + err = netlink_register_notifier(&dm_nl_nl_notifier); + if (err) + return err; + + dm_nl_sock = netlink_kernel_create(NETLINK_DM, 0, + dm_nl_rcv, THIS_MODULE); + if (!dm_nl_sock) { + err = -ENOBUFS; + goto notifier_out; + } + + z_dm_evt.cache = kmem_cache_create("dm_events", + sizeof(struct dm_evt), 0, 0, NULL, NULL); + if (!z_dm_evt.cache) + goto socket_out; + + err = mp_zone_init(&z_dm_evt, EVT_SKB_SIZE, MIN_EVT_SKBS, HIWAT_EVT_SKBS); - if (!err) - printk(KERN_DEBUG "dm-netlink version 0.0.1 loaded\n"); + if (err) + goto cache_out; + + printk(KERN_DEBUG "dm-netlink version 0.0.2 loaded\n"); return err; +cache_out: + kmem_cache_destroy(z_dm_evt.cache); +socket_out: + sock_release(dm_nl_sock->sk_socket); +notifier_out: + netlink_unregister_notifier(&dm_nl_nl_notifier); + printk(KERN_ERR "%s: failed %d\n", __FUNCTION__, err); + return err; } void dm_nl_exit(void) { + flush_scheduled_work(); mempool_destroy(z_dm_evt.pool); + kmem_cache_destroy(z_dm_evt.cache); + sock_release(dm_nl_sock->sk_socket); + netlink_unregister_notifier(&dm_nl_nl_notifier); } Index: sas-2.6-patched/include/linux/dm-netlink.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ sas-2.6-patched/include/linux/dm-netlink.h 2006-01-15 17:40:31.000000000 -0800 @@ -0,0 +1,46 @@ +/* + * Device Mapper Netlink Support + * + * Copyright (C) 2005 IBM Corporation + * Author: Mike Anderson <andmike@xxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ +#ifndef LINUX_DM_NETLINK_H +#define LINUX_DM_NETLINK_H + +enum dm_evt_attr { + DM_E_ATTR_SEQNUM = 1, + DM_E_ATTR_TSSEC = 2, + DM_E_ATTR_TSUSEC = 3, + DM_E_ATTR_DMNAME = 4, + DM_E_ATTR_BLKERR = 5, + DM_E_ATTR_MAX, +}; + +#define DM_EVT NLMSG_MIN_TYPE + 0x1 + +#define DM_EVT_FAIL_PATH 0x1 +#define DM_EVT_REINSTATE_PATH 0x2 + +struct dm_nl_msghdr { + uint16_t type; + uint16_t version; + uint16_t reserved1; + uint16_t reserved2; +} __attribute__((aligned(sizeof(uint64_t)))); + +#endif /* LINUX_DM_NETLINK_H */ Index: sas-2.6-patched/drivers/md/dm-netlink.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ sas-2.6-patched/drivers/md/dm-netlink.h 2006-01-15 17:42:56.000000000 -0800 @@ -0,0 +1,58 @@ +/* + * Device Mapper Netlink Support + * + * Copyright (C) 2005 IBM Corporation + * Author: Mike Anderson <andmike@xxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ +#ifndef DM_NETLINK_H +#define DM_NETLINK_H + +struct dm_evt { + struct list_head zlist; + struct list_head elist; + struct sk_buff *skb; +}; + +#ifdef CONFIG_DM_NL_EVT +void dm_send_evt(struct dm_evt *); +struct dm_evt *dm_path_fail_evt(char*, int); +struct dm_evt *dm_path_reinstate_evt(char*); +int dm_nl_init(void); +void dm_nl_exit(void); +#else +static inline void dm_send_evt(struct dm_evt *evt) +{ +} +static inline struct dm_evt *dm_path_fail_evt(char* dm_name, int blk_err) +{ + return NULL; +} +static inline struct dm_evt *dm_path_reinstate_evt(char* dm_name) +{ + return NULL; +} +static inline int __init dm_nl_init(void) +{ + return 0; +} +static inline void dm_nl_exit(void) +{ +} +#endif + +#endif /* DM_NETLINK_H */ -- dm-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/dm-devel