connector/cn_proc: Add filtering to fix some bugs
[linux-2.6-microblaze.git] / drivers / connector / cn_proc.c
index ccac1c4..1ba288e 100644 (file)
@@ -48,6 +48,21 @@ static DEFINE_PER_CPU(struct local_event, local_event) = {
        .lock = INIT_LOCAL_LOCK(lock),
 };
 
+static int cn_filter(struct sock *dsk, struct sk_buff *skb, void *data)
+{
+       enum proc_cn_mcast_op mc_op;
+
+       if (!dsk)
+               return 0;
+
+       mc_op = ((struct proc_input *)(dsk->sk_user_data))->mcast_op;
+
+       if (mc_op == PROC_CN_MCAST_IGNORE)
+               return 1;
+
+       return 0;
+}
+
 static inline void send_msg(struct cn_msg *msg)
 {
        local_lock(&local_event.lock);
@@ -61,7 +76,8 @@ static inline void send_msg(struct cn_msg *msg)
         *
         * If cn_netlink_send() fails, the data is not sent.
         */
-       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_NOWAIT);
+       cn_netlink_send_mult(msg, msg->len, 0, CN_IDX_PROC, GFP_NOWAIT,
+                            cn_filter, NULL);
 
        local_unlock(&local_event.lock);
 }
@@ -346,11 +362,9 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
 static void cn_proc_mcast_ctl(struct cn_msg *msg,
                              struct netlink_skb_parms *nsp)
 {
-       enum proc_cn_mcast_op *mc_op = NULL;
-       int err = 0;
-
-       if (msg->len != sizeof(*mc_op))
-               return;
+       enum proc_cn_mcast_op mc_op = 0, prev_mc_op = 0;
+       int err = 0, initial = 0;
+       struct sock *sk = NULL;
 
        /* 
         * Events are reported with respect to the initial pid
@@ -367,13 +381,36 @@ static void cn_proc_mcast_ctl(struct cn_msg *msg,
                goto out;
        }
 
-       mc_op = (enum proc_cn_mcast_op *)msg->data;
-       switch (*mc_op) {
+       if (msg->len == sizeof(mc_op))
+               mc_op = *((enum proc_cn_mcast_op *)msg->data);
+       else
+               return;
+
+       if (nsp->sk) {
+               sk = nsp->sk;
+               if (sk->sk_user_data == NULL) {
+                       sk->sk_user_data = kzalloc(sizeof(struct proc_input),
+                                                  GFP_KERNEL);
+                       if (sk->sk_user_data == NULL) {
+                               err = ENOMEM;
+                               goto out;
+                       }
+                       initial = 1;
+               } else {
+                       prev_mc_op =
+                       ((struct proc_input *)(sk->sk_user_data))->mcast_op;
+               }
+               ((struct proc_input *)(sk->sk_user_data))->mcast_op = mc_op;
+       }
+
+       switch (mc_op) {
        case PROC_CN_MCAST_LISTEN:
-               atomic_inc(&proc_event_num_listeners);
+               if (initial || (prev_mc_op != PROC_CN_MCAST_LISTEN))
+                       atomic_inc(&proc_event_num_listeners);
                break;
        case PROC_CN_MCAST_IGNORE:
-               atomic_dec(&proc_event_num_listeners);
+               if (!initial && (prev_mc_op != PROC_CN_MCAST_IGNORE))
+                       atomic_dec(&proc_event_num_listeners);
                break;
        default:
                err = EINVAL;