1217 lines · 31.6 KB · C
RawDownload
	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 long 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 long dev_map_redirect(struct bpf_map *map, u64 ifindex, u64 flags){	return __bpf_xdp_redirect_map(map, ifindex, flags,				      BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS,				      __dev_map_lookup_elem);} static long dev_hash_map_redirect(struct bpf_map *map, u64 ifindex, u64 flags){	return __bpf_xdp_redirect_map(map, ifindex, flags,				      BPF_F_BROADCAST | BPF_F_EXCLUDE_INGRESS,				      __dev_map_hash_lookup_elem);} static u64 dev_map_mem_usage(const struct bpf_map *map){	struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);	u64 usage = sizeof(struct bpf_dtab); 	if (map->map_type == BPF_MAP_TYPE_DEVMAP_HASH)		usage += (u64)dtab->n_buckets * sizeof(struct hlist_head);	else		usage += (u64)map->max_entries * sizeof(struct bpf_dtab_netdev *);	usage += atomic_read((atomic_t *)&dtab->items) *			 (u64)sizeof(struct bpf_dtab_netdev);	return usage;} BTF_ID_LIST_SINGLE(dev_map_btf_ids, struct, bpf_dtab)const struct bpf_map_ops dev_map_ops = {	.map_meta_equal = bpf_map_meta_equal,	.map_alloc_check = dev_map_alloc_check,	.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_mem_usage = dev_map_mem_usage,	.map_btf_id = &dev_map_btf_ids[0],	.map_redirect = dev_map_redirect,}; const struct bpf_map_ops dev_map_hash_ops = {	.map_meta_equal = bpf_map_meta_equal,	.map_alloc_check = dev_map_alloc_check,	.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_mem_usage = dev_map_mem_usage,	.map_btf_id = &dev_map_btf_ids[0],	.map_redirect = dev_hash_map_redirect,}; 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(struct xdp_dev_bulk_queue);		if (!netdev->xdp_bulkq)			return NOTIFY_BAD; 		for_each_possible_cpu(cpu) {			struct xdp_dev_bulk_queue *bq; 			bq = per_cpu_ptr(netdev->xdp_bulkq, cpu);			bq->dev = netdev;			local_lock_init(&bq->bq_lock);		}		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 = rcu_dereference(dtab->netdev_map[i]);				if (!dev || netdev != dev->dev)					continue;				odev = unrcu_pointer(cmpxchg(&dtab->netdev_map[i], RCU_INITIALIZER(dev), NULL));				if (dev == odev) {					call_rcu(&dev->rcu,						 __dev_map_entry_free);					atomic_dec((atomic_t *)&dtab->items);				}			}		}		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){	/* 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); 	return 0;} subsys_initcall(dev_map_init);

Lines 1001–1217 of 1217. Previous lines