877 lines · 23.1 KB · C
RawDownload
 int dev_map_enqueue(struct bpf_dtab_netdev *dst, struct xdp_buff *xdp,		    struct net_device *dev_rx){	struct net_device *dev = dst->dev; 	if (dst->xdp_prog) {		xdp = dev_map_run_prog(dev, xdp, dst->xdp_prog);		if (!xdp)			return 0;	}	return __xdp_enqueue(dev, xdp, dev_rx);} int dev_map_generic_redirect(struct bpf_dtab_netdev *dst, struct sk_buff *skb,			     struct bpf_prog *xdp_prog){	int err; 	err = xdp_ok_fwd_dev(dst->dev, skb->len);	if (unlikely(err))		return err;	skb->dev = dst->dev;	generic_xdp_tx(skb, xdp_prog); 	return 0;} static void *dev_map_lookup_elem(struct bpf_map *map, void *key){	struct bpf_dtab_netdev *obj = __dev_map_lookup_elem(map, *(u32 *)key); 	return obj ? &obj->val : NULL;} static void *dev_map_hash_lookup_elem(struct bpf_map *map, void *key){	struct bpf_dtab_netdev *obj = __dev_map_hash_lookup_elem(map,								*(u32 *)key);	return obj ? &obj->val : NULL;} static void __dev_map_entry_free(struct rcu_head *rcu){	struct bpf_dtab_netdev *dev; 	dev = container_of(rcu, struct bpf_dtab_netdev, rcu);	if (dev->xdp_prog)		bpf_prog_put(dev->xdp_prog);	dev_put(dev->dev);	kfree(dev);} static int dev_map_delete_elem(struct bpf_map *map, void *key){	struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);	struct bpf_dtab_netdev *old_dev;	int k = *(u32 *)key; 	if (k >= map->max_entries)		return -EINVAL; 	/* Use call_rcu() here to ensure any rcu critical sections have	 * completed as well as any flush operations because call_rcu	 * will wait for preempt-disable region to complete, NAPI in this	 * context.  And additionally, the driver tear down ensures all	 * soft irqs are complete before removing the net device in the	 * case of dev_put equals zero.	 */	old_dev = xchg(&dtab->netdev_map[k], NULL);	if (old_dev)		call_rcu(&old_dev->rcu, __dev_map_entry_free);	return 0;} static int dev_map_hash_delete_elem(struct bpf_map *map, void *key){	struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);	struct bpf_dtab_netdev *old_dev;	int k = *(u32 *)key;	unsigned long flags;	int ret = -ENOENT; 	spin_lock_irqsave(&dtab->index_lock, flags); 	old_dev = __dev_map_hash_lookup_elem(map, k);	if (old_dev) {		dtab->items--;		hlist_del_init_rcu(&old_dev->index_hlist);		call_rcu(&old_dev->rcu, __dev_map_entry_free);		ret = 0;	}	spin_unlock_irqrestore(&dtab->index_lock, flags); 	return ret;} static struct bpf_dtab_netdev *__dev_map_alloc_node(struct net *net,						    struct bpf_dtab *dtab,						    struct bpf_devmap_val *val,						    unsigned int idx){	struct bpf_prog *prog = NULL;	struct bpf_dtab_netdev *dev; 	dev = kmalloc_node(sizeof(*dev), GFP_ATOMIC | __GFP_NOWARN,			   dtab->map.numa_node);	if (!dev)		return ERR_PTR(-ENOMEM); 	dev->dev = dev_get_by_index(net, val->ifindex);	if (!dev->dev)		goto err_out; 	if (val->bpf_prog.fd > 0) {		prog = bpf_prog_get_type_dev(val->bpf_prog.fd,					     BPF_PROG_TYPE_XDP, false);		if (IS_ERR(prog))			goto err_put_dev;		if (prog->expected_attach_type != BPF_XDP_DEVMAP)			goto err_put_prog;	} 	dev->idx = idx;	dev->dtab = dtab;	if (prog) {		dev->xdp_prog = prog;		dev->val.bpf_prog.id = prog->aux->id;	} else {		dev->xdp_prog = NULL;		dev->val.bpf_prog.id = 0;	}	dev->val.ifindex = val->ifindex; 	return dev;err_put_prog:	bpf_prog_put(prog);err_put_dev:	dev_put(dev->dev);err_out:	kfree(dev);	return ERR_PTR(-EINVAL);} static int __dev_map_update_elem(struct net *net, struct bpf_map *map,				 void *key, void *value, u64 map_flags){	struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);	struct bpf_dtab_netdev *dev, *old_dev;	struct bpf_devmap_val val = {};	u32 i = *(u32 *)key; 	if (unlikely(map_flags > BPF_EXIST))		return -EINVAL;	if (unlikely(i >= dtab->map.max_entries))		return -E2BIG;	if (unlikely(map_flags == BPF_NOEXIST))		return -EEXIST; 	/* already verified value_size <= sizeof val */	memcpy(&val, value, map->value_size); 	if (!val.ifindex) {		dev = NULL;		/* can not specify fd if ifindex is 0 */		if (val.bpf_prog.fd > 0)			return -EINVAL;	} else {		dev = __dev_map_alloc_node(net, dtab, &val, i);		if (IS_ERR(dev))			return PTR_ERR(dev);	} 	/* Use call_rcu() here to ensure rcu critical sections have completed	 * Remembering the driver side flush operation will happen before the	 * net device is removed.	 */	old_dev = xchg(&dtab->netdev_map[i], dev);	if (old_dev)		call_rcu(&old_dev->rcu, __dev_map_entry_free); 	return 0;} static int dev_map_update_elem(struct bpf_map *map, void *key, void *value,			       u64 map_flags){	return __dev_map_update_elem(current->nsproxy->net_ns,				     map, key, value, map_flags);} static int __dev_map_hash_update_elem(struct net *net, struct bpf_map *map,				     void *key, void *value, u64 map_flags){	struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);	struct bpf_dtab_netdev *dev, *old_dev;	struct bpf_devmap_val val = {};	u32 idx = *(u32 *)key;	unsigned long flags;	int err = -EEXIST; 	/* already verified value_size <= sizeof val */	memcpy(&val, value, map->value_size); 	if (unlikely(map_flags > BPF_EXIST || !val.ifindex))		return -EINVAL; 	spin_lock_irqsave(&dtab->index_lock, flags); 	old_dev = __dev_map_hash_lookup_elem(map, idx);	if (old_dev && (map_flags & BPF_NOEXIST))		goto out_err; 	dev = __dev_map_alloc_node(net, dtab, &val, idx);	if (IS_ERR(dev)) {		err = PTR_ERR(dev);		goto out_err;	} 	if (old_dev) {		hlist_del_rcu(&old_dev->index_hlist);	} else {		if (dtab->items >= dtab->map.max_entries) {			spin_unlock_irqrestore(&dtab->index_lock, flags);			call_rcu(&dev->rcu, __dev_map_entry_free);			return -E2BIG;		}		dtab->items++;	} 	hlist_add_head_rcu(&dev->index_hlist,			   dev_map_index_hash(dtab, idx));	spin_unlock_irqrestore(&dtab->index_lock, flags); 	if (old_dev)		call_rcu(&old_dev->rcu, __dev_map_entry_free); 	return 0; out_err:	spin_unlock_irqrestore(&dtab->index_lock, flags);	return err;} static int dev_map_hash_update_elem(struct bpf_map *map, void *key, void *value,				   u64 map_flags){	return __dev_map_hash_update_elem(current->nsproxy->net_ns,					 map, key, value, map_flags);} static int dev_map_btf_id;const struct bpf_map_ops dev_map_ops = {	.map_alloc = dev_map_alloc,	.map_free = dev_map_free,	.map_get_next_key = dev_map_get_next_key,	.map_lookup_elem = dev_map_lookup_elem,	.map_update_elem = dev_map_update_elem,	.map_delete_elem = dev_map_delete_elem,	.map_check_btf = map_check_no_btf,	.map_btf_name = "bpf_dtab",	.map_btf_id = &dev_map_btf_id,}; static int dev_map_hash_map_btf_id;const struct bpf_map_ops dev_map_hash_ops = {	.map_alloc = dev_map_alloc,	.map_free = dev_map_free,	.map_get_next_key = dev_map_hash_get_next_key,	.map_lookup_elem = dev_map_hash_lookup_elem,	.map_update_elem = dev_map_hash_update_elem,	.map_delete_elem = dev_map_hash_delete_elem,	.map_check_btf = map_check_no_btf,	.map_btf_name = "bpf_dtab",	.map_btf_id = &dev_map_hash_map_btf_id,}; static void dev_map_hash_remove_netdev(struct bpf_dtab *dtab,				       struct net_device *netdev){	unsigned long flags;	u32 i; 	spin_lock_irqsave(&dtab->index_lock, flags);	for (i = 0; i < dtab->n_buckets; i++) {		struct bpf_dtab_netdev *dev;		struct hlist_head *head;		struct hlist_node *next; 		head = dev_map_index_hash(dtab, i); 		hlist_for_each_entry_safe(dev, next, head, index_hlist) {			if (netdev != dev->dev)				continue; 			dtab->items--;			hlist_del_rcu(&dev->index_hlist);			call_rcu(&dev->rcu, __dev_map_entry_free);		}	}	spin_unlock_irqrestore(&dtab->index_lock, flags);} static int dev_map_notification(struct notifier_block *notifier,				ulong event, void *ptr){	struct net_device *netdev = netdev_notifier_info_to_dev(ptr);	struct bpf_dtab *dtab;	int i, cpu; 	switch (event) {	case NETDEV_REGISTER:		if (!netdev->netdev_ops->ndo_xdp_xmit || netdev->xdp_bulkq)			break; 		/* will be freed in free_netdev() */		netdev->xdp_bulkq =			__alloc_percpu_gfp(sizeof(struct xdp_dev_bulk_queue),					   sizeof(void *), GFP_ATOMIC);		if (!netdev->xdp_bulkq)			return NOTIFY_BAD; 		for_each_possible_cpu(cpu)			per_cpu_ptr(netdev->xdp_bulkq, cpu)->dev = netdev;		break;	case NETDEV_UNREGISTER:		/* This rcu_read_lock/unlock pair is needed because		 * dev_map_list is an RCU list AND to ensure a delete		 * operation does not free a netdev_map entry while we		 * are comparing it against the netdev being unregistered.		 */		rcu_read_lock();		list_for_each_entry_rcu(dtab, &dev_map_list, list) {			if (dtab->map.map_type == BPF_MAP_TYPE_DEVMAP_HASH) {				dev_map_hash_remove_netdev(dtab, netdev);				continue;			} 			for (i = 0; i < dtab->map.max_entries; i++) {				struct bpf_dtab_netdev *dev, *odev; 				dev = READ_ONCE(dtab->netdev_map[i]);				if (!dev || netdev != dev->dev)					continue;				odev = cmpxchg(&dtab->netdev_map[i], dev, NULL);				if (dev == odev)					call_rcu(&dev->rcu,						 __dev_map_entry_free);			}		}		rcu_read_unlock();		break;	default:		break;	}	return NOTIFY_OK;} static struct notifier_block dev_map_notifier = {	.notifier_call = dev_map_notification,}; static int __init dev_map_init(void){	int cpu; 	/* Assure tracepoint shadow struct _bpf_dtab_netdev is in sync */	BUILD_BUG_ON(offsetof(struct bpf_dtab_netdev, dev) !=		     offsetof(struct _bpf_dtab_netdev, dev));	register_netdevice_notifier(&dev_map_notifier); 	for_each_possible_cpu(cpu)		INIT_LIST_HEAD(&per_cpu(dev_flush_list, cpu));	return 0;} subsys_initcall(dev_map_init);

Lines 501–877 of 877. Previous lines