Files
scst/iscsi-scst/kernel/event.c
Vladislav Bolkhovitin bc7c69bfc4 - Fixed a major IET-derived iSCSI RFC violation: sessions and connections were not working
- A lot of cleanups and minor fixes, mostly IET-derived



git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@685 d57e44dd-8a1f-0410-8b47-8ef2f437770f
2009-03-03 18:13:23 +00:00

152 lines
3.3 KiB
C

/*
* Event notification code.
*
* Copyright (C) 2005 FUJITA Tomonori <tomof@acm.org>
* Copyright (C) 2007 - 2008 Vladislav Bolkhovitin
* Copyright (C) 2007 - 2008 CMS Distribution Limited
*
* 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.
*
* 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.
*
* Some functions are based on audit code.
*/
#include <net/tcp.h>
#include "iscsi_scst.h"
#include "iscsi.h"
static struct sock *nl;
static u32 iscsid_pid;
static int event_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
u32 uid, pid, seq;
char *data;
pid = NETLINK_CREDS(skb)->pid;
uid = NETLINK_CREDS(skb)->uid;
seq = nlh->nlmsg_seq;
data = NLMSG_DATA(nlh);
iscsid_pid = pid;
return 0;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
static int event_recv_skb(struct sk_buff *skb)
#else
static void event_recv_skb(struct sk_buff *skb)
#endif
{
int err;
struct nlmsghdr *nlh;
u32 rlen;
while (skb->len >= NLMSG_SPACE(0)) {
nlh = (struct nlmsghdr *)skb->data;
if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
goto out;
rlen = NLMSG_ALIGN(nlh->nlmsg_len);
if (rlen > skb->len)
rlen = skb->len;
err = event_recv_msg(skb, nlh);
if (err)
netlink_ack(skb, nlh, -err);
else if (nlh->nlmsg_flags & NLM_F_ACK)
netlink_ack(skb, nlh, 0);
skb_pull(skb, rlen);
}
out:
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
return 0;
#else
return;
#endif
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
static void event_recv(struct sock *sk, int length)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
if (event_recv_skb(skb) && skb->len)
skb_queue_head(&sk->sk_receive_queue, skb);
else
kfree_skb(skb);
}
}
#endif
static int notify(void *data, int len, gfp_t gfp_mask)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
static u32 seq;
skb = alloc_skb(NLMSG_SPACE(len), gfp_mask);
if (!skb)
return -ENOMEM;
nlh = __nlmsg_put(skb, iscsid_pid, seq++, NLMSG_DONE,
len - sizeof(*nlh), 0);
memcpy(NLMSG_DATA(nlh), data, len);
return netlink_unicast(nl, skb, iscsid_pid, 0);
}
int event_send(u32 tid, u64 sid, u32 cid, u32 state, int atomic)
{
int err;
struct iscsi_kern_event event;
event.tid = tid;
event.sid = sid;
event.cid = cid;
event.state = state;
err = notify(&event, NLMSG_SPACE(sizeof(struct iscsi_kern_event)), 0);
return err;
}
int __init event_init(void)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22))
nl = netlink_kernel_create(NETLINK_ISCSI_SCST, 1, event_recv,
THIS_MODULE);
#else
# if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
nl = netlink_kernel_create(NETLINK_ISCSI_SCST, 1, event_recv, NULL,
THIS_MODULE);
# else
nl = netlink_kernel_create(&init_net, NETLINK_ISCSI_SCST, 1,
event_recv_skb, NULL, THIS_MODULE);
# endif
#endif
if (!nl) {
PRINT_ERROR("%s", "netlink_kernel_create() failed");
return -ENOMEM;
} else
return 0;
}
void event_exit(void)
{
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24))
if (nl)
sock_release(nl->sk_socket);
#else
netlink_kernel_release(nl);
#endif
}