net: openvswitch: make masks cache size configurable
[linux-2.6-block.git] / net / openvswitch / datapath.c
index 94b024534987a02428e7bd579e531958ca4b190b..42f8cc70bb2cb7790f4e48fc8353e9c340beb2c6 100644 (file)
@@ -130,6 +130,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *,
                                  const struct dp_upcall_info *,
                                  uint32_t cutlen);
 
+static void ovs_dp_masks_rebalance(struct work_struct *work);
+
 /* Must be called with rcu_read_lock or ovs_mutex. */
 const char *ovs_dp_name(const struct datapath *dp)
 {
@@ -223,13 +225,14 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
        struct dp_stats_percpu *stats;
        u64 *stats_counter;
        u32 n_mask_hit;
+       u32 n_cache_hit;
        int error;
 
        stats = this_cpu_ptr(dp->stats_percpu);
 
        /* Look up flow. */
        flow = ovs_flow_tbl_lookup_stats(&dp->table, key, skb_get_hash(skb),
-                                        &n_mask_hit);
+                                        &n_mask_hit, &n_cache_hit);
        if (unlikely(!flow)) {
                struct dp_upcall_info upcall;
 
@@ -260,6 +263,7 @@ out:
        u64_stats_update_begin(&stats->syncp);
        (*stats_counter)++;
        stats->n_mask_hit += n_mask_hit;
+       stats->n_cache_hit += n_cache_hit;
        u64_stats_update_end(&stats->syncp);
 }
 
@@ -697,6 +701,7 @@ static void get_dp_stats(const struct datapath *dp, struct ovs_dp_stats *stats,
                stats->n_missed += local_stats.n_missed;
                stats->n_lost += local_stats.n_lost;
                mega_stats->n_mask_hit += local_stats.n_mask_hit;
+               mega_stats->n_cache_hit += local_stats.n_cache_hit;
        }
 }
 
@@ -1493,6 +1498,7 @@ static size_t ovs_dp_cmd_msg_size(void)
        msgsize += nla_total_size_64bit(sizeof(struct ovs_dp_stats));
        msgsize += nla_total_size_64bit(sizeof(struct ovs_dp_megaflow_stats));
        msgsize += nla_total_size(sizeof(u32)); /* OVS_DP_ATTR_USER_FEATURES */
+       msgsize += nla_total_size(sizeof(u32)); /* OVS_DP_ATTR_MASKS_CACHE_SIZE */
 
        return msgsize;
 }
@@ -1530,6 +1536,10 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
        if (nla_put_u32(skb, OVS_DP_ATTR_USER_FEATURES, dp->user_features))
                goto nla_put_failure;
 
+       if (nla_put_u32(skb, OVS_DP_ATTR_MASKS_CACHE_SIZE,
+                       ovs_flow_tbl_masks_cache_size(&dp->table)))
+               goto nla_put_failure;
+
        genlmsg_end(skb, ovs_header);
        return 0;
 
@@ -1594,6 +1604,16 @@ static int ovs_dp_change(struct datapath *dp, struct nlattr *a[])
 #endif
        }
 
+       if (a[OVS_DP_ATTR_MASKS_CACHE_SIZE]) {
+               int err;
+               u32 cache_size;
+
+               cache_size = nla_get_u32(a[OVS_DP_ATTR_MASKS_CACHE_SIZE]);
+               err = ovs_flow_tbl_masks_cache_resize(&dp->table, cache_size);
+               if (err)
+                       return err;
+       }
+
        dp->user_features = user_features;
 
        if (dp->user_features & OVS_DP_F_TC_RECIRC_SHARING)
@@ -1882,6 +1902,8 @@ static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = {
        [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
        [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 },
        [OVS_DP_ATTR_USER_FEATURES] = { .type = NLA_U32 },
+       [OVS_DP_ATTR_MASKS_CACHE_SIZE] =  NLA_POLICY_RANGE(NLA_U32, 0,
+               PCPU_MIN_UNIT_SIZE / sizeof(struct mask_cache_entry)),
 };
 
 static const struct genl_ops dp_datapath_genl_ops[] = {
@@ -2338,6 +2360,23 @@ out:
        return skb->len;
 }
 
+static void ovs_dp_masks_rebalance(struct work_struct *work)
+{
+       struct ovs_net *ovs_net = container_of(work, struct ovs_net,
+                                              masks_rebalance.work);
+       struct datapath *dp;
+
+       ovs_lock();
+
+       list_for_each_entry(dp, &ovs_net->dps, list_node)
+               ovs_flow_masks_rebalance(&dp->table);
+
+       ovs_unlock();
+
+       schedule_delayed_work(&ovs_net->masks_rebalance,
+                             msecs_to_jiffies(DP_MASKS_REBALANCE_INTERVAL));
+}
+
 static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
        [OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
        [OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) },
@@ -2432,6 +2471,9 @@ static int __net_init ovs_init_net(struct net *net)
 
        INIT_LIST_HEAD(&ovs_net->dps);
        INIT_WORK(&ovs_net->dp_notify_work, ovs_dp_notify_wq);
+       INIT_DELAYED_WORK(&ovs_net->masks_rebalance, ovs_dp_masks_rebalance);
+       schedule_delayed_work(&ovs_net->masks_rebalance,
+                             msecs_to_jiffies(DP_MASKS_REBALANCE_INTERVAL));
        return ovs_ct_init(net);
 }
 
@@ -2486,6 +2528,7 @@ static void __net_exit ovs_exit_net(struct net *dnet)
 
        ovs_unlock();
 
+       cancel_delayed_work_sync(&ovs_net->masks_rebalance);
        cancel_work_sync(&ovs_net->dp_notify_work);
 }