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 drivers/md/dm-netlink.c | 180 ++++++++++++++++++++++++++++++++++++++++++++- drivers/md/dm-netlink.h | 25 ++++++ drivers/md/dm.c | 26 ++++++ drivers/md/dm.h | 3 include/linux/dm-netlink.h | 50 ++++++++++++ 5 files changed, 282 insertions(+), 2 deletions(-) Index: linux-2.6.18-rc7/drivers/md/dm-netlink.c =================================================================== --- linux-2.6.18-rc7.orig/drivers/md/dm-netlink.c 2006-10-13 17:10:28.000000000 +0100 +++ linux-2.6.18-rc7/drivers/md/dm-netlink.c 2006-10-13 17:10:33.000000000 +0100 @@ -27,7 +27,9 @@ #include #include #include +#include "dm.h" #include "dm-netlink.h" +#include #define MIN_EVENT_SKBS 16 #define HIWAT_EVENT_SKBS 32 @@ -151,14 +153,181 @@ static struct dm_event* mp_zone_get_dm_e return evt; } +static struct sock *dm_netlink_sock; +static int dm_netlink_daemon_pid; + +static u64 dm_event_seqnum; +static DEFINE_SPINLOCK(sequence_lock); + +static struct dm_event *dm_netlink_build_path_msg(char* dm_name, int type, int + nr_valid_paths) +{ + struct dm_event *evt; + struct nlmsghdr *nlh; + struct dm_netlink_msghdr *dm_nlh; + u64 seq; + struct timeval tv; + int err = -ENOBUFS; + + evt = mp_zone_get_dm_event(&z_dm_event); + if (!evt) { + printk(KERN_ERR "%s: mp_zone_get_dm_event %d\n", + __FUNCTION__, err); + err = -ENOMEM; + goto out; + } + + nlh = nlmsg_put(evt->skb, dm_netlink_daemon_pid, 0, DM_EVENT, + 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_event_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); + NLA_PUT_U32(evt->skb, DM_E_ATTR_VLDPTHS, nr_valid_paths); + + return evt; + +nla_put_failure: + printk(KERN_ERR "%s: nla_put_failure\n", __FUNCTION__); + /* Set skb users so zone_complete can free */ + atomic_set(&evt->skb->users, 1); + mp_zone_complete(&z_dm_event, 0); +out: + return ERR_PTR(err); + +} + +void dm_send_event(char *md_name, struct dm_event *evt) +{ + struct nlmsghdr *nlh = (struct nlmsghdr *) evt->skb->data; + int err; + + NLA_PUT_STRING(evt->skb, DM_E_ATTR_MDNAME, md_name); + nlmsg_end(evt->skb, nlh); + + err = nlmsg_unicast(dm_netlink_sock, evt->skb, nlh->nlmsg_pid); + + if (err < 0) { + printk(KERN_ERR "%s: nlmsg_unicast failed %d\n", + __FUNCTION__, err); + goto unicast_err; + } + + return; + +nla_put_failure: + printk(KERN_ERR "%s: nla_put_failure\n", __FUNCTION__); +unicast_err: + /* Set skb users so zone_complete can free */ + atomic_set(&evt->skb->users, 1); + mp_zone_complete(&z_dm_event, 0); +} +EXPORT_SYMBOL(dm_send_event); + +void dm_path_event(enum dm_event_type evt_type, struct dm_table *t, + char* dm_name, int nr_valid_paths) +{ + struct dm_event *evt; + evt = dm_netlink_build_path_msg(dm_name, evt_type, nr_valid_paths); + + if (evt) { + struct mapped_device *md = dm_table_get_md(t); + dm_add_event(md, evt); + dm_put(md); + } + + return; +} + +EXPORT_SYMBOL(dm_path_event); + +static int dm_netlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, + int *errp) +{ + if (security_netlink_recv(skb)) { + *errp = -EPERM; + goto err_out; + } + + if (dm_netlink_daemon_pid) { + if (dm_netlink_daemon_pid != nlh->nlmsg_pid) { + *errp = -EBUSY; + goto err_out; + } + } else { + dm_netlink_daemon_pid = nlh->nlmsg_pid; + } + + return 0; + +err_out: + return -1; +} + +static void dm_netlink_rcv(struct sock *sk, int len) +{ + unsigned int qlen; + + do { + netlink_run_queue(sk, &qlen, &dm_netlink_rcv_msg); + } while (qlen); + +} + +static int dm_netlink_rcv_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_netlink_daemon_pid ) { + mp_zone_complete(&z_dm_event, + dm_netlink_daemon_pid); + dm_netlink_daemon_pid = 0; + } + } + + return NOTIFY_DONE; + } + +static struct notifier_block dm_netlink_notifier = { + .notifier_call = dm_netlink_rcv_event, +}; + +static struct sock *dm_netlink_sock; +static int dm_netlink_daemon_pid; + int __init dm_netlink_init(void) { int err; + err = netlink_register_notifier(&dm_netlink_notifier); + if (err) + return err; + + dm_netlink_sock = netlink_kernel_create(NETLINK_DM, 0, + dm_netlink_rcv, THIS_MODULE); + if (!dm_netlink_sock) { + err = -ENOBUFS; + goto notifier_out; + } + z_dm_event.cache = kmem_cache_create("dm_events", sizeof(struct dm_event), 0, 0, NULL, NULL); if (!z_dm_event.cache) - goto out; + goto socket_out; err = mp_zone_init(&z_dm_event, EVENT_SKB_SIZE, MIN_EVENT_SKBS, HIWAT_EVENT_SKBS); @@ -168,9 +337,14 @@ int __init dm_netlink_init(void) printk(KERN_DEBUG "dm-netlink version 0.0.5 loaded\n"); return err; + cache_out: kmem_cache_destroy(z_dm_event.cache); -out: +socket_out: + sock_release(dm_netlink_sock->sk_socket); +notifier_out: + netlink_unregister_notifier(&dm_netlink_notifier); + printk(KERN_ERR "%s: failed %d\n", __FUNCTION__, err); return err; } @@ -178,4 +352,6 @@ void dm_netlink_exit(void) { mempool_destroy(z_dm_event.pool); kmem_cache_destroy(z_dm_event.cache); + sock_release(dm_netlink_sock->sk_socket); + netlink_unregister_notifier(&dm_netlink_notifier); } Index: linux-2.6.18-rc7/drivers/md/dm-netlink.h =================================================================== --- linux-2.6.18-rc7.orig/drivers/md/dm-netlink.h 2006-10-13 17:10:28.000000000 +0100 +++ linux-2.6.18-rc7/drivers/md/dm-netlink.h 2006-10-13 17:10:33.000000000 +0100 @@ -22,10 +22,35 @@ #ifndef DM_NETLINK_H #define DM_NETLINK_H +#include + struct dm_event { struct list_head zlist; struct list_head elist; struct sk_buff *skb; }; +#ifdef CONFIG_DM_NETLINK_EVENT +void dm_send_event(char*, struct dm_event *); +void dm_path_event(enum dm_event_type evt, struct dm_table*, char*, int); +int dm_netlink_init(void); +void dm_netlink_exit(void); +#else +static inline void dm_send_event(char *md_name, struct dm_event *evt) +{ +} +static inline void dm_path_event(enum dm_event_type evt, + struct dm_table *t, char* dm_name, + int nr_valid_paths) +{ +} +static inline int __init dm_netlink_init(void) +{ + return 0; +} +static inline void dm_netlink_exit(void) +{ +} +#endif + #endif /* DM_NETLINK_H */ Index: linux-2.6.18-rc7/include/linux/dm-netlink.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.18-rc7/include/linux/dm-netlink.h 2006-10-13 17:10:33.000000000 +0100 @@ -0,0 +1,50 @@ +/* + * Device Mapper Netlink Support + * + * Copyright (C) 2005 IBM Corporation + * Author: Mike Anderson + * + * 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 + +#define DM_EVENT NLMSG_MIN_TYPE + 0x1 + +enum dm_event_type { + DM_EVENT_FAIL_PATH = 1, + DM_EVENT_REINSTATE_PATH = 2, + DM_EVENT_MAX, +}; + +enum dm_event_attr { + DM_E_ATTR_SEQNUM = 1, + DM_E_ATTR_TSSEC = 2, + DM_E_ATTR_TSUSEC = 3, + DM_E_ATTR_DMNAME = 4, + DM_E_ATTR_VLDPTHS = 5, + DM_E_ATTR_MDNAME = 6, + DM_E_ATTR_MAX, +}; + +struct dm_netlink_msghdr { + uint16_t type; + uint16_t version; + uint16_t reserved1; + uint16_t reserved2; +} __attribute__((aligned(sizeof(uint64_t)))); + +#endif /* LINUX_DM_NETLINK_H */ Index: linux-2.6.18-rc7/drivers/md/dm.c =================================================================== --- linux-2.6.18-rc7.orig/drivers/md/dm.c 2006-10-13 17:10:28.000000000 +0100 +++ linux-2.6.18-rc7/drivers/md/dm.c 2006-10-13 17:10:33.000000000 +0100 @@ -112,6 +112,8 @@ struct mapped_device { */ atomic_t event_nr; wait_queue_head_t eventq; + struct list_head event_list; + spinlock_t event_lock; /* * freeze/thaw support require holding onto a super block @@ -993,6 +995,8 @@ static struct mapped_device *alloc_dev(i atomic_set(&md->holders, 1); atomic_set(&md->open_count, 0); atomic_set(&md->event_nr, 0); + INIT_LIST_HEAD(&md->event_list); + spin_lock_init(&md->event_lock); md->queue = blk_alloc_queue(GFP_KERNEL); if (!md->queue) @@ -1091,6 +1095,18 @@ static void free_dev(struct mapped_devic static void event_callback(void *context) { struct mapped_device *md = (struct mapped_device *) context; + unsigned long flags; + struct dm_event *evt, *next; + LIST_HEAD(events); + + spin_lock_irqsave(&md->event_lock, flags); + list_splice_init(&md->event_list, &events); + spin_unlock_irqrestore(&md->event_lock, flags); + + list_for_each_entry_safe(evt, next, &events, elist) { + list_del_init(&evt->elist); + dm_send_event(md->name, evt); + } atomic_inc(&md->event_nr); wake_up(&md->eventq); @@ -1505,6 +1521,16 @@ int dm_wait_event(struct mapped_device * (event_nr != atomic_read(&md->event_nr))); } +void dm_add_event(struct mapped_device *md, struct dm_event *evt) +{ + unsigned long flags; + + spin_lock_irqsave(&md->event_lock, flags); + list_add(&evt->elist, &md->event_list); + spin_unlock_irqrestore(&md->event_lock, flags); + +} + /* * The gendisk is only valid as long as you have a reference * count on 'md'. Index: linux-2.6.18-rc7/drivers/md/dm.h =================================================================== --- linux-2.6.18-rc7.orig/drivers/md/dm.h 2006-10-13 17:10:19.000000000 +0100 +++ linux-2.6.18-rc7/drivers/md/dm.h 2006-10-13 17:10:33.000000000 +0100 @@ -15,6 +15,7 @@ #include #include #include +#include "dm-netlink.h" #define DM_NAME "device-mapper" @@ -151,4 +152,6 @@ union map_info *dm_get_mapinfo(struct bi int dm_open_count(struct mapped_device *md); int dm_lock_for_deletion(struct mapped_device *md); +void dm_add_event(struct mapped_device *md, struct dm_event *evt); + #endif