code:
=======================================
Oracle Data Analytics Accelerator (DAX)
=======================================

DAX is a coprocessor which resides on the SPARC M7 (DAX1) and M8
(DAX2) processor chips, and has direct access to the CPU's L3 caches
as well as physical memory. It can perform several operations on data
streams with various input and output formats.  A driver provides a
transport mechanism and has limited knowledge of the various opcodes
and data formats. A user space library provides high level services
and translates these into low level commands which are then passed
into the driver and subsequently the Hypervisor and the coprocessor.
The library is the recommended way for applications to use the
coprocessor, and the driver interface is not intended for general use.
This document describes the general flow of the driver, its
structures, and its programmatic interface. It also provides example
code sufficient to write user or kernel applications that use DAX
functionality.

The user library is open source and available at:

    https://oss.oracle.com/git/gitweb.cgi?p=libdax.git

The Hypervisor interface to the coprocessor is described in detail in
the accompanying document, dax-hv-api.txt, which is a plain text
excerpt of the (Oracle internal) "UltraSPARC Virtual Machine
Specification" version 3.0.20+15, dated 2017-09-25.

High Level Overview
===================

A coprocessor request is described by a Command Control Block
(CCB). The CCB contains an opcode and various parameters. The opcode
specifies what operation is to be done, and the parameters specify
options, flags, sizes, and addresses.  The CCB (or an array of CCBs)
is passed to the Hypervisor, which handles queueing and scheduling of
requests to the available coprocessor execution units. A status code
returned indicates if the request was submitted successfully or if
there was an error.  One of the addresses given in each CCB is a
pointer to a "completion area", which is a 128 byte memory block that
is written by the coprocessor to provide execution status. No
interrupt is generated upon completion; the completion area must be
polled by software to find out when a transaction has finished, but
the M7 and later processors provide a mechanism to pause the virtual
processor until the completion status has been updated by the
coprocessor. This is done using the monitored load and mwait
instructions, which are described in more detail later.  The DAX
coprocessor was designed so that after a request is submitted, the
kernel is no longer involved in the processing of it.  The polling is
done at the user level, which results in almost zero latency between
completion of a request and resumption of execution of the requesting
thread.

Addressing Memory
=================

The kernel does not have access to physical memory in the Sun4v
architecture, as there is an additional level of memory virtualization
present. This intermediate level is called "real" memory, and the
kernel treats this as if it were physical.  The Hypervisor handles the
translations between real memory and physical so that each logical
domain (LDOM) can have a partition of physical memory that is isolated
from that of other LDOMs.  When the kernel sets up a virtual mapping,
it specifies a virtual address and the real address to which it should
be mapped.

The DAX coprocessor can only operate on physical memory, so before a
request can be fed to the coprocessor, all the addresses in a CCB must
be converted into physical addresses. The kernel cannot do this since
it has no visibility into physical addresses. So a CCB may contain
either the virtual or real addresses of the buffers or a combination
of them. An "address type" field is available for each address that
may be given in the CCB. In all cases, the Hypervisor will translate
all the addresses to physical before dispatching to hardware. Address
translations are performed using the context of the process initiating
the request.

The Driver API
==============

An application makes requests to the driver via the write() system
call, and gets results (if any) via read(). The completion areas are
made accessible via mmap(), and are read-only for the application.

The request may either be an immediate command or an array of CCBs to
be submitted to the hardware.

Each open instance of the device is exclusive to the thread that
opened it, and must be used by that thread for all subsequent
operations. The driver open function creates a new context for the
thread and initializes it for use.  This context contains pointers and
values used internally by the driver to keep track of submitted
requests. The completion area buffer is also allocated, and this is
large enough to contain the completion areas for many concurrent
requests.  When the device is closed, any outstanding transactions are
flushed and the context is cleaned up.

On a DAX1 system (M7), the device will be called "oradax1", while on a
DAX2 system (M8) it will be "oradax2". If an application requires one
or the other, it should simply attempt to open the appropriate
device. Only one of the devices will exist on any given system, so the
name can be used to determine what the platform supports.

The immediate commands are CCB_DEQUEUE, CCB_KILL, and CCB_INFO. For
all of these, success is indicated by a return value from write()
equal to the number of bytes given in the call. Otherwise -1 is
returned and errno is set.

CCB_DEQUEUE
-----------

Tells the driver to clean up resources associated with past
requests. Since no interrupt is generated upon the completion of a
request, the driver must be told when it may reclaim resources.  No
further status information is returned, so the user should not
subsequently call read().

CCB_KILL
--------

Kills a CCB during execution. The CCB is guaranteed to not continue
executing once this call returns successfully. On success, read() must
be called to retrieve the result of the action.

CCB_INFO
--------

Retrieves information about a currently executing CCB. Note that some
Hypervisors might return 'notfound' when the CCB is in 'inprogress'
state. To ensure a CCB in the 'notfound' state will never be executed,
CCB_KILL must be invoked on that CCB. Upon success, read() must be
called to retrieve the details of the action.

Submission of an array of CCBs for execution
---------------------------------------------

A write() whose length is a multiple of the CCB size is treated as a
submit operation. The file offset is treated as the index of the
completion area to use, and may be set via lseek() or using the
pwrite() system call. If -1 is returned then errno is set to indicate
the error. Otherwise, the return value is the length of the array that
was actually accepted by the coprocessor. If the accepted length is
equal to the requested length, then the submission was completely
successful and there is no further status needed; hence, the user
should not subsequently call read(). Partial acceptance of the CCB
array is indicated by a return value less than the requested length,
and read() must be called to retrieve further status information.  The
status will reflect the error caused by the first CCB that was not
accepted, and status_data will provide additional data in some cases.

MMAP
----

The mmap() function provides access to the completion area allocated
in the driver.  Note that the completion area is not writeable by the
user process, and the mmap call must not specify PROT_WRITE.

Completion of a Request
=======================

The first byte in each completion area is the command status which is
updated by the coprocessor hardware. Software may take advantage of
new M7/M8 processor capabilities to efficiently poll this status byte.
First, a "monitored load" is achieved via a Load from Alternate Space
(ldxa, lduba, etc.) with ASI 0x84 (ASI_MONITOR_PRIMARY).  Second, a
"monitored wait" is achieved via the mwait instruction (a write to
%asr28). This instruction is like pause in that it suspends execution
of the virtual processor for the given number of nanoseconds, but in
addition will terminate early when one of several events occur. If the
block of data containing the monitored location is modified, then the
mwait terminates. This causes software to resume execution immediately
(without a context switch or kernel to user transition) after a
transaction completes. Thus the latency between transaction completion
and resumption of execution may be just a few nanoseconds.

Application Life Cycle of a DAX Submission
==========================================

 - open dax device
 - call mmap() to get the completion area address
 - allocate a CCB and fill in the opcode, flags, parameters, addresses, etc.
 - submit CCB via write() or pwrite()
 - go into a loop executing monitored load + monitored wait and
   terminate when the command status indicates the request is complete
   (CCB_KILL or CCB_INFO may be used any time as necessary)
 - perform a CCB_DEQUEUE
 - call munmap() for completion area
 - close the dax device

Memory Constraints
==================

The DAX hardware operates only on physical addresses. Therefore, it is
not aware of virtual memory mappings and the discontiguities that may
exist in the physical memory that a virtual buffer maps to. There is
no I/O TLB or any scatter/gather mechanism. All buffers, whether input
or output, must reside in a physically contiguous region of memory.

The Hypervisor translates all addresses within a CCB to physical
before handing off the CCB to DAX. The Hypervisor determines the
virtual page size for each virtual address given, and uses this to
program a size limit for each address. This prevents the coprocessor
from reading or writing beyond the bound of the virtual page, even
though it is accessing physical memory directly. A simpler way of
saying this is that a DAX operation will never "cross" a virtual page
boundary. If an 8k virtual page is used, then the data is strictly
limited to 8k. If a user's buffer is larger than 8k, then a larger
page size must be used, or the transaction size will be truncated to
8k.

Huge pages. A user may allocate huge pages using standard interfaces.
Memory buffers residing on huge pages may be used to achieve much
larger DAX transaction sizes, but the rules must still be followed,
and no transaction will cross a page boundary, even a huge page.  A
major caveat is that Linux on Sparc presents 8Mb as one of the huge
page sizes. Sparc does not actually provide a 8Mb hardware page size,
and this size is synthesized by pasting together two 4Mb pages. The
reasons for this are historical, and it creates an issue because only
half of this 8Mb page can actually be used for any given buffer in a
DAX request, and it must be either the first half or the second half;
it cannot be a 4Mb chunk in the middle, since that crosses a
(hardware) page boundary. Note that this entire issue may be hidden by
higher level libraries.

CCB Structure
-------------
A CCB is an array of 8 64-bit words. Several of these words provide
command opcodes, parameters, flags, etc., and the rest are addresses
for the completion area, output buffer, and various inputs::

   struct ccb {
       u64   control;
       u64   completion;
       u64   input0;
       u64   access;
       u64   input1;
       u64   op_data;
       u64   output;
       u64   table;
   };

See libdax/common/sys/dax1/dax1_ccb.h for a detailed description of
each of these fields, and see dax-hv-api.txt for a complete description
of the Hypervisor API available to the guest OS (ie, Linux kernel).

The first word (control) is examined by the driver for the following:
 - CCB version, which must be consistent with hardware version
 - Opcode, which must be one of the documented allowable commands
 - Address types, which must be set to "virtual" for all the addresses
   given by the user, thereby ensuring that the application can
   only access memory that it owns

Example Code
============

The DAX is accessible to both user and kernel code.  The kernel code
can make hypercalls directly while the user code must use wrappers
provided by the driver. The setup of the CCB is nearly identical for
both; the only difference is in preparation of the completion area. An
example of user code is given now, with kernel code afterwards.

In order to program using the driver API, the file
arch/sparc/include/uapi/asm/oradax.h must be included.

First, the proper device must be opened. For M7 it will be
/dev/oradax1 and for M8 it will be /dev/oradax2. The simplest
procedure is to attempt to open both, as only one will succeed::

	fd = open("/dev/oradax1", O_RDWR);
	if (fd < 0)
		fd = open("/dev/oradax2", O_RDWR);
	if (fd < 0)
	       /* No DAX found */

Next, the completion area must be mapped::

      completion_area = mmap(NULL, DAX_MMAP_LEN, PROT_READ, MAP_SHARED, fd, 0);

All input and output buffers must be fully contained in one hardware
page, since as explained above, the DAX is strictly constrained by
virtual page boundaries.  In addition, the output buffer must be
64-byte aligned and its size must be a multiple of 64 bytes because
the coprocessor writes in units of cache lines.

This example demonstrates the DAX Scan command, which takes as input a
vector and a match value, and produces a bitmap as the output. For
each input element that matches the value, the corresponding bit is
set in the output.

In this example, the input vector consists of a series of single bits,
and the match value is 0. So each 0 bit in the input will produce a 1
in the output, and vice versa, which produces an output bitmap which
is the input bitmap inverted.

For details of all the parameters and bits used in this CCB, please
refer to section 36.2.1.3 of the DAX Hypervisor API document, which
describes the Scan command in detail::

	ccb->control =       /* Table 36.1, CCB Header Format */
		  (2L << 48)     /* command = Scan Value */
		| (3L << 40)     /* output address type = primary virtual */
		| (3L << 34)     /* primary input address type = primary virtual */
		             /* Section 36.2.1, Query CCB Command Formats */
		| (1 << 28)     /* 36.2.1.1.1 primary input format = fixed width bit packed */
		| (0 << 23)     /* 36.2.1.1.2 primary input element size = 0 (1 bit) */
		| (8 << 10)     /* 36.2.1.1.6 output format = bit vector */
		| (0 <<  5)	/* 36.2.1.3 First scan criteria size = 0 (1 byte) */
		| (31 << 0);	/* 36.2.1.3 Disable second scan criteria */

	ccb->completion = 0;    /* Completion area address, to be filled in by driver */

	ccb->input0 = (unsigned long) input; /* primary input address */

	ccb->access =       /* Section 36.2.1.2, Data Access Control */
		  (2 << 24)    /* Primary input length format = bits */
		| (nbits - 1); /* number of bits in primary input stream, minus 1 */

	ccb->input1 = 0;       /* secondary input address, unused */

	ccb->op_data = 0;      /* scan criteria (value to be matched) */

	ccb->output = (unsigned long) output;	/* output address */

	ccb->table = 0;	       /* table address, unused */

The CCB submission is a write() or pwrite() system call to the
driver. If the call fails, then a read() must be used to retrieve the
status::

	if (pwrite(fd, ccb, 64, 0) != 64) {
		struct ccb_exec_result status;
		read(fd, &status, sizeof(status));
		/* bail out */
	}

After a successful submission of the CCB, the completion area may be
polled to determine when the DAX is finished. Detailed information on
the contents of the completion area can be found in section 36.2.2 of
the DAX HV API document::

	while (1) {
		/* Monitored Load */
		__asm__ __volatile__("lduba [%1] 0x84, %0\n"
				     : "=r" (status)
				     : "r"  (completion_area));

		if (status)	     /* 0 indicates command in progress */
			break;

		/* MWAIT */
		__asm__ __volatile__("wr %%g0, 1000, %%asr28\n" ::);    /* 1000 ns */
	}

A completion area status of 1 indicates successful completion of the
CCB and validity of the output bitmap, which may be used immediately.
All other non-zero values indicate error conditions which are
described in section 36.2.2::

	if (completion_area[0] != 1) {	/* section 36.2.2, 1 = command ran and succeeded */
		/* completion_area[0] contains the completion status */
		/* completion_area[1] contains an error code, see 36.2.2 */
	}

After the completion area has been processed, the driver must be
notified that it can release any resources associated with the
request. This is done via the dequeue operation::

	struct dax_command cmd;
	cmd.command = CCB_DEQUEUE;
	if (write(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) {
		/* bail out */
	}

Finally, normal program cleanup should be done, i.e., unmapping
completion area, closing the dax device, freeing memory etc.

Kernel example
--------------

The only difference in using the DAX in kernel code is the treatment
of the completion area. Unlike user applications which mmap the
completion area allocated by the driver, kernel code must allocate its
own memory to use for the completion area, and this address and its
type must be given in the CCB::

	ccb->control |=      /* Table 36.1, CCB Header Format */
	        (3L << 32);     /* completion area address type = primary virtual */

	ccb->completion = (unsigned long) completion_area;   /* Completion area address */

The dax submit hypercall is made directly. The flags used in the
ccb_submit call are documented in the DAX HV API in section 36.3.1/

::

  #include <asm/hypervisor.h>

	hv_rv = sun4v_ccb_submit((unsigned long)ccb, 64,
				 HV_CCB_QUERY_CMD |
				 HV_CCB_ARG0_PRIVILEGED | HV_CCB_ARG0_TYPE_PRIMARY |
				 HV_CCB_VA_PRIVILEGED,
				 0, &bytes_accepted, &status_data);

	if (hv_rv != HV_EOK) {
		/* hv_rv is an error code, status_data contains */
		/* potential additional status, see 36.3.1.1 */
	}

After the submission, the completion area polling code is identical to
that in user land::

	while (1) {
		/* Monitored Load */
		__asm__ __volatile__("lduba [%1] 0x84, %0\n"
				     : "=r" (status)
				     : "r"  (completion_area));

		if (status)	     /* 0 indicates command in progress */
			break;

		/* MWAIT */
		__asm__ __volatile__("wr %%g0, 1000, %%asr28\n" ::);    /* 1000 ns */
	}

	if (completion_area[0] != 1) {	/* section 36.2.2, 1 = command ran and succeeded */
		/* completion_area[0] contains the completion status */
		/* completion_area[1] contains an error code, see 36.2.2 */
	}

The output bitmap is ready for consumption immediately after the
completion status indicates success.

Excer[t from UltraSPARC Virtual Machine Specification
=====================================================

 .. include:: dax-hv-api.txt
    :literal:

repo_name: angery/linux


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * CSS tweaks for the Alabaster theme
 */

/* Shrink the headers a bit */
div.body h1 { font-size: 180%; }
div.body h2 { font-size: 150%; }
div.body h3 { font-size: 130%; }

/* Tighten up the layout slightly */
div.body { padding: 0 15px 0 10px; }
div.sphinxsidebarwrapper { padding: 1em 0.4em; }
div.sphinxsidebar { font-size: inherit; }
/* Tweak document margins and don't force width */
div.document {
    margin: 20px 10px 0 10px;
    width: auto;
}

/*
 * Parameters for the display of function prototypes and such included
 * from C source files.
 */
dl.function, dl.struct, dl.enum { margin-top: 2em; background-color: #ecf0f3; }
/* indent lines 2+ of multi-line function prototypes */
dl.function dt { margin-left: 10em; text-indent: -10em; }
dt.sig-object { font-size: larger; }
div.kernelindent { margin-left: 2em; margin-right: 4em; }


code:
/* -*- coding: utf-8; mode: css -*-
 *
 * Sphinx HTML theme customization: read the doc
 * Please don't add any color definition here, as the theme should
 * work for both normal and dark modes.
 */

/* Improve contrast and increase size for easier reading. */

body {
	font-family: serif;
	font-size: 100%;
}

h1, h2, .rst-content .toctree-wrapper p.caption, h3, h4, h5, h6, legend {
	font-family: sans-serif;
}

div[class^="highlight"] pre {
	font-family: monospace;
	font-size: 100%;
}

.wy-menu-vertical {
	font-family: sans-serif;
}

.c {
	font-style: normal;
}

p {
	font-size: 100%;
}

/* Interim: Code-blocks with line nos - lines and line numbers don't line up.
 * see: https://github.com/rtfd/sphinx_rtd_theme/issues/419
 */

div[class^="highlight"] pre {
    line-height: normal;
}
.rst-content .highlight > pre {
    line-height: normal;
}

/* Keep fields from being strangely far apart due to inheirited table CSS. */
.rst-content table.field-list th.field-name {
    padding-top: 1px;
    padding-bottom: 1px;
}
.rst-content table.field-list td.field-body {
    padding-top: 1px;
    padding-bottom: 1px;
}

@media screen {

    /* content column
     *
     * RTD theme's default is 800px as max width for the content, but we have
     * tables with tons of columns, which need the full width of the view-port.
     */

    .wy-nav-content{max-width: none; }

    /* table:
     *
     *   - Sequences of whitespace should collapse into a single whitespace.
     *   - make the overflow auto (scrollbar if needed)
     *   - align caption "left" ("center" is unsuitable on vast tables)
     */

    .wy-table-responsive table td { white-space: normal; }
    .wy-table-responsive { overflow: auto; }
    .rst-content table.docutils caption { text-align: left; font-size: 100%; }

    /* captions:
     *
     *   - captions should have 100% (not 85%) font size
     *   - hide the permalink symbol as long as link is not hovered
     */

    .toc-title {
        font-size: 150%;
	font-weight: bold;
    }

    caption, .wy-table caption, .rst-content table.field-list caption {
        font-size: 100%;
    }
    caption a.headerlink { opacity: 0; }
    caption a.headerlink:hover { opacity: 1; }

    /* Menu selection and keystrokes */

    span.menuselection {
	font-family: "Courier New", Courier, monospace
    }

    code.kbd, code.kbd span {
	font-weight: bold;
	font-family: "Courier New", Courier, monospace
    }

    /* fix bottom margin of lists items */

    .rst-content .section ul li:last-child, .rst-content .section ul li p:last-child {
          margin-bottom: 12px;
    }

    /* inline literal: drop the borderbox, padding and red color */

    code, .rst-content tt, .rst-content code {
        color: inherit;
        border: none;
        padding: unset;
        background: inherit;
        font-size: 85%;
    }

    .rst-content tt.literal,.rst-content tt.literal,.rst-content code.literal {
        color: inherit;
    }
}


code:
/* -*- coding: utf-8; mode: css -*-
 *
 * Sphinx HTML theme customization: color settings for RTD (non-dark) theme
 *
 */

/* Improve contrast and increase size for easier reading. */

body {
	color: black;
}

.wy-menu-vertical li.current a {
	color: #505050;
}

.wy-menu-vertical li.on a, .wy-menu-vertical li.current > a {
	color: #303030;
}

div[class^="highlight"] pre {
	color: black;
}

@media screen {

    /* Menu selection and keystrokes */

    span.menuselection {
	color: blue;
    }

    code.kbd, code.kbd span {
	color: white;
	background-color: darkblue;
    }
}


code:
# SPDX-License-Identifier: GPL-2.0
# Copyright 2019 Jonathan Corbet <corbet@lwn.net>
#
# Apply kernel-specific tweaks after the initial document processing
# has been done.
#
from docutils import nodes
import sphinx
from sphinx import addnodes
if sphinx.version_info[0] < 2 or \
   sphinx.version_info[0] == 2 and sphinx.version_info[1] < 1:
    from sphinx.environment import NoUri
else:
    from sphinx.errors import NoUri
import re
from itertools import chain

#
# Python 2 lacks re.ASCII...
#
try:
    ascii_p3 = re.ASCII
except AttributeError:
    ascii_p3 = 0

#
# Regex nastiness.  Of course.
# Try to identify "function()" that's not already marked up some
# other way.  Sphinx doesn't like a lot of stuff right after a
# :c:func: block (i.e. ":c:func:`mmap()`s" flakes out), so the last
# bit tries to restrict matches to things that won't create trouble.
#
RE_function = re.compile(r'\b(([a-zA-Z_]\w+)\(\))', flags=ascii_p3)

#
# Sphinx 2 uses the same :c:type role for struct, union, enum and typedef
#
RE_generic_type = re.compile(r'\b(struct|union|enum|typedef)\s+([a-zA-Z_]\w+)',
                             flags=ascii_p3)

#
# Sphinx 3 uses a different C role for each one of struct, union, enum and
# typedef
#
RE_struct = re.compile(r'\b(struct)\s+([a-zA-Z_]\w+)', flags=ascii_p3)
RE_union = re.compile(r'\b(union)\s+([a-zA-Z_]\w+)', flags=ascii_p3)
RE_enum = re.compile(r'\b(enum)\s+([a-zA-Z_]\w+)', flags=ascii_p3)
RE_typedef = re.compile(r'\b(typedef)\s+([a-zA-Z_]\w+)', flags=ascii_p3)

#
# Detects a reference to a documentation page of the form Documentation/... with
# an optional extension
#
RE_doc = re.compile(r'(\bDocumentation/)?((\.\./)*[\w\-/]+)\.(rst|txt)')

RE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$')

#
# Reserved C words that we should skip when cross-referencing
#
Skipnames = [ 'for', 'if', 'register', 'sizeof', 'struct', 'unsigned' ]

#
# Many places in the docs refer to common system calls.  It is
# pointless to try to cross-reference them and, as has been known
# to happen, somebody defining a function by these names can lead
# to the creation of incorrect and confusing cross references.  So
# just don't even try with these names.
#
Skipfuncs = [ 'open', 'close', 'read', 'write', 'fcntl', 'mmap',
              'select', 'poll', 'fork', 'execve', 'clone', 'ioctl',
              'socket' ]

c_namespace = ''

def markup_refs(docname, app, node):
    t = node.astext()
    done = 0
    repl = [ ]
    #
    # Associate each regex with the function that will markup its matches
    #
    markup_func_sphinx2 = {RE_doc: markup_doc_ref,
                           RE_function: markup_c_ref,
                           RE_generic_type: markup_c_ref}

    markup_func_sphinx3 = {RE_doc: markup_doc_ref,
                           RE_function: markup_func_ref_sphinx3,
                           RE_struct: markup_c_ref,
                           RE_union: markup_c_ref,
                           RE_enum: markup_c_ref,
                           RE_typedef: markup_c_ref}

    if sphinx.version_info[0] >= 3:
        markup_func = markup_func_sphinx3
    else:
        markup_func = markup_func_sphinx2

    match_iterators = [regex.finditer(t) for regex in markup_func]
    #
    # Sort all references by the starting position in text
    #
    sorted_matches = sorted(chain(*match_iterators), key=lambda m: m.start())
    for m in sorted_matches:
        #
        # Include any text prior to match as a normal text node.
        #
        if m.start() > done:
            repl.append(nodes.Text(t[done:m.start()]))

        #
        # Call the function associated with the regex that matched this text and
        # append its return to the text
        #
        repl.append(markup_func[m.re](docname, app, m))

        done = m.end()
    if done < len(t):
        repl.append(nodes.Text(t[done:]))
    return repl

#
# Keep track of cross-reference lookups that failed so we don't have to
# do them again.
#
failed_lookups = { }
def failure_seen(target):
    return (target) in failed_lookups
def note_failure(target):
    failed_lookups[target] = True

#
# In sphinx3 we can cross-reference to C macro and function, each one with its
# own C role, but both match the same regex, so we try both.
#
def markup_func_ref_sphinx3(docname, app, match):
    cdom = app.env.domains['c']
    #
    # Go through the dance of getting an xref out of the C domain
    #
    base_target = match.group(2)
    target_text = nodes.Text(match.group(0))
    xref = None
    possible_targets = [base_target]
    # Check if this document has a namespace, and if so, try
    # cross-referencing inside it first.
    if c_namespace:
        possible_targets.insert(0, c_namespace + "." + base_target)

    if base_target not in Skipnames:
        for target in possible_targets:
            if (target not in Skipfuncs) and not failure_seen(target):
                lit_text = nodes.literal(classes=['xref', 'c', 'c-func'])
                lit_text += target_text
                pxref = addnodes.pending_xref('', refdomain = 'c',
                                              reftype = 'function',
                                              reftarget = target,
                                              modname = None,
                                              classname = None)
                #
                # XXX The Latex builder will throw NoUri exceptions here,
                # work around that by ignoring them.
                #
                try:
                    xref = cdom.resolve_xref(app.env, docname, app.builder,
                                             'function', target, pxref,
                                             lit_text)
                except NoUri:
                    xref = None

                if xref:
                    return xref
                note_failure(target)

    return target_text

def markup_c_ref(docname, app, match):
    class_str = {# Sphinx 2 only
                 RE_function: 'c-func',
                 RE_generic_type: 'c-type',
                 # Sphinx 3+ only
                 RE_struct: 'c-struct',
                 RE_union: 'c-union',
                 RE_enum: 'c-enum',
                 RE_typedef: 'c-type',
                 }
    reftype_str = {# Sphinx 2 only
                   RE_function: 'function',
                   RE_generic_type: 'type',
                   # Sphinx 3+ only
                   RE_struct: 'struct',
                   RE_union: 'union',
                   RE_enum: 'enum',
                   RE_typedef: 'type',
                   }

    cdom = app.env.domains['c']
    #
    # Go through the dance of getting an xref out of the C domain
    #
    base_target = match.group(2)
    target_text = nodes.Text(match.group(0))
    xref = None
    possible_targets = [base_target]
    # Check if this document has a namespace, and if so, try
    # cross-referencing inside it first.
    if c_namespace:
        possible_targets.insert(0, c_namespace + "." + base_target)

    if base_target not in Skipnames:
        for target in possible_targets:
            if not (match.re == RE_function and target in Skipfuncs):
                lit_text = nodes.literal(classes=['xref', 'c', class_str[match.re]])
                lit_text += target_text
                pxref = addnodes.pending_xref('', refdomain = 'c',
                                              reftype = reftype_str[match.re],
                                              reftarget = target, modname = None,
                                              classname = None)
                #
                # XXX The Latex builder will throw NoUri exceptions here,
                # work around that by ignoring them.
                #
                try:
                    xref = cdom.resolve_xref(app.env, docname, app.builder,
                                             reftype_str[match.re], target, pxref,
                                             lit_text)
                except NoUri:
                    xref = None

                if xref:
                    return xref

    return target_text

#
# Try to replace a documentation reference of the form Documentation/... with a
# cross reference to that page
#
def markup_doc_ref(docname, app, match):
    stddom = app.env.domains['std']
    #
    # Go through the dance of getting an xref out of the std domain
    #
    absolute = match.group(1)
    target = match.group(2)
    if absolute:
       target = "/" + target
    xref = None
    pxref = addnodes.pending_xref('', refdomain = 'std', reftype = 'doc',
                                  reftarget = target, modname = None,
                                  classname = None, refexplicit = False)
    #
    # XXX The Latex builder will throw NoUri exceptions here,
    # work around that by ignoring them.
    #
    try:
        xref = stddom.resolve_xref(app.env, docname, app.builder, 'doc',
                                   target, pxref, None)
    except NoUri:
        xref = None
    #
    # Return the xref if we got it; otherwise just return the plain text.
    #
    if xref:
        return xref
    else:
        return nodes.Text(match.group(0))

def get_c_namespace(app, docname):
    source = app.env.doc2path(docname)
    with open(source) as f:
        for l in f:
            match = RE_namespace.search(l)
            if match:
                return match.group(1)
    return ''

def auto_markup(app, doctree, name):
    global c_namespace
    c_namespace = get_c_namespace(app, name)
    def text_but_not_a_reference(node):
        # The nodes.literal test catches ``literal text``, its purpose is to
        # avoid adding cross-references to functions that have been explicitly
        # marked with cc:func:.
        if not isinstance(node, nodes.Text) or isinstance(node.parent, nodes.literal):
            return False

        child_of_reference = False
        parent = node.parent
        while parent:
            if isinstance(parent, nodes.Referential):
                child_of_reference = True
                break
            parent = parent.parent
        return not child_of_reference

    #
    # This loop could eventually be improved on.  Someday maybe we
    # want a proper tree traversal with a lot of awareness of which
    # kinds of nodes to prune.  But this works well for now.
    #
    for para in doctree.traverse(nodes.paragraph):
        for node in para.traverse(condition=text_but_not_a_reference):
            node.parent.replace(node, markup_refs(name, app, node))

def setup(app):
    app.connect('doctree-resolved', auto_markup)
    return {
        'parallel_read_safe': True,
        'parallel_write_safe': True,
        }


code:
# -*- coding: utf-8; mode: python -*-
# pylint: disable=W0141,C0113,C0103,C0325
u"""
    cdomain
    ~~~~~~~

    Replacement for the sphinx c-domain.

    :copyright:  Copyright (C) 2016  Markus Heiser
    :license:    GPL Version 2, June 1991 see Linux/COPYING for details.

    List of customizations:

    * Moved the *duplicate C object description* warnings for function
      declarations in the nitpicky mode. See Sphinx documentation for
      the config values for ``nitpick`` and ``nitpick_ignore``.

    * Add option 'name' to the "c:function:" directive.  With option 'name' the
      ref-name of a function can be modified. E.g.::

          .. c:function:: int ioctl( int fd, int request )
             :name: VIDIOC_LOG_STATUS

      The func-name (e.g. ioctl) remains in the output but the ref-name changed
      from 'ioctl' to 'VIDIOC_LOG_STATUS'. The function is referenced by::

          * :c:func:`VIDIOC_LOG_STATUS` or
          * :any:`VIDIOC_LOG_STATUS` (``:any:`` needs sphinx 1.3)

     * Handle signatures of function-like macros well. Don't try to deduce
       arguments types of function-like macros.

"""

from docutils import nodes
from docutils.parsers.rst import directives

import sphinx
from sphinx import addnodes
from sphinx.domains.c import c_funcptr_sig_re, c_sig_re
from sphinx.domains.c import CObject as Base_CObject
from sphinx.domains.c import CDomain as Base_CDomain
from itertools import chain
import re

__version__  = '1.1'

# Get Sphinx version
major, minor, patch = sphinx.version_info[:3]

# Namespace to be prepended to the full name
namespace = None

#
# Handle trivial newer c domain tags that are part of Sphinx 3.1 c domain tags
# - Store the namespace if ".. c:namespace::" tag is found
#
RE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$')

def markup_namespace(match):
    global namespace

    namespace = match.group(1)

    return ""

#
# Handle c:macro for function-style declaration
#
RE_macro = re.compile(r'^\s*..\s*c:macro::\s*(\S+)\s+(\S.*)\s*$')
def markup_macro(match):
    return ".. c:function:: " + match.group(1) + ' ' + match.group(2)

#
# Handle newer c domain tags that are evaluated as .. c:type: for
# backward-compatibility with Sphinx < 3.0
#
RE_ctype = re.compile(r'^\s*..\s*c:(struct|union|enum|enumerator|alias)::\s*(.*)$')

def markup_ctype(match):
    return ".. c:type:: " + match.group(2)

#
# Handle newer c domain tags that are evaluated as :c:type: for
# backward-compatibility with Sphinx < 3.0
#
RE_ctype_refs = re.compile(r':c:(var|struct|union|enum|enumerator)::`([^\`]+)`')
def markup_ctype_refs(match):
    return ":c:type:`" + match.group(2) + '`'

#
# Simply convert :c:expr: and :c:texpr: into a literal block.
#
RE_expr = re.compile(r':c:(expr|texpr):`([^\`]+)`')
def markup_c_expr(match):
    return '\ ``' + match.group(2) + '``\ '

#
# Parse Sphinx 3.x C markups, replacing them by backward-compatible ones
#
def c_markups(app, docname, source):
    result = ""
    markup_func = {
        RE_namespace: markup_namespace,
        RE_expr: markup_c_expr,
        RE_macro: markup_macro,
        RE_ctype: markup_ctype,
        RE_ctype_refs: markup_ctype_refs,
    }

    lines = iter(source[0].splitlines(True))
    for n in lines:
        match_iterators = [regex.finditer(n) for regex in markup_func]
        matches = sorted(chain(*match_iterators), key=lambda m: m.start())
        for m in matches:
            n = n[:m.start()] + markup_func[m.re](m) + n[m.end():]

        result = result + n

    source[0] = result

#
# Now implements support for the cdomain namespacing logic
#

def setup(app):

    # Handle easy Sphinx 3.1+ simple new tags: :c:expr and .. c:namespace::
    app.connect('source-read', c_markups)

    if (major == 1 and minor < 8):
        app.override_domain(CDomain)
    else:
        app.add_domain(CDomain, override=True)

    return dict(
        version = __version__,
        parallel_read_safe = True,
        parallel_write_safe = True
    )

class CObject(Base_CObject):

    """
    Description of a C language object.
    """
    option_spec = {
        "name" : directives.unchanged
    }

    def handle_func_like_macro(self, sig, signode):
        u"""Handles signatures of function-like macros.

        If the objtype is 'function' and the the signature ``sig`` is a
        function-like macro, the name of the macro is returned. Otherwise
        ``False`` is returned.  """

        global namespace

        if not self.objtype == 'function':
            return False

        m = c_funcptr_sig_re.match(sig)
        if m is None:
            m = c_sig_re.match(sig)
            if m is None:
                raise ValueError('no match')

        rettype, fullname, arglist, _const = m.groups()
        arglist = arglist.strip()
        if rettype or not arglist:
            return False

        arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
        arglist = [a.strip() for a in arglist.split(",")]

        # has the first argument a type?
        if len(arglist[0].split(" ")) > 1:
            return False

        # This is a function-like macro, it's arguments are typeless!
        signode  += addnodes.desc_name(fullname, fullname)
        paramlist = addnodes.desc_parameterlist()
        signode  += paramlist

        for argname in arglist:
            param = addnodes.desc_parameter('', '', noemph=True)
            # separate by non-breaking space in the output
            param += nodes.emphasis(argname, argname)
            paramlist += param

        if namespace:
            fullname = namespace + "." + fullname

        return fullname

    def handle_signature(self, sig, signode):
        """Transform a C signature into RST nodes."""

        global namespace

        fullname = self.handle_func_like_macro(sig, signode)
        if not fullname:
            fullname = super(CObject, self).handle_signature(sig, signode)

        if "name" in self.options:
            if self.objtype == 'function':
                fullname = self.options["name"]
            else:
                # FIXME: handle :name: value of other declaration types?
                pass
        else:
            if namespace:
                fullname = namespace + "." + fullname

        return fullname

    def add_target_and_index(self, name, sig, signode):
        # for C API items we add a prefix since names are usually not qualified
        # by a module name and so easily clash with e.g. section titles
        targetname = 'c.' + name
        if targetname not in self.state.document.ids:
            signode['names'].append(targetname)
            signode['ids'].append(targetname)
            signode['first'] = (not self.names)
            self.state.document.note_explicit_target(signode)
            inv = self.env.domaindata['c']['objects']
            if (name in inv and self.env.config.nitpicky):
                if self.objtype == 'function':
                    if ('c:func', name) not in self.env.config.nitpick_ignore:
                        self.state_machine.reporter.warning(
                            'duplicate C object description of %s, ' % name +
                            'other instance in ' + self.env.doc2path(inv[name][0]),
                            line=self.lineno)
            inv[name] = (self.env.docname, self.objtype)

        indextext = self.get_index_text(name)
        if indextext:
            self.indexnode['entries'].append(
                    ('single', indextext, targetname, '', None))

class CDomain(Base_CDomain):

    """C language domain."""
    name = 'c'
    label = 'C'
    directives = {
        'function': CObject,
        'member':   CObject,
        'macro':    CObject,
        'type':     CObject,
        'var':      CObject,
    }


code:
# -*- coding: utf-8; mode: python -*-
# coding=utf-8
# SPDX-License-Identifier: GPL-2.0
#
u"""
    kernel-abi
    ~~~~~~~~~~

    Implementation of the ``kernel-abi`` reST-directive.

    :copyright:  Copyright (C) 2016  Markus Heiser
    :copyright:  Copyright (C) 2016-2020  Mauro Carvalho Chehab
    :maintained-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
    :license:    GPL Version 2, June 1991 see Linux/COPYING for details.

    The ``kernel-abi`` (:py:class:`KernelCmd`) directive calls the
    scripts/get_abi.pl script to parse the Kernel ABI files.

    Overview of directive's argument and options.

    .. code-block:: rst

        .. kernel-abi:: <ABI directory location>
            :debug:

    The argument ``<ABI directory location>`` is required. It contains the
    location of the ABI files to be parsed.

    ``debug``
      Inserts a code-block with the *raw* reST. Sometimes it is helpful to see
      what reST is generated.

"""

import codecs
import os
import subprocess
import sys
import re
import kernellog

from os import path

from docutils import nodes, statemachine
from docutils.statemachine import ViewList
from docutils.parsers.rst import directives, Directive
from docutils.utils.error_reporting import ErrorString
from sphinx.util.docutils import switch_source_input

__version__  = '1.0'

def setup(app):

    app.add_directive("kernel-abi", KernelCmd)
    return dict(
        version = __version__
        , parallel_read_safe = True
        , parallel_write_safe = True
    )

class KernelCmd(Directive):

    u"""KernelABI (``kernel-abi``) directive"""

    required_arguments = 1
    optional_arguments = 2
    has_content = False
    final_argument_whitespace = True

    option_spec = {
        "debug"     : directives.flag,
        "rst"       : directives.unchanged
    }

    def run(self):

        doc = self.state.document
        if not doc.settings.file_insertion_enabled:
            raise self.warning("docutils: file insertion disabled")

        env = doc.settings.env
        cwd = path.dirname(doc.current_source)
        cmd = "get_abi.pl rest --enable-lineno --dir "
        cmd += self.arguments[0]

        if 'rst' in self.options:
            cmd += " --rst-source"

        srctree = path.abspath(os.environ["srctree"])

        fname = cmd

        # extend PATH with $(srctree)/scripts
        path_env = os.pathsep.join([
            srctree + os.sep + "scripts",
            os.environ["PATH"]
        ])
        shell_env = os.environ.copy()
        shell_env["PATH"]    = path_env
        shell_env["srctree"] = srctree

        lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env)
        nodeList = self.nestedParse(lines, self.arguments[0])
        return nodeList

    def runCmd(self, cmd, **kwargs):
        u"""Run command ``cmd`` and return its stdout as unicode."""

        try:
            proc = subprocess.Popen(
                cmd
                , stdout = subprocess.PIPE
                , stderr = subprocess.PIPE
                , **kwargs
            )
            out, err = proc.communicate()

            out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')

            if proc.returncode != 0:
                raise self.severe(
                    u"command '%s' failed with return code %d"
                    % (cmd, proc.returncode)
                )
        except OSError as exc:
            raise self.severe(u"problems with '%s' directive: %s."
                              % (self.name, ErrorString(exc)))
        return out

    def nestedParse(self, lines, fname):
        env = self.state.document.settings.env
        content = ViewList()
        node = nodes.section()

        if "debug" in self.options:
            code_block = "\n\n.. code-block:: rst\n    :linenos:\n"
            for l in lines.split("\n"):
                code_block += "\n    " + l
            lines = code_block + "\n\n"

        line_regex = re.compile("^\.\. LINENO (\S+)\#([0-9]+)$")
        ln = 0
        n = 0
        f = fname

        for line in lines.split("\n"):
            n = n + 1
            match = line_regex.search(line)
            if match:
                new_f = match.group(1)

                # Sphinx parser is lazy: it stops parsing contents in the
                # middle, if it is too big. So, handle it per input file
                if new_f != f and content:
                    self.do_parse(content, node)
                    content = ViewList()

                    # Add the file to Sphinx build dependencies
                    env.note_dependency(os.path.abspath(f))

                f = new_f

                # sphinx counts lines from 0
                ln = int(match.group(2)) - 1
            else:
                content.append(line, f, ln)

        kernellog.info(self.state.document.settings.env.app, "%s: parsed %i lines" % (fname, n))

        if content:
            self.do_parse(content, node)

        return node.children

    def do_parse(self, content, node):
        with switch_source_input(self.state, content):
            self.state.nested_parse(content, 0, node, match_titles=1)


code:
# coding=utf-8
# SPDX-License-Identifier: GPL-2.0
#
u"""
    kernel-feat
    ~~~~~~~~~~~

    Implementation of the ``kernel-feat`` reST-directive.

    :copyright:  Copyright (C) 2016  Markus Heiser
    :copyright:  Copyright (C) 2016-2019  Mauro Carvalho Chehab
    :maintained-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
    :license:    GPL Version 2, June 1991 see Linux/COPYING for details.

    The ``kernel-feat`` (:py:class:`KernelFeat`) directive calls the
    scripts/get_feat.pl script to parse the Kernel ABI files.

    Overview of directive's argument and options.

    .. code-block:: rst

        .. kernel-feat:: <ABI directory location>
            :debug:

    The argument ``<ABI directory location>`` is required. It contains the
    location of the ABI files to be parsed.

    ``debug``
      Inserts a code-block with the *raw* reST. Sometimes it is helpful to see
      what reST is generated.

"""

import codecs
import os
import re
import subprocess
import sys

from os import path

from docutils import nodes, statemachine
from docutils.statemachine import ViewList
from docutils.parsers.rst import directives, Directive
from docutils.utils.error_reporting import ErrorString
from sphinx.util.docutils import switch_source_input

__version__  = '1.0'

def setup(app):

    app.add_directive("kernel-feat", KernelFeat)
    return dict(
        version = __version__
        , parallel_read_safe = True
        , parallel_write_safe = True
    )

class KernelFeat(Directive):

    u"""KernelFeat (``kernel-feat``) directive"""

    required_arguments = 1
    optional_arguments = 2
    has_content = False
    final_argument_whitespace = True

    option_spec = {
        "debug"     : directives.flag
    }

    def warn(self, message, **replace):
        replace["fname"]   = self.state.document.current_source
        replace["line_no"] = replace.get("line_no", self.lineno)
        message = ("%(fname)s:%(line_no)s: [kernel-feat WARN] : " + message) % replace
        self.state.document.settings.env.app.warn(message, prefix="")

    def run(self):

        doc = self.state.document
        if not doc.settings.file_insertion_enabled:
            raise self.warning("docutils: file insertion disabled")

        env = doc.settings.env
        cwd = path.dirname(doc.current_source)
        cmd = "get_feat.pl rest --enable-fname --dir "
        cmd += self.arguments[0]

        if len(self.arguments) > 1:
            cmd += " --arch " + self.arguments[1]

        srctree = path.abspath(os.environ["srctree"])

        fname = cmd

        # extend PATH with $(srctree)/scripts
        path_env = os.pathsep.join([
            srctree + os.sep + "scripts",
            os.environ["PATH"]
        ])
        shell_env = os.environ.copy()
        shell_env["PATH"]    = path_env
        shell_env["srctree"] = srctree

        lines = self.runCmd(cmd, shell=True, cwd=cwd, env=shell_env)

        line_regex = re.compile("^\.\. FILE (\S+)$")

        out_lines = ""

        for line in lines.split("\n"):
            match = line_regex.search(line)
            if match:
                fname = match.group(1)

                # Add the file to Sphinx build dependencies
                env.note_dependency(os.path.abspath(fname))
            else:
                out_lines += line + "\n"

        nodeList = self.nestedParse(out_lines, fname)
        return nodeList

    def runCmd(self, cmd, **kwargs):
        u"""Run command ``cmd`` and return its stdout as unicode."""

        try:
            proc = subprocess.Popen(
                cmd
                , stdout = subprocess.PIPE
                , stderr = subprocess.PIPE
                , **kwargs
            )
            out, err = proc.communicate()

            out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')

            if proc.returncode != 0:
                raise self.severe(
                    u"command '%s' failed with return code %d"
                    % (cmd, proc.returncode)
                )
        except OSError as exc:
            raise self.severe(u"problems with '%s' directive: %s."
                              % (self.name, ErrorString(exc)))
        return out

    def nestedParse(self, lines, fname):
        content = ViewList()
        node    = nodes.section()

        if "debug" in self.options:
            code_block = "\n\n.. code-block:: rst\n    :linenos:\n"
            for l in lines.split("\n"):
                code_block += "\n    " + l
            lines = code_block + "\n\n"

        for c, l in enumerate(lines.split("\n")):
            content.append(l, fname, c)

        buf  = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter

        with switch_source_input(self.state, content):
            self.state.nested_parse(content, 0, node, match_titles=1)

        return node.children


code:
#!/usr/bin/env python3
# -*- coding: utf-8; mode: python -*-
# pylint: disable=R0903, C0330, R0914, R0912, E0401

u"""
    kernel-include
    ~~~~~~~~~~~~~~

    Implementation of the ``kernel-include`` reST-directive.

    :copyright:  Copyright (C) 2016  Markus Heiser
    :license:    GPL Version 2, June 1991 see linux/COPYING for details.

    The ``kernel-include`` reST-directive is a replacement for the ``include``
    directive. The ``kernel-include`` directive expand environment variables in
    the path name and allows to include files from arbitrary locations.

    .. hint::

      Including files from arbitrary locations (e.g. from ``/etc``) is a
      security risk for builders. This is why the ``include`` directive from
      docutils *prohibit* pathnames pointing to locations *above* the filesystem
      tree where the reST document with the include directive is placed.

    Substrings of the form $name or ${name} are replaced by the value of
    environment variable name. Malformed variable names and references to
    non-existing variables are left unchanged.
"""

# ==============================================================================
# imports
# ==============================================================================

import os.path

from docutils import io, nodes, statemachine
from docutils.utils.error_reporting import SafeString, ErrorString
from docutils.parsers.rst import directives
from docutils.parsers.rst.directives.body import CodeBlock, NumberLines
from docutils.parsers.rst.directives.misc import Include

__version__  = '1.0'

# ==============================================================================
def setup(app):
# ==============================================================================

    app.add_directive("kernel-include", KernelInclude)
    return dict(
        version = __version__,
        parallel_read_safe = True,
        parallel_write_safe = True
    )

# ==============================================================================
class KernelInclude(Include):
# ==============================================================================

    u"""KernelInclude (``kernel-include``) directive"""

    def run(self):
        env = self.state.document.settings.env
        path = os.path.realpath(
            os.path.expandvars(self.arguments[0]))

        # to get a bit security back, prohibit /etc:
        if path.startswith(os.sep + "etc"):
            raise self.severe(
                'Problems with "%s" directive, prohibited path: %s'
                % (self.name, path))

        self.arguments[0] = path

        env.note_dependency(os.path.abspath(path))

        #return super(KernelInclude, self).run() # won't work, see HINTs in _run()
        return self._run()

    def _run(self):
        """Include a file as part of the content of this reST file."""

        # HINT: I had to copy&paste the whole Include.run method. I'am not happy
        # with this, but due to security reasons, the Include.run method does
        # not allow absolute or relative pathnames pointing to locations *above*
        # the filesystem tree where the reST document is placed.

        if not self.state.document.settings.file_insertion_enabled:
            raise self.warning('"%s" directive disabled.' % self.name)
        source = self.state_machine.input_lines.source(
            self.lineno - self.state_machine.input_offset - 1)
        source_dir = os.path.dirname(os.path.abspath(source))
        path = directives.path(self.arguments[0])
        if path.startswith('<') and path.endswith('>'):
            path = os.path.join(self.standard_include_path, path[1:-1])
        path = os.path.normpath(os.path.join(source_dir, path))

        # HINT: this is the only line I had to change / commented out:
        #path = utils.relative_path(None, path)

        path = nodes.reprunicode(path)
        encoding = self.options.get(
            'encoding', self.state.document.settings.input_encoding)
        e_handler=self.state.document.settings.input_encoding_error_handler
        tab_width = self.options.get(
            'tab-width', self.state.document.settings.tab_width)
        try:
            self.state.document.settings.record_dependencies.add(path)
            include_file = io.FileInput(source_path=path,
                                        encoding=encoding,
                                        error_handler=e_handler)
        except UnicodeEncodeError as error:
            raise self.severe('Problems with "%s" directive path:\n'
                              'Cannot encode input file path "%s" '
                              '(wrong locale?).' %
                              (self.name, SafeString(path)))
        except IOError as error:
            raise self.severe('Problems with "%s" directive path:\n%s.' %
                      (self.name, ErrorString(error)))
        startline = self.options.get('start-line', None)
        endline = self.options.get('end-line', None)
        try:
            if startline or (endline is not None):
                lines = include_file.readlines()
                rawtext = ''.join(lines[startline:endline])
            else:
                rawtext = include_file.read()
        except UnicodeError as error:
            raise self.severe('Problem with "%s" directive:\n%s' %
                              (self.name, ErrorString(error)))
        # start-after/end-before: no restrictions on newlines in match-text,
        # and no restrictions on matching inside lines vs. line boundaries
        after_text = self.options.get('start-after', None)
        if after_text:
            # skip content in rawtext before *and incl.* a matching text
            after_index = rawtext.find(after_text)
            if after_index < 0:
                raise self.severe('Problem with "start-after" option of "%s" '
                                  'directive:\nText not found.' % self.name)
            rawtext = rawtext[after_index + len(after_text):]
        before_text = self.options.get('end-before', None)
        if before_text:
            # skip content in rawtext after *and incl.* a matching text
            before_index = rawtext.find(before_text)
            if before_index < 0:
                raise self.severe('Problem with "end-before" option of "%s" '
                                  'directive:\nText not found.' % self.name)
            rawtext = rawtext[:before_index]

        include_lines = statemachine.string2lines(rawtext, tab_width,
                                                  convert_whitespace=True)
        if 'literal' in self.options:
            # Convert tabs to spaces, if `tab_width` is positive.
            if tab_width >= 0:
                text = rawtext.expandtabs(tab_width)
            else:
                text = rawtext
            literal_block = nodes.literal_block(rawtext, source=path,
                                    classes=self.options.get('class', []))
            literal_block.line = 1
            self.add_name(literal_block)
            if 'number-lines' in self.options:
                try:
                    startline = int(self.options['number-lines'] or 1)
                except ValueError:
                    raise self.error(':number-lines: with non-integer '
                                     'start value')
                endline = startline + len(include_lines)
                if text.endswith('\n'):
                    text = text[:-1]
                tokens = NumberLines([([], text)], startline, endline)
                for classes, value in tokens:
                    if classes:
                        literal_block += nodes.inline(value, value,
                                                      classes=classes)
                    else:
                        literal_block += nodes.Text(value, value)
            else:
                literal_block += nodes.Text(text, text)
            return [literal_block]
        if 'code' in self.options:
            self.options['source'] = path
            codeblock = CodeBlock(self.name,
                                  [self.options.pop('code')], # arguments
                                  self.options,
                                  include_lines, # content
                                  self.lineno,
                                  self.content_offset,
                                  self.block_text,
                                  self.state,
                                  self.state_machine)
            return codeblock.run()
        self.state_machine.insert_input(include_lines, path)
        return []


code:
% -*- coding: utf-8 -*-
% SPDX-License-Identifier: GPL-2.0
%
% LaTeX preamble for "make latexdocs" or "make pdfdocs" including:
%   - TOC width settings
%   - Setting of tabulary (\tymin)
%   - Headheight setting for fancyhdr
%   - Fontfamily settings for CJK (Chinese, Japanese, and Korean) translations
%
% Note on the suffix of .sty:
%   This is not implemented as a LaTeX style file, but as a file containing
%   plain LaTeX code to be included into preamble.
%   ".sty" is chosen because ".tex" would cause the build scripts to confuse
%   this file with a LaTeX main file.
%
% Copyright (C) 2022  Akira Yokosawa

% Custom width parameters for TOC
%  - Redefine low-level commands defined in report.cls.
%  - Indent of 2 chars is preserved for ease of comparison.
% Summary of changes from default params:
%   Width of page number (\@pnumwidth): 1.55em -> 2.7em
%   Width of chapter number:            1.5em  -> 2.4em
%   Indent of section number:           1.5em  -> 2.4em
%   Width of section number:            2.6em  -> 3.2em
%   Indent of subsection number:        4.1em  -> 5.6em
%   Width of subsection number:         3.5em  -> 4.3em
%
% These params can have 4 digit page counts, 3 digit chapter counts,
% section counts of 4 digits + 1 period (e.g., 18.10), and subsection counts
% of 5 digits + 2 periods (e.g., 18.7.13).
\makeatletter
%% Redefine \@pnumwidth (page number width)
\renewcommand*\@pnumwidth{2.7em}
%% Redefine \l@chapter (chapter list entry)
\renewcommand*\l@chapter[2]{%
  \ifnum \c@tocdepth >\m@ne
    \addpenalty{-\@highpenalty}%
    \vskip 1.0em \@plus\p@
    \setlength\@tempdima{2.4em}%
    \begingroup
      \parindent \z@ \rightskip \@pnumwidth
      \parfillskip -\@pnumwidth
      \leavevmode \bfseries
      \advance\leftskip\@tempdima
      \hskip -\leftskip
      #1\nobreak\hfil
      \nobreak\hb@xt@\@pnumwidth{\hss #2%
                                 \kern-\p@\kern\p@}\par
      \penalty\@highpenalty
    \endgroup
  \fi}
%% Redefine \l@section and \l@subsection
\renewcommand*\l@section{\@dottedtocline{1}{2.4em}{3.2em}}
\renewcommand*\l@subsection{\@dottedtocline{2}{5.6em}{4.3em}}
\makeatother
%% Sphinx < 1.8 doesn't have \sphinxtableofcontentshook
\providecommand{\sphinxtableofcontentshook}{}
%% Undefine it for compatibility with Sphinx 1.7.9
\renewcommand{\sphinxtableofcontentshook}{} % Empty the hook

% Prevent column squeezing of tabulary.  \tymin is set by Sphinx as:
%   \setlength{\tymin}{3\fontcharwd\font`0 }
% , which is too short.
\setlength{\tymin}{20em}

% Adjust \headheight for fancyhdr
\addtolength{\headheight}{1.6pt}
\addtolength{\topmargin}{-1.6pt}

% Translations have Asian (CJK) characters which are only displayed if
% xeCJK is used
\usepackage{ifthen}
\newboolean{enablecjk}
\setboolean{enablecjk}{false}
\IfFontExistsTF{Noto Sans CJK SC}{
    \IfFileExists{xeCJK.sty}{
	\setboolean{enablecjk}{true}
    }{}
}{}
\ifthenelse{\boolean{enablecjk}}{
    % Load xeCJK when both the Noto Sans CJK font and xeCJK.sty are available.
    \usepackage{xeCJK}
    % Noto CJK fonts don't provide slant shape. [AutoFakeSlant] permits
    % its emulation.
    % Select KR variant at the beginning of each document so that quotation
    % and apostorph symbols of half-width is used in TOC of Latin documents.
    \IfFontExistsTF{Noto Serif CJK KR}{
	\setCJKmainfont{Noto Serif CJK KR}[AutoFakeSlant]
    }{
	\setCJKmainfont{Noto Sans CJK KR}[AutoFakeSlant]
    }
    \setCJKsansfont{Noto Sans CJK KR}[AutoFakeSlant]
    \setCJKmonofont{Noto Sans Mono CJK KR}[AutoFakeSlant]
    % Teach xeCJK of half-width symbols
    \xeCJKDeclareCharClass{HalfLeft}{`“,`‘}
    \xeCJKDeclareCharClass{HalfRight}{`”,`’}
    % CJK Language-specific font choices
    %% for Simplified Chinese
    \IfFontExistsTF{Noto Serif CJK SC}{
	\newCJKfontfamily[SCmain]\scmain{Noto Serif CJK SC}[AutoFakeSlant]
	\newCJKfontfamily[SCserif]\scserif{Noto Serif CJK SC}[AutoFakeSlant]
    }{
	\newCJKfontfamily[SCmain]\scmain{Noto Sans CJK SC}[AutoFakeSlant]
	\newCJKfontfamily[SCserif]\scserif{Noto Sans CJK SC}[AutoFakeSlant]
    }
    \newCJKfontfamily[SCsans]\scsans{Noto Sans CJK SC}[AutoFakeSlant]
    \newCJKfontfamily[SCmono]\scmono{Noto Sans Mono CJK SC}[AutoFakeSlant]
    %% for Traditional Chinese
    \IfFontExistsTF{Noto Serif CJK TC}{
	\newCJKfontfamily[TCmain]\tcmain{Noto Serif CJK TC}[AutoFakeSlant]
	\newCJKfontfamily[TCserif]\tcserif{Noto Serif CJK TC}[AutoFakeSlant]
    }{
	\newCJKfontfamily[TCmain]\tcmain{Noto Sans CJK TC}[AutoFakeSlant]
	\newCJKfontfamily[TCserif]\tcserif{Noto Sans CJK TC}[AutoFakeSlant]
    }
    \newCJKfontfamily[TCsans]\tcsans{Noto Sans CJK TC}[AutoFakeSlant]
    \newCJKfontfamily[TCmono]\tcmono{Noto Sans Mono CJK TC}[AutoFakeSlant]
    %% for Korean
    \IfFontExistsTF{Noto Serif CJK KR}{
	\newCJKfontfamily[KRmain]\krmain{Noto Serif CJK KR}[AutoFakeSlant]
	\newCJKfontfamily[KRserif]\krserif{Noto Serif CJK KR}[AutoFakeSlant]
    }{
	\newCJKfontfamily[KRmain]\krmain{Noto Sans CJK KR}[AutoFakeSlant]
	\newCJKfontfamily[KRserif]\krserif{Noto Sans CJK KR}[AutoFakeSlant]
    }
    \newCJKfontfamily[KRsans]\krsans{Noto Sans CJK KR}[AutoFakeSlant]
    \newCJKfontfamily[KRmono]\krmono{Noto Sans Mono CJK KR}[AutoFakeSlant]
    %% for Japanese
    \IfFontExistsTF{Noto Serif CJK JP}{
	\newCJKfontfamily[JPmain]\jpmain{Noto Serif CJK JP}[AutoFakeSlant]
	\newCJKfontfamily[JPserif]\jpserif{Noto Serif CJK JP}[AutoFakeSlant]
    }{
	\newCJKfontfamily[JPmain]\jpmain{Noto Sans CJK JP}[AutoFakeSlant]
	\newCJKfontfamily[JPserif]\jpserif{Noto Sans CJK JP}[AutoFakeSlant]
    }
    \newCJKfontfamily[JPsans]\jpsans{Noto Sans CJK JP}[AutoFakeSlant]
    \newCJKfontfamily[JPmono]\jpmono{Noto Sans Mono CJK JP}[AutoFakeSlant]
    % Dummy commands for Sphinx < 2.3 (no 'extrapackages' support)
    \providecommand{\onehalfspacing}{}
    \providecommand{\singlespacing}{}
    % Define custom macros to on/off CJK
    %% One and half spacing for CJK contents
    \newcommand{\kerneldocCJKon}{\makexeCJKactive\onehalfspacing}
    \newcommand{\kerneldocCJKoff}{\makexeCJKinactive\singlespacing}
    % Define custom macros for switching CJK font setting
    %% for Simplified Chinese
    \newcommand{\kerneldocBeginSC}{%
	\begingroup%
	\scmain%
	\xeCJKDeclareCharClass{FullLeft}{`“,`‘}% Full-width in SC
	\xeCJKDeclareCharClass{FullRight}{`”,`’}% Full-width in SC
	\renewcommand{\CJKrmdefault}{SCserif}%
	\renewcommand{\CJKsfdefault}{SCsans}%
	\renewcommand{\CJKttdefault}{SCmono}%
	\xeCJKsetup{CJKspace = false}% gobble white spaces by ' '
	% For CJK ascii-art alignment
	\setmonofont{Noto Sans Mono CJK SC}[AutoFakeSlant]%
    }
    \newcommand{\kerneldocEndSC}{\endgroup}
    %% for Traditional Chinese
    \newcommand{\kerneldocBeginTC}{%
	\begingroup%
	\tcmain%
	\xeCJKDeclareCharClass{FullLeft}{`“,`‘}% Full-width in TC
	\xeCJKDeclareCharClass{FullRight}{`”,`’}% Full-width in TC
	\renewcommand{\CJKrmdefault}{TCserif}%
	\renewcommand{\CJKsfdefault}{TCsans}%
	\renewcommand{\CJKttdefault}{TCmono}%
	\xeCJKsetup{CJKspace = false}% gobble white spaces by ' '
	% For CJK ascii-art alignment
	\setmonofont{Noto Sans Mono CJK TC}[AutoFakeSlant]%
    }
    \newcommand{\kerneldocEndTC}{\endgroup}
    %% for Korean
    \newcommand{\kerneldocBeginKR}{%
	\begingroup%
	\krmain%
	\renewcommand{\CJKrmdefault}{KRserif}%
	\renewcommand{\CJKsfdefault}{KRsans}%
	\renewcommand{\CJKttdefault}{KRmono}%
	% \xeCJKsetup{CJKspace = true} % true by default
	% For CJK ascii-art alignment (still misaligned for Hangul)
	\setmonofont{Noto Sans Mono CJK KR}[AutoFakeSlant]%
    }
    \newcommand{\kerneldocEndKR}{\endgroup}
    %% for Japanese
    \newcommand{\kerneldocBeginJP}{%
	\begingroup%
	\jpmain%
	\renewcommand{\CJKrmdefault}{JPserif}%
	\renewcommand{\CJKsfdefault}{JPsans}%
	\renewcommand{\CJKttdefault}{JPmono}%
	\xeCJKsetup{CJKspace = false}% gobble white space by ' '
	% For CJK ascii-art alignment
	\setmonofont{Noto Sans Mono CJK JP}[AutoFakeSlant]%
    }
    \newcommand{\kerneldocEndJP}{\endgroup}

    % Single spacing in literal blocks
    \fvset{baselinestretch=1}
    % To customize \sphinxtableofcontents
    \usepackage{etoolbox}
    % Inactivate CJK after tableofcontents
    \apptocmd{\sphinxtableofcontents}{\kerneldocCJKoff}{}{}
    \xeCJKsetup{CJKspace = true}% For inter-phrase space of Korean TOC
}{ % Don't enable CJK
    % Custom macros to on/off CJK and switch CJK fonts (Dummy)
    \newcommand{\kerneldocCJKon}{}
    \newcommand{\kerneldocCJKoff}{}
    %% By defining \kerneldocBegin(SC|TC|KR|JP) as commands with an argument
    %% and ignore the argument (#1) in their definitions, whole contents of
    %% CJK chapters can be ignored.
    \newcommand{\kerneldocBeginSC}[1]{%
	%% Put a note on missing CJK fonts or the xecjk package in place of
	%% zh_CN translation.
	\begin{sphinxadmonition}{note}{Note on missing fonts and a package:}
	    Translations of Simplified Chinese (zh\_CN), Traditional Chinese
	    (zh\_TW), Korean (ko\_KR), and Japanese (ja\_JP) were skipped
	    due to the lack of suitable font families and/or the texlive-xecjk
	    package.

	    If you want them, please install ``Noto Sans CJK'' font families
	    along with the texlive-xecjk package by following instructions from
	    \sphinxcode{./scripts/sphinx-pre-install}.
	    Having optional ``Noto Serif CJK'' font families will improve
	    the looks of those translations.
	\end{sphinxadmonition}}
    \newcommand{\kerneldocEndSC}{}
    \newcommand{\kerneldocBeginTC}[1]{}
    \newcommand{\kerneldocEndTC}{}
    \newcommand{\kerneldocBeginKR}[1]{}
    \newcommand{\kerneldocEndKR}{}
    \newcommand{\kerneldocBeginJP}[1]{}
    \newcommand{\kerneldocEndJP}{}
}


code:
# coding=utf-8
#
# Copyright © 2016 Intel Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# Authors:
#    Jani Nikula <jani.nikula@intel.com>
#
# Please make sure this works on both python2 and python3.
#

import codecs
import os
import subprocess
import sys
import re
import glob

from docutils import nodes, statemachine
from docutils.statemachine import ViewList
from docutils.parsers.rst import directives, Directive
import sphinx
from sphinx.util.docutils import switch_source_input
import kernellog

__version__  = '1.0'

class KernelDocDirective(Directive):
    """Extract kernel-doc comments from the specified file"""
    required_argument = 1
    optional_arguments = 4
    option_spec = {
        'doc': directives.unchanged_required,
        'export': directives.unchanged,
        'internal': directives.unchanged,
        'identifiers': directives.unchanged,
        'no-identifiers': directives.unchanged,
        'functions': directives.unchanged,
    }
    has_content = False

    def run(self):
        env = self.state.document.settings.env
        cmd = [env.config.kerneldoc_bin, '-rst', '-enable-lineno']

	# Pass the version string to kernel-doc, as it needs to use a different
	# dialect, depending what the C domain supports for each specific
	# Sphinx versions
        cmd += ['-sphinx-version', sphinx.__version__]

        filename = env.config.kerneldoc_srctree + '/' + self.arguments[0]
        export_file_patterns = []

        # Tell sphinx of the dependency
        env.note_dependency(os.path.abspath(filename))

        tab_width = self.options.get('tab-width', self.state.document.settings.tab_width)

        # 'function' is an alias of 'identifiers'
        if 'functions' in self.options:
            self.options['identifiers'] = self.options.get('functions')

        # FIXME: make this nicer and more robust against errors
        if 'export' in self.options:
            cmd += ['-export']
            export_file_patterns = str(self.options.get('export')).split()
        elif 'internal' in self.options:
            cmd += ['-internal']
            export_file_patterns = str(self.options.get('internal')).split()
        elif 'doc' in self.options:
            cmd += ['-function', str(self.options.get('doc'))]
        elif 'identifiers' in self.options:
            identifiers = self.options.get('identifiers').split()
            if identifiers:
                for i in identifiers:
                    cmd += ['-function', i]
            else:
                cmd += ['-no-doc-sections']

        if 'no-identifiers' in self.options:
            no_identifiers = self.options.get('no-identifiers').split()
            if no_identifiers:
                for i in no_identifiers:
                    cmd += ['-nosymbol', i]

        for pattern in export_file_patterns:
            for f in glob.glob(env.config.kerneldoc_srctree + '/' + pattern):
                env.note_dependency(os.path.abspath(f))
                cmd += ['-export-file', f]

        cmd += [filename]

        try:
            kernellog.verbose(env.app,
                              'calling kernel-doc \'%s\'' % (" ".join(cmd)))

            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            out, err = p.communicate()

            out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8')

            if p.returncode != 0:
                sys.stderr.write(err)

                kernellog.warn(env.app,
                               'kernel-doc \'%s\' failed with return code %d' % (" ".join(cmd), p.returncode))
                return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))]
            elif env.config.kerneldoc_verbosity > 0:
                sys.stderr.write(err)

            lines = statemachine.string2lines(out, tab_width, convert_whitespace=True)
            result = ViewList()

            lineoffset = 0;
            line_regex = re.compile("^\.\. LINENO ([0-9]+)$")
            for line in lines:
                match = line_regex.search(line)
                if match:
                    # sphinx counts lines from 0
                    lineoffset = int(match.group(1)) - 1
                    # we must eat our comments since the upset the markup
                else:
                    doc = env.srcdir + "/" + env.docname + ":" + str(self.lineno)
                    result.append(line, doc + ": " + filename, lineoffset)
                    lineoffset += 1

            node = nodes.section()
            self.do_parse(result, node)

            return node.children

        except Exception as e:  # pylint: disable=W0703
            kernellog.warn(env.app, 'kernel-doc \'%s\' processing failed with: %s' %
                           (" ".join(cmd), str(e)))
            return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))]

    def do_parse(self, result, node):
        with switch_source_input(self.state, result):
            self.state.nested_parse(result, 0, node, match_titles=1)

def setup(app):
    app.add_config_value('kerneldoc_bin', None, 'env')
    app.add_config_value('kerneldoc_srctree', None, 'env')
    app.add_config_value('kerneldoc_verbosity', 1, 'env')

    app.add_directive('kernel-doc', KernelDocDirective)

    return dict(
        version = __version__,
        parallel_read_safe = True,
        parallel_write_safe = True
    )


code:
# SPDX-License-Identifier: GPL-2.0
#
# Sphinx has deprecated its older logging interface, but the replacement
# only goes back to 1.6.  So here's a wrapper layer to keep around for
# as long as we support 1.4.
#
# We don't support 1.4 anymore, but we'll keep the wrappers around until
# we change all the code to not use them anymore :)
#
import sphinx
from sphinx.util import logging

logger = logging.getLogger('kerneldoc')

def warn(app, message):
    logger.warning(message)

def verbose(app, message):
    logger.verbose(message)

def info(app, message):
    logger.info(message)