net: dcbnl, add multicast group for DCB
[pandora-kernel.git] / net / dcb / dcbnl.c
index 3a6d97d..ffba326 100644 (file)
@@ -1166,64 +1166,6 @@ err:
        return ret;
 }
 
-/* Handle IEEE 802.1Qaz SET commands. If any requested operation can not
- * be completed the entire msg is aborted and error value is returned.
- * No attempt is made to reconcile the case where only part of the
- * cmd can be completed.
- */
-static int dcbnl_ieee_set(struct net_device *netdev, struct nlattr **tb,
-                         u32 pid, u32 seq, u16 flags)
-{
-       const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
-       struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1];
-       int err = -EOPNOTSUPP;
-
-       if (!ops)
-               goto err;
-
-       err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX,
-                              tb[DCB_ATTR_IEEE], dcbnl_ieee_policy);
-       if (err)
-               goto err;
-
-       if (ieee[DCB_ATTR_IEEE_ETS] && ops->ieee_setets) {
-               struct ieee_ets *ets = nla_data(ieee[DCB_ATTR_IEEE_ETS]);
-               err = ops->ieee_setets(netdev, ets);
-               if (err)
-                       goto err;
-       }
-
-       if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) {
-               struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]);
-               err = ops->ieee_setpfc(netdev, pfc);
-               if (err)
-                       goto err;
-       }
-
-       if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
-               struct nlattr *attr;
-               int rem;
-
-               nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
-                       struct dcb_app *app_data;
-                       if (nla_type(attr) != DCB_ATTR_IEEE_APP)
-                               continue;
-                       app_data = nla_data(attr);
-                       if (ops->ieee_setapp)
-                               err = ops->ieee_setapp(netdev, app_data);
-                       else
-                               err = dcb_setapp(netdev, app_data);
-                       if (err)
-                               goto err;
-               }
-       }
-
-err:
-       dcbnl_reply(err, RTM_SETDCB, DCB_CMD_IEEE_SET, DCB_ATTR_IEEE,
-                   pid, seq, flags);
-       return err;
-}
-
 static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb,
                                int app_nested_type, int app_info_type,
                                int app_entry_type)
@@ -1279,30 +1221,13 @@ nla_put_failure:
 }
 
 /* Handle IEEE 802.1Qaz GET commands. */
-static int dcbnl_ieee_get(struct net_device *netdev, struct nlattr **tb,
-                         u32 pid, u32 seq, u16 flags)
+static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
 {
-       struct sk_buff *skb;
-       struct nlmsghdr *nlh;
-       struct dcbmsg *dcb;
        struct nlattr *ieee, *app;
        struct dcb_app_type *itr;
        const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
        int dcbx;
-       int err;
-
-       if (!ops)
-               return -EOPNOTSUPP;
-
-       skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!skb)
-               return -ENOBUFS;
-
-       nlh = NLMSG_NEW(skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
-
-       dcb = NLMSG_DATA(nlh);
-       dcb->dcb_family = AF_UNSPEC;
-       dcb->cmd = DCB_CMD_IEEE_GET;
+       int err = -EMSGSIZE;
 
        NLA_PUT_STRING(skb, DCB_ATTR_IFNAME, netdev->name);
 
@@ -1378,16 +1303,154 @@ static int dcbnl_ieee_get(struct net_device *netdev, struct nlattr **tb,
                if (err)
                        goto nla_put_failure;
        }
-       nlmsg_end(skb, nlh);
 
-       return rtnl_unicast(skb, &init_net, pid);
+       return 0;
+
 nla_put_failure:
-       nlmsg_cancel(skb, nlh);
-nlmsg_failure:
-       kfree_skb(skb);
-       return -1;
+       return err;
 }
 
+int dcbnl_notify(struct net_device *dev, int event, int cmd,
+                       u32 seq, u32 pid)
+{
+       struct net *net = dev_net(dev);
+       struct sk_buff *skb;
+       struct nlmsghdr *nlh;
+       struct dcbmsg *dcb;
+       const struct dcbnl_rtnl_ops *ops = dev->dcbnl_ops;
+       int err;
+
+       if (!ops)
+               return -EOPNOTSUPP;
+
+       skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!skb)
+               return -ENOBUFS;
+
+       nlh = nlmsg_put(skb, pid, 0, event, sizeof(*dcb), 0);
+       if (nlh == NULL) {
+               kfree(skb);
+               return -EMSGSIZE;
+       }
+
+       dcb = NLMSG_DATA(nlh);
+       dcb->dcb_family = AF_UNSPEC;
+       dcb->cmd = cmd;
+
+       err = dcbnl_ieee_fill(skb, dev);
+       if (err < 0) {
+               /* Report error to broadcast listeners */
+               nlmsg_cancel(skb, nlh);
+               kfree_skb(skb);
+               rtnl_set_sk_err(net, RTNLGRP_DCB, err);
+       } else {
+               /* End nlmsg and notify broadcast listeners */
+               nlmsg_end(skb, nlh);
+               rtnl_notify(skb, net, 0, RTNLGRP_DCB, NULL, GFP_KERNEL);
+       }
+
+       return err;
+}
+EXPORT_SYMBOL(dcbnl_notify);
+
+/* Handle IEEE 802.1Qaz SET commands. If any requested operation can not
+ * be completed the entire msg is aborted and error value is returned.
+ * No attempt is made to reconcile the case where only part of the
+ * cmd can be completed.
+ */
+static int dcbnl_ieee_set(struct net_device *netdev, struct nlattr **tb,
+                         u32 pid, u32 seq, u16 flags)
+{
+       const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
+       struct nlattr *ieee[DCB_ATTR_IEEE_MAX + 1];
+       int err = -EOPNOTSUPP;
+
+       if (!ops)
+               return err;
+
+       err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX,
+                              tb[DCB_ATTR_IEEE], dcbnl_ieee_policy);
+       if (err)
+               return err;
+
+       if (ieee[DCB_ATTR_IEEE_ETS] && ops->ieee_setets) {
+               struct ieee_ets *ets = nla_data(ieee[DCB_ATTR_IEEE_ETS]);
+               err = ops->ieee_setets(netdev, ets);
+               if (err)
+                       goto err;
+       }
+
+       if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) {
+               struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]);
+               err = ops->ieee_setpfc(netdev, pfc);
+               if (err)
+                       goto err;
+       }
+
+       if (ieee[DCB_ATTR_IEEE_APP_TABLE]) {
+               struct nlattr *attr;
+               int rem;
+
+               nla_for_each_nested(attr, ieee[DCB_ATTR_IEEE_APP_TABLE], rem) {
+                       struct dcb_app *app_data;
+                       if (nla_type(attr) != DCB_ATTR_IEEE_APP)
+                               continue;
+                       app_data = nla_data(attr);
+                       if (ops->ieee_setapp)
+                               err = ops->ieee_setapp(netdev, app_data);
+                       else
+                               err = dcb_setapp(netdev, app_data);
+                       if (err)
+                               goto err;
+               }
+       }
+
+err:
+       dcbnl_reply(err, RTM_SETDCB, DCB_CMD_IEEE_SET, DCB_ATTR_IEEE,
+                   pid, seq, flags);
+       dcbnl_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_SET, seq, 0);
+       return err;
+}
+
+static int dcbnl_ieee_get(struct net_device *netdev, struct nlattr **tb,
+                         u32 pid, u32 seq, u16 flags)
+{
+       struct net *net = dev_net(netdev);
+       struct sk_buff *skb;
+       struct nlmsghdr *nlh;
+       struct dcbmsg *dcb;
+       const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops;
+       int err;
+
+       if (!ops)
+               return -EOPNOTSUPP;
+
+       skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!skb)
+               return -ENOBUFS;
+
+       nlh = nlmsg_put(skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
+       if (nlh == NULL) {
+               kfree(skb);
+               return -EMSGSIZE;
+       }
+
+       dcb = NLMSG_DATA(nlh);
+       dcb->dcb_family = AF_UNSPEC;
+       dcb->cmd = DCB_CMD_IEEE_GET;
+
+       err = dcbnl_ieee_fill(skb, netdev);
+
+       if (err < 0) {
+               nlmsg_cancel(skb, nlh);
+               kfree_skb(skb);
+       } else {
+               nlmsg_end(skb, nlh);
+               err = rtnl_unicast(skb, net, pid);
+       }
+
+       return err;
+}
 /* DCBX configuration */
 static int dcbnl_getdcbx(struct net_device *netdev, struct nlattr **tb,
                         u32 pid, u32 seq, u16 flags)