Skip to content

Commit b16d9aa

Browse files
iamkafaidavem330
authored andcommitted
bpf: Add BPF_PROG_GET_FD_BY_ID
Add BPF_PROG_GET_FD_BY_ID command to allow user to get a fd from a bpf_prog's ID. bpf_prog_inc_not_zero() is added and is called with prog_idr_lock held. __bpf_prog_put() is also added which has the 'bool do_idr_lock' param to decide if the prog_idr_lock should be acquired when freeing the prog->id. In the error path of bpf_prog_inc_not_zero(), it may have to call __bpf_prog_put(map, false) which does not need to take the prog_idr_lock when freeing the prog->id. It is currently limited to CAP_SYS_ADMIN which we can consider to lift it in followup patches. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Acked-by: Alexei Starovoitov <ast@fb.com> Acked-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 34ad558 commit b16d9aa

File tree

2 files changed

+87
-12
lines changed

2 files changed

+87
-12
lines changed

include/uapi/linux/bpf.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ enum bpf_cmd {
8484
BPF_PROG_TEST_RUN,
8585
BPF_PROG_GET_NEXT_ID,
8686
BPF_MAP_GET_NEXT_ID,
87+
BPF_PROG_GET_FD_BY_ID,
8788
};
8889

8990
enum bpf_map_type {
@@ -212,8 +213,11 @@ union bpf_attr {
212213
__u32 duration;
213214
} test;
214215

215-
struct { /* anonymous struct used by BPF_*_GET_NEXT_ID */
216-
__u32 start_id;
216+
struct { /* anonymous struct used by BPF_*_GET_*_ID */
217+
union {
218+
__u32 start_id;
219+
__u32 prog_id;
220+
};
217221
__u32 next_id;
218222
};
219223
} __attribute__((aligned(8)));

kernel/bpf/syscall.c

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -703,15 +703,23 @@ static int bpf_prog_alloc_id(struct bpf_prog *prog)
703703
return id > 0 ? 0 : id;
704704
}
705705

706-
static void bpf_prog_free_id(struct bpf_prog *prog)
706+
static void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock)
707707
{
708708
/* cBPF to eBPF migrations are currently not in the idr store. */
709709
if (!prog->aux->id)
710710
return;
711711

712-
spin_lock_bh(&prog_idr_lock);
712+
if (do_idr_lock)
713+
spin_lock_bh(&prog_idr_lock);
714+
else
715+
__acquire(&prog_idr_lock);
716+
713717
idr_remove(&prog_idr, prog->aux->id);
714-
spin_unlock_bh(&prog_idr_lock);
718+
719+
if (do_idr_lock)
720+
spin_unlock_bh(&prog_idr_lock);
721+
else
722+
__release(&prog_idr_lock);
715723
}
716724

717725
static void __bpf_prog_put_rcu(struct rcu_head *rcu)
@@ -723,16 +731,21 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)
723731
bpf_prog_free(aux->prog);
724732
}
725733

726-
void bpf_prog_put(struct bpf_prog *prog)
734+
static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
727735
{
728736
if (atomic_dec_and_test(&prog->aux->refcnt)) {
729737
trace_bpf_prog_put_rcu(prog);
730738
/* bpf_prog_free_id() must be called first */
731-
bpf_prog_free_id(prog);
739+
bpf_prog_free_id(prog, do_idr_lock);
732740
bpf_prog_kallsyms_del(prog);
733741
call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
734742
}
735743
}
744+
745+
void bpf_prog_put(struct bpf_prog *prog)
746+
{
747+
__bpf_prog_put(prog, true);
748+
}
736749
EXPORT_SYMBOL_GPL(bpf_prog_put);
737750

738751
static int bpf_prog_release(struct inode *inode, struct file *filp)
@@ -814,6 +827,24 @@ struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog)
814827
}
815828
EXPORT_SYMBOL_GPL(bpf_prog_inc);
816829

830+
/* prog_idr_lock should have been held */
831+
static struct bpf_prog *bpf_prog_inc_not_zero(struct bpf_prog *prog)
832+
{
833+
int refold;
834+
835+
refold = __atomic_add_unless(&prog->aux->refcnt, 1, 0);
836+
837+
if (refold >= BPF_MAX_REFCNT) {
838+
__bpf_prog_put(prog, false);
839+
return ERR_PTR(-EBUSY);
840+
}
841+
842+
if (!refold)
843+
return ERR_PTR(-ENOENT);
844+
845+
return prog;
846+
}
847+
817848
static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *type)
818849
{
819850
struct fd f = fdget(ufd);
@@ -928,16 +959,21 @@ static int bpf_prog_load(union bpf_attr *attr)
928959
goto free_used_maps;
929960

930961
err = bpf_prog_new_fd(prog);
931-
if (err < 0)
932-
/* failed to allocate fd */
933-
goto free_id;
962+
if (err < 0) {
963+
/* failed to allocate fd.
964+
* bpf_prog_put() is needed because the above
965+
* bpf_prog_alloc_id() has published the prog
966+
* to the userspace and the userspace may
967+
* have refcnt-ed it through BPF_PROG_GET_FD_BY_ID.
968+
*/
969+
bpf_prog_put(prog);
970+
return err;
971+
}
934972

935973
bpf_prog_kallsyms_add(prog);
936974
trace_bpf_prog_load(prog, err);
937975
return err;
938976

939-
free_id:
940-
bpf_prog_free_id(prog);
941977
free_used_maps:
942978
free_used_maps(prog->aux);
943979
free_prog:
@@ -1099,6 +1135,38 @@ static int bpf_obj_get_next_id(const union bpf_attr *attr,
10991135
return err;
11001136
}
11011137

1138+
#define BPF_PROG_GET_FD_BY_ID_LAST_FIELD prog_id
1139+
1140+
static int bpf_prog_get_fd_by_id(const union bpf_attr *attr)
1141+
{
1142+
struct bpf_prog *prog;
1143+
u32 id = attr->prog_id;
1144+
int fd;
1145+
1146+
if (CHECK_ATTR(BPF_PROG_GET_FD_BY_ID))
1147+
return -EINVAL;
1148+
1149+
if (!capable(CAP_SYS_ADMIN))
1150+
return -EPERM;
1151+
1152+
spin_lock_bh(&prog_idr_lock);
1153+
prog = idr_find(&prog_idr, id);
1154+
if (prog)
1155+
prog = bpf_prog_inc_not_zero(prog);
1156+
else
1157+
prog = ERR_PTR(-ENOENT);
1158+
spin_unlock_bh(&prog_idr_lock);
1159+
1160+
if (IS_ERR(prog))
1161+
return PTR_ERR(prog);
1162+
1163+
fd = bpf_prog_new_fd(prog);
1164+
if (fd < 0)
1165+
bpf_prog_put(prog);
1166+
1167+
return fd;
1168+
}
1169+
11021170
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
11031171
{
11041172
union bpf_attr attr = {};
@@ -1184,6 +1252,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
11841252
err = bpf_obj_get_next_id(&attr, uattr,
11851253
&map_idr, &map_idr_lock);
11861254
break;
1255+
case BPF_PROG_GET_FD_BY_ID:
1256+
err = bpf_prog_get_fd_by_id(&attr);
1257+
break;
11871258
default:
11881259
err = -EINVAL;
11891260
break;

0 commit comments

Comments
 (0)