Skip to content

Commit

Permalink
Merge branch 'tools-ynl-user-space-c'
Browse files Browse the repository at this point in the history
Jakub Kicinski says:

====================
tools: ynl: user space C

Use the code gen which is already in tree to generate a user space
library for a handful of simple families. I find YNL C quite useful
in some WIP projects, and I think others may find it useful, too.
I was hoping someone will pick this work up and finish it...
but it seems that Python YNL has largely stolen the thunder.
Python may not be great for selftest, tho, and actually this lib
is more fully-featured. The Python script was meant as a quick demo,
funny how those things go.

v2: https://lore.kernel.org/all/20230604175843.662084-1-kuba@kernel.org/
v1: https://lore.kernel.org/all/20230603052547.631384-1-kuba@kernel.org/
====================

Link: https://lore.kernel.org/r/20230605190108.809439-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed Jun 6, 2023
2 parents ae91f7e + ee0202e commit 2dc4764
Show file tree
Hide file tree
Showing 15 changed files with 2,466 additions and 4 deletions.
79 changes: 79 additions & 0 deletions Documentation/userspace-api/netlink/intro-specs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,82 @@ to see other examples.
The code generation itself is performed by ``tools/net/ynl/ynl-gen-c.py``
but it takes a few arguments so calling it directly for each file
quickly becomes tedious.

YNL lib
=======

``tools/net/ynl/lib/`` contains an implementation of a C library
(based on libmnl) which integrates with code generated by
``tools/net/ynl/ynl-gen-c.py`` to create easy to use netlink wrappers.

YNL basics
----------

The YNL library consists of two parts - the generic code (functions
prefix by ``ynl_``) and per-family auto-generated code (prefixed
with the name of the family).

To create a YNL socket call ynl_sock_create() passing the family
struct (family structs are exported by the auto-generated code).
ynl_sock_destroy() closes the socket.

YNL requests
------------

Steps for issuing YNL requests are best explained on an example.
All the functions and types in this example come from the auto-generated
code (for the netdev family in this case):

.. code-block:: c
// 0. Request and response pointers
struct netdev_dev_get_req *req;
struct netdev_dev_get_rsp *d;
// 1. Allocate a request
req = netdev_dev_get_req_alloc();
// 2. Set request parameters (as needed)
netdev_dev_get_req_set_ifindex(req, ifindex);
// 3. Issues the request
d = netdev_dev_get(ys, req);
// 4. Free the request arguments
netdev_dev_get_req_free(req);
// 5. Error check (the return value from step 3)
if (!d) {
// 6. Print the YNL-generated error
fprintf(stderr, "YNL: %s\n", ys->err.msg);
return -1;
}
// ... do stuff with the response @d
// 7. Free response
netdev_dev_get_rsp_free(d);
YNL dumps
---------

Performing dumps follows similar pattern as requests.
Dumps return a list of objects terminated by a special marker,
or NULL on error. Use ``ynl_dump_foreach()`` to iterate over
the result.

YNL notifications
-----------------

YNL lib supports using the same socket for notifications and
requests. In case notifications arrive during processing of a request
they are queued internally and can be retrieved at a later time.

To subscribed to notifications use ``ynl_subscribe()``.
The notifications have to be read out from the socket,
``ynl_socket_get_fd()`` returns the underlying socket fd which can
be plugged into appropriate asynchronous IO API like ``poll``,
or ``select``.

Notifications can be retrieved using ``ynl_ntf_dequeue()`` and have
to be freed using ``ynl_ntf_free()``. Since we don't know the notification
type upfront the notifications are returned as ``struct ynl_ntf_base_type *``
and user is expected to cast them to the appropriate full type based
on the ``cmd`` member.
19 changes: 19 additions & 0 deletions tools/net/ynl/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0

SUBDIRS = lib generated samples

all: $(SUBDIRS)

$(SUBDIRS):
@if [ -f "$@/Makefile" ] ; then \
$(MAKE) -C $@ ; \
fi

clean hardclean:
@for dir in $(SUBDIRS) ; do \
if [ -f "$$dir/Makefile" ] ; then \
$(MAKE) -C $$dir $@; \
fi \
done

.PHONY: clean all $(SUBDIRS)
45 changes: 45 additions & 0 deletions tools/net/ynl/generated/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# SPDX-License-Identifier: GPL-2.0

CC=gcc
CFLAGS=-std=gnu11 -O2 -W -Wall -Wextra -Wno-unused-parameter -Wshadow \
-I../lib/
ifeq ("$(DEBUG)","1")
CFLAGS += -g -fsanitize=address -fsanitize=leak -static-libasan
endif

TOOL:=../ynl-gen-c.py

GENS:=fou netdev
SRCS=$(patsubst %,%-user.c,${GENS})
HDRS=$(patsubst %,%-user.h,${GENS})
OBJS=$(patsubst %,%-user.o,${GENS})

all: protos.a $(HDRS) $(SRCS) $(KHDRS) $(KSRCS) $(UAPI) regen

protos.a: $(OBJS)
@echo -e "\tAR $@"
@ar rcs $@ $(OBJS)

%-user.h: ../../../../Documentation/netlink/specs/%.yaml $(TOOL)
@echo -e "\tGEN $@"
@$(TOOL) --mode user --header --spec $< > $@

%-user.c: ../../../../Documentation/netlink/specs/%.yaml $(TOOL)
@echo -e "\tGEN $@"
@$(TOOL) --mode user --source --spec $< > $@

%-user.o: %-user.c %-user.h
@echo -e "\tCC $@"
@$(COMPILE.c) -c -o $@ $<

clean:
rm -f *.o

hardclean: clean
rm -f *.c *.h *.a

regen:
@../ynl-regen.sh

.PHONY: all clean hardclean regen
.DEFAULT_GOAL: all
Loading

0 comments on commit 2dc4764

Please sign in to comment.