diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65df588 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +mdevctl.spec +*.tar.gz +*.rpm +*/ diff --git a/60-mdevctl.rules b/60-mdevctl.rules new file mode 100644 index 0000000..6e0e056 --- /dev/null +++ b/60-mdevctl.rules @@ -0,0 +1,8 @@ +# When registered with mdev, try to start any persistent devices. Note that +# this uevent is not triggered on older kernels. +ACTION=="change", ENV{MDEV_STATE}=="registered", TEST=="/etc/mdevctl.d/$kernel", RUN+="/bin/sh -c '/usr/sbin/mdevctl start-parent-mdevs %k'" + +# For compatibility with kernels where mdev doesn't trigger the change uevent +# on parent device registration, try to start mdevs when the device is added; +# if the device is setup early this may still work. +ACTION=="add", TEST=="/etc/mdevctl.d/$kernel", RUN+="/bin/sh -c '/usr/sbin/mdevctl start-parent-mdevs %k'" diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e332777 --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +PREFIX=/usr +UDEVDIR=$(shell pkg-config --variable=udevdir udev) +SBINDIR=$(PREFIX)/sbin +CONFDIR=/etc/mdevctl.d +MANDIR=$(PREFIX)/share/man +NAME=mdevctl +VERSION=0.$(shell git rev-list --count HEAD) +NVFMT=$(NAME)-$(VERSION) + +files: mdevctl 60-mdevctl.rules mdevctl.8 \ + Makefile COPYING README.md mdevctl.spec.in + +archive: files mdevctl.spec + git archive --prefix=$(NVFMT)/ HEAD > $(NVFMT).tar + gzip -f -9 $(NVFMT).tar + +mdevctl.spec: tag mdevctl.spec.in files + sed -e 's:#VERSION#:$(VERSION):g' < mdevctl.spec.in > mdevctl.spec + PREV=""; \ + for TAG in `git tag --sort=version:refname | tac`; do \ + if [ -n "$$PREV" ]; then \ + git log --format="- %h (\"%s\")" $$TAG..$$PREV >> mdevctl.spec; \ + fi; \ + git log -1 --format="%n* %cd %aN <%ae> - $$TAG-1" --date="format:%a %b %d %Y" $$TAG >> mdevctl.spec; \ + PREV=$$TAG; \ + done; \ + git log --format="- %h (\"%s\")" $$TAG >> mdevctl.spec + +srpm: mdevctl.spec archive + rpmbuild -bs --define "_sourcedir $(PWD)" --define "_specdir $(PWD)" --define "_builddir $(PWD)" --define "_srcrpmdir $(PWD)" --define "_rpmdir $(PWD)" mdevctl.spec + +rpm: mdevctl.spec archive + rpmbuild -bb --define "_sourcedir $(PWD)" --define "_specdir $(PWD)" --define "_builddir $(PWD)" --define "_srcrpmdir $(PWD)" --define "_rpmdir $(PWD)" mdevctl.spec + +install: + mkdir -p $(DESTDIR)$(CONFDIR) + mkdir -p $(DESTDIR)$(UDEVDIR)/rules.d/ + install -m 644 60-mdevctl.rules $(DESTDIR)$(UDEVDIR)/rules.d/ + mkdir -p $(DESTDIR)$(SBINDIR) + install -m 755 mdevctl $(DESTDIR)$(SBINDIR)/ + ln -s mdevctl $(DESTDIR)$(SBINDIR)/lsmdev + mkdir -p $(DESTDIR)$(MANDIR)/man8 + install -m 644 mdevctl.8 $(DESTDIR)$(MANDIR)/man8/ + ln -s mdevctl.8 $(DESTDIR)$(MANDIR)/man8/lsmdev.8 + +clean: + rm -f mdevctl.spec *.src.rpm noarch/*.rpm *.tar.gz + +tag: + git tag -l $(VERSION) | grep -q $(VERSION) || git tag $(VERSION) diff --git a/README.md b/README.md new file mode 100644 index 0000000..9696dba --- /dev/null +++ b/README.md @@ -0,0 +1,270 @@ +# mdevctl - a mediated device management utility for Linux + +## Description + +mdevctl is a utility for managing and persisting devices in the +mediated device device framework of the Linux kernel. Mediated +devices are sub-devices of a parent device (ex. a vGPU) which +can be dynamically created and potentially used by drivers like +vfio-mdev for assignment to virtual machines. + +## License + +Licensed under the GNU Lesser General Public License aka LGPL v2.1. +See [COPYING](COPYING) for details. + +## Source repository + +https://github.com/mdevctl/mdevctl + +## Installation + +On RPM based systems, `make rpm` then install the resulting package. +Otherwise, `make install` + +## Architecture + +mdevctl stores defined mediated devices in /etc/mdevctl.d/ with +directories matching the parent device name and config files named +by the UUID of the mdev device itself. The format used is JSON; a +configuration file for an mdev device looks like follows: + +``` + { + "mdev_type": "$VENDOR_TYPE", + "start": "auto|manual", + "attrs": [ + ...optional list of device-specific attributes... + ] + } +``` + +When a known parent device add udev event occurs (or, for more recent +kernels, change events with MDEV_STATE values), mdevctl is called by +a udev rule to create defined devices with "start": "auto" configured. + +mdevctl defines three classes of commands, those that manage device +config files, those that manage the device itself, and listing +commands for showing defined, active, or potential mdev devices. + +Starting with the latter, mdevctl is able to manage mdev devices +created either with mdevctl or externally, such as through direct +sysfs interactions. Likewise, when generating a list of currently +active mdev devices via the `list` command, all mdevs are included. +When provided with the `--defined` option, the list command will show +mdev device configs defined on the system, regardless of whether they +are currently active. The `types` command provides details of the +mdev types supported on the system, including the number of +instances of each that may be created, the API exposed for each, as +well as vendor provided name and description as available. + +Mediated device definitions can be created with the `define` command, +which not only accepts a fully specified configuration via options, +but can also create a config for a currently running mdev. Thus a +transient device created either through mdevctl or sysfs can be +promoted to a defined device. The `undefine` command simply removes +the config definition without modifying the running device, while +the `modify` command allows device config to be modified. Config +modifications to a running device to not take effect until the device +is stopped and restarted. + +This leads to the final class of commands, which provides the `start` +and `stop` functionality. The start command can operate either on +a previously defined mdev or the mdev can be fully specified via +options to create a transient device, ie. a running device with no +persistence. + +# Usage + +List running mdev devices: + +``` +# mdevctl list +85006552-1b4b-45ef-ad62-de05be9171df 0000:00:02.0 i915-GVTg_V4_4 +83c32df7-d52e-4ec1-9668-1f3c7e4df107 0000:00:02.0 i915-GVTg_V4_8 (defined) +``` + +List defined mdev devices: + +``` +# mdevctl list -d +83c32df7-d52e-4ec1-9668-1f3c7e4df107 0000:00:02.0 i915-GVTg_V4_8 auto +b0a3989f-8138-4d49-b63a-59db28ec8b48 0000:00:02.0 i915-GVTg_V4_8 auto +5cf14a12-a437-4c82-a13f-70e945782d7b 0000:00:02.0 i915-GVTg_V4_4 manual +``` + +List mdev types supported on the host system: + +``` +# mdevctl types +0000:00:02.0 + i915-GVTg_V4_2 + Available instances: 1 + Device API: vfio-pci + Description: low_gm_size: 256MB high_gm_size: 1024MB fence: 4 resolution: 1920x1200 weight: 8 + i915-GVTg_V4_1 + Available instances: 0 + Device API: vfio-pci + Description: low_gm_size: 512MB high_gm_size: 2048MB fence: 4 resolution: 1920x1200 weight: 16 + i915-GVTg_V4_8 + Available instances: 4 + Device API: vfio-pci + Description: low_gm_size: 64MB high_gm_size: 384MB fence: 4 resolution: 1024x768 weight: 2 + i915-GVTg_V4_4 + Available instances: 3 + Device API: vfio-pci + Description: low_gm_size: 128MB high_gm_size: 512MB fence: 4 resolution: 1920x1200 weight: 4 +``` + +Modify a defined device from automatic start to manual: + +``` +# mdevctl modify --uuid 83c32df7-d52e-4ec1-9668-1f3c7e4df107 --manual +# mdevctl list -d +83c32df7-d52e-4ec1-9668-1f3c7e4df107 0000:00:02.0 i915-GVTg_V4_8 manual +b0a3989f-8138-4d49-b63a-59db28ec8b48 0000:00:02.0 i915-GVTg_V4_8 auto +5cf14a12-a437-4c82-a13f-70e945782d7b 0000:00:02.0 i915-GVTg_V4_4 manual +``` + +Stop a running mdev device: + +``` +# mdevctl stop -u 83c32df7-d52e-4ec1-9668-1f3c7e4df107 +``` + +Start an mdev device that is not defined + +``` +# uuidgen +6eba5b41-176e-40db-b93e-7f18e04e0b93 +# mdevctl start -u 6eba5b41-176e-40db-b93e-7f18e04e0b93 -p 0000:00:02.0 --type i915-GVTg_V4_1 +# mdevctl list +85006552-1b4b-45ef-ad62-de05be9171df 0000:00:02.0 i915-GVTg_V4_4 +6eba5b41-176e-40db-b93e-7f18e04e0b93 0000:00:02.0 i915-GVTg_V4_1 +``` + +Promote the new created mdev to a defined device: + +``` +# mdevctl define --uuid 6eba5b41-176e-40db-b93e-7f18e04e0b93 +# mdevctl list -d +83c32df7-d52e-4ec1-9668-1f3c7e4df107 0000:00:02.0 i915-GVTg_V4_8 manual +6eba5b41-176e-40db-b93e-7f18e04e0b93 0000:00:02.0 i915-GVTg_V4_1 manual +b0a3989f-8138-4d49-b63a-59db28ec8b48 0000:00:02.0 i915-GVTg_V4_8 auto +5cf14a12-a437-4c82-a13f-70e945782d7b 0000:00:02.0 i915-GVTg_V4_4 manual +``` + +## Advanced usage (attributes and JSON) + +mdevctl provides support for specifying additional configuration via +device-specific attributes. It also provides support for inspecting +and modifying its internal JSON representation of the configuration +directly. + +Example: + +``` +# mdevctl list -d +783e6dbb-ea0e-411f-94e2-717eaad438bf matrix vfio_ap-passthrough manual +``` + +Add some attributes: + +``` +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_adapter --value=5 +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_adapter --value=6 +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_domain --value=0xab +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_control_domain --value=0xab +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_domain --value=4 +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_control_domain --value=4 +# mdevctl list -dv +783e6dbb-ea0e-411f-94e2-717eaad438bf matrix vfio_ap-passthrough manual + Attrs: + @{0}: {"assign_adapter":"5"} + @{1}: {"assign_adapter":"6"} + @{2}: {"assign_domain":"0xab"} + @{3}: {"assign_control_domain":"0xab"} + @{4}: {"assign_domain":"4"} + @{5}: {"assign_control_domain":"4"} +``` + +Dump the JSON configuration: + +``` +# mdevctl list -d -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --dumpjson +{ + "mdev_type": "vfio_ap-passthrough", + "start": "manual", + "attrs": [ + { + "assign_adapter": "5" + }, + { + "assign_adapter": "6" + }, + { + "assign_domain": "0xab" + }, + { + "assign_control_domain": "0xab" + }, + { + "assign_domain": "4" + }, + { + "assign_control_domain": "4" + } + ] +} +``` + +Remove some attributes: + +``` +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --delattr --index=5 +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --delattr --index=4 +# mdevctl list -dv +783e6dbb-ea0e-411f-94e2-717eaad438bf matrix vfio_ap-passthrough manual + Attrs: + @{0}: {"assign_adapter":"5"} + @{1}: {"assign_adapter":"6"} + @{2}: {"assign_domain":"0xab"} + @{3}: {"assign_control_domain":"0xab"} +``` + +Define an mdev device from a file: + +``` +# cat vfio_ap_device.json +{ + "mdev_type": "vfio_ap-passthrough", + "start": "manual", + "attrs": [ + { + "assign_adapter": "5" + }, + { + "assign_domain": "0x47" + }, + { + "assign_domain": "0xff" + } + ] +} +# mdevctl define -p matrix --jsonfile vfio_ap_device.json +e2e73122-cc39-40ee-89eb-b0a47d334cae +# mdevctl list -dv +783e6dbb-ea0e-411f-94e2-717eaad438bf matrix vfio_ap-passthrough manual + Attrs: + @{0}: {"assign_adapter":"5"} + @{1}: {"assign_adapter":"6"} + @{2}: {"assign_domain":"0xab"} + @{3}: {"assign_control_domain":"0xab"} +e2e73122-cc39-40ee-89eb-b0a47d334cae matrix vfio_ap-passthrough manual + Attrs: + @{0}: {"assign_adapter":"5"} + @{1}: {"assign_domain":"0x47"} + @{2}: {"assign_domain":"0xff"} +``` + +See `mdevctl --help` or the manpage for more information. diff --git a/mdevctl b/mdevctl new file mode 100755 index 0000000..317600e --- /dev/null +++ b/mdevctl @@ -0,0 +1,1008 @@ +#!/usr/bin/bash + +persist_base=/etc/mdevctl.d +mdev_base=/sys/bus/mdev/devices +parent_base=/sys/class/mdev_bus + +# Alias 'lsmdev' to 'mdevctl list' +if [ $(basename $0) == "lsmdev" ]; then + set -- "list" "${@}" +fi + +# See https://stackoverflow.com/a/29754866/4775714 for getopt usage +getopt --test > /dev/null +if [ $? -ne 4 ]; then + echo "Sorry, $0 requires enhanced getopt support" + exit 1 +fi + +config={} +attrs=[] + +jsonify() { + echo "\"$1\"" +} + +validate_attr() { + # first arg: expected root, second arg: attr to check + if [ ! -w "$1/$2" ]; then + echo "Attribute $2 cannot be set" >&2 + return 1 + fi + if [ $(realpath --relative-base "$1" "$1/$2") == "$2" ]; then + return 0 + else + echo "Invalid attribute $2" >&2 + return 1 + fi +} + +del_config_key() { + key="$1" + + config=$(echo "$config" | jq -c -M --arg key "$key" 'del(."$key")') +} + +set_config_key() { + key=$(jsonify "$1") + value=$(jsonify "$2") + + del_config_key "$key" + + config=$(echo "$config" | jq -c -M --argjson obj "{$key:$value}" '. + $obj') +} + +has_config_key() { + key="$1" + + if [ $(echo "$config" | jq -M --arg key "$key" 'has($key)') == "true" ]; then + return 0 + else + return 1 + fi +} + +get_config_key() { + key="$1" + + echo "$config" | jq -r -M --arg key "$key" '.[$key]' +} + +config_file() { + uuid="$1" + parent="$2" + + if [ -n "$parent" ]; then + if [ ! -e "$persist_base/$parent/$uuid" ]; then + echo "Config for $uuid on $parent does not exist, define it first?" >&2 + return 1 + fi + echo "$persist_base/$parent/$uuid" + else + count=$(find "$persist_base" -name "$uuid" -type f | wc -l) + if [ "$count" -eq 0 ]; then + echo "Config for $uuid does not exist, define it first?" >&2 + return 1 + elif [ "$count" -gt 1 ]; then + echo "Multiple configs found for $uuid, specify a parent" >&2 + return 1 + fi + echo $(find "$persist_base" -name "$uuid" -type f) + fi + + return 0 +} + +get_attr_length() { + echo "$attrs" | jq -M '. | length' +} + +get_attrs_raw() { + echo "$attrs" +} + +add_attr_index() { + key=$(jsonify "$1") + value=$(jsonify "$2") + + if [ -z "$3" ]; then + index=$(get_attr_length) + else + index="$3" + fi + + attrs=$(echo "$attrs" | jq -c -M --argjson obj "{$key:$value}" \ + --argjson i $index '.[0:$i] + [$obj] + .[$i:]') +} + +del_attr_index() { + if [ -z "$1" ]; then + index=$(( $(get_attr_length) - 1 )) + else + index="$1" + fi + + attrs=$(echo "$attrs" | jq -c -M --argjson i "$index" '.[0:$i] + .[$i+1:]') +} + +get_attr_index_key() { + if [ -z "$1" ]; then + index=0 + else + index="$1" + fi + + echo "$attrs" | jq -r -M --argjson i "$index" '.[$i] | keys | .[]' +} + +get_attr_index_value() { + if [ -z "$1" ]; then + index=0 + else + index="$1" + fi + + echo "$attrs" | jq -r -M --argjson i "$index" '.[$i] | .[]' +} + +get_attr_index_raw() { + if [ -z "$1" ]; then + index=0 + else + index="$1" + fi + + echo "$attrs" | jq -c -M --argjson i "$index" '.[$i]' +} + +read_config() { + file="$1" + + config=$(jq -c -M '.' "$file") + if [ $? -eq 0 ] && has_config_key mdev_type && has_config_key start; then + attrs=$(echo "$config" | jq -c -M '.attrs') + if [ "$attrs" == null ]; then + attrs=[] + fi + config=$(echo "$config" | jq -c -M 'del(.attrs)') + return 0 + else + config={} + attrs=[] + return 1 + fi +} + +dump_config() { + echo "$config" | jq -M --argjson attrs "{\"attrs\":$attrs}" '. + $attrs' +} + +write_config() { + file="$1" + + dump_config > "$file" +} + +valid_uuid () { + uuid="$1" + + if [[ "$uuid" =~ ^\{?[A-F0-9a-f]{8}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{12}\}?$ ]]; then + echo "$uuid" + fi +} + +create_mdev() { + uuid="$1" + parent="$2" + type="$3" + + if [ -z "$(valid_uuid $uuid)" ]; then + echo "Invalid UUID $uuid" >&2 + return 1 + fi + + if [ -L "$mdev_base/$uuid" ]; then + cur_parent=$(basename $(realpath "$mdev_base/$uuid" | sed -s "s/\/$uuid//")) + if [ $? -ne 0 ] || [ "$cur_parent" != "$parent" ]; then + echo "Device exists under different parent" >&2 + return 1 + fi + + cur_type=$(basename $(realpath "$mdev_base/$uuid/mdev_type")) + if [ $? -ne 0 ] || [ "$cur_type" != "$type" ]; then + echo "Device exists with different type" >&2 + return 1 + fi + + return 1 + fi + + if [ ! -d "$parent_base/$parent/mdev_supported_types" ]; then + echo "Parent $parent is not currently registered for mdev support" >&2 + return 1 + fi + + if [ ! -d "$parent_base/$parent/mdev_supported_types/$type" ]; then + echo "Parent $parent does not support mdev type $type" >&2 + return 1 + fi + + avail=$(cat "$parent_base/$parent/mdev_supported_types/$type/available_instances") + if [ $? -ne 0 ] || [ "$avail" -eq 0 ]; then + echo "No available instances of $type on $parent" >&2 + return 1 + fi + + echo "$uuid" > "$parent_base/$parent/mdev_supported_types/$type/create" + if [ $? -ne 0 ]; then + echo "Error creating mdev type $type on $parent" >&2 + return 1 + fi + + return 0 +} + +start_mdev() { + uuid="$1" + parent="$2" + type="$3" + if [ $# -eq 4 ]; then + print_uuid="$4" + fi + + create_mdev "$uuid" "$parent" "$type" + if [ $? -eq 0 ]; then + count=$(( $(get_attr_length) - 1 )) + if [ "$count" -ge 0 ]; then + for i in $(seq 0 "$count"); do + attr=$(get_attr_index_key $i) + validate_attr "$mdev_base/$uuid" "$attr" + if [ $? -ne 0 ]; then + remove_mdev "$uuid" + return 1 + fi + val=$(get_attr_index_value $i) + echo -e "$val" > "$mdev_base/$uuid/$attr" + if [ $? -ne 0 ]; then + echo "Failed to write $val to attribute $attr" >&2 + remove_mdev "$uuid" + return 1 + fi + done + fi + $print_uuid + return 0 + fi + return 1 +} + +remove_mdev() { + uuid="$1" + + if [ -z "$(valid_uuid $uuid)" ]; then + echo "Invalid UUID $uuid" >&2 + return 1 + fi + + if [ ! -L "$mdev_base/$uuid" ]; then + return 1 + fi + + echo 1 > "$mdev_base/$uuid/remove" + if [ $? -ne 0 ]; then + echo "Error removing device $uuid" >&2 + return 1 + fi + + return 0 +} + +# Get a UUID that's not locally defined or running +unique_uuid() { + count=1 + while [ $count -ne 0 ]; do + uuid=$(uuidgen) + count=$(find "$persist_base" -name "$uuid" -type f | wc -l) + if [ "$count" -eq 0 ] && [ -L "$mdev_base/$uuid" ]; then + count=1 + fi + done + + echo "$uuid" +} + +usage() { + cat >&2 < [-a|--auto] + [-u|--uuid=UUID] <-p|--parent=PARENT> <-t|--type=TYPE> [-a|--auto] + [-u|--uuid=UUID] <-p|--parent=PARENT> <--jsonfile=FILE> + If the device specified by the UUID currently exists, parent + and type may be omitted to use the existing values. The auto + option marks the device to start on parent availability. + If defined via FILE then type, startup, and any attributes + are provided via the file. Running devices are unaffected + by this command. +undefine Undefine, or remove a config for an mdev device. Options: + <-u|--uuid=UUID> [-p|--parent=PARENT] + If a UUID exists for multiple parents, all will be removed + unless a parent is specified. Running devices are unaffected + by this command. +modify Modify the config for a defined mdev device. Options: + <-u|--uuid=UUID> [-p|--parent=PARENT] [-t|--type=TYPE] \\ + [--addattr=ATTRIBUTE] [--delattr] [-i|--index=INDEX] [--value=VALUE] \\ + [-a|--auto|-m|--manual] + The parent option further identifies a UUID if it is not + unique, the parent for a device cannot be modified via this + command, undefine and re-define should be used instead. An + ATTRIBUTE can be added or removed, which correlates to a + sysfs attribute under the created device. Unless an INDEX + value is provided, operations are performed at the end of + the attribute list. VALUE is to be specified in the format + that is accepted by the attribute. Upon device start, mdevctl + will go through each attribute in order, writing the value into + the corresponding sysfs attribute for the device. The startup + mode of the device can also be selected, auto or manual. + Running devices are unaffected by this command. +start Start an mdev device. Options: + <-u|--uuid=UUID> [-p|--parent=PARENT] + [-u|--uuid=UUID] <-p|--parent=PARENT> <-t|--type=TYPE> + [-u|--uuid=UUID] <-p|--parent=PARENT> <--jsonfile=FILE> + If the UUID is previously defined and unique, the UUID is + sufficient to start the device (UUIDs may not collide between + running devices). If a UUID is used in multiple defined + configs, the parent device is necessary to identify the config. + When specified with PARENT and TYPE, the device is fully + specified and will be started based only on these parameters. + The UUID is optional in this case, if not provided a UUID is + generated and returned as output. A FILE may replace the TYPE + specification and also include additional attributes to be + applied to the started device. +stop Stop an mdev device. Options: + <-u|--uuid=UUID> +list List mdev devices. Options: + [-d|--defined] [-u|--uuid=UUID] [-p|--parent=PARENT] \\ + [--dumpjson] [-v|--verbose] + With no options, information about the currently running mdev + devices is provided. Specifying DEFINED lists the + configuration of defined devices, regardless of their running + state. This may be further reduced by specifying specific + UUID or PARENT devices to list. The dumpjson option provides + output listing in machine readable JSON format. When a UUID + option is provided and the result is a single device, the + output contains only the JSON fields necessary to recreate a + config file for the device (minus attributes for listings of + running devices). When the verbose option is provided, the + human readable listing will include attributes for the + device(s). +types List mdev types. Options: + [-p|--parent=PARENT] [--dumpjson] + Specifying a PARENT lists only the types provided by the given + parent device. The dumpjson option provides output in machine + readable JSON format. +EOF + exit 1 +} + +if [ $# -lt 1 ]; then + usage +fi + +case ${1} in + # + # Internal commands, these are expected to be called from other scripts + # and therefore do not offer the convenience of getopt processing + # (they typically take one arg after the command) and are not listed in + # the usage text + # + start-parent-mdevs) + if [ $# -ne 2 ]; then + echo "Usage: $0 $1 " >&2 + exit 1 + fi + + parent="$2" + if [ ! -d "$persist_base/$parent" ]; then + # Nothing to do + exit 0 + fi + + for file in $(find "$persist_base/$parent/" -maxdepth 1 -mindepth 1 -type f); do + uuid=$(basename "$file") + if [ -n "$(valid_uuid $uuid)" ]; then + read_config "$file" + if [ $? -ne 0 ]; then + continue + fi + + if [ "$(get_config_key start)" == "auto" ]; then + create_mdev "$uuid" "$parent" "$(get_config_key mdev_type)" + if [ $? -ne 0 ]; then + echo "Failed to create mdev $uuid, type $(get_config_key mdev_type) on $parent" >&2 + # continue... + fi + fi + fi + done + exit 0 + ;; + # + # User commands + # + --help|-h|-?) + usage + ;; + define) + cmd="$1" + OPTIONS="u:p:t:a" + LONGOPTS="uuid:,parent:,type:,auto,jsonfile:" + shift + ;; + undefine) + cmd="$1" + OPTIONS="u:p:" + LONGOPTS="uuid:,parent:" + shift + ;; + modify) + cmd="$1" + OPTIONS="u:p:t:ami:" + LONGOPTS="uuid:,parent:,type:,auto,manual,addattr:,delattr,index:,value:" + shift + ;; + start) + cmd="$1" + OPTIONS="u:p:t:" + LONGOPTS="uuid:,parent:,type:,jsonfile:" + shift + ;; + stop) + cmd="$1" + OPTIONS="u:" + LONGOPTS="uuid:" + shift + ;; + list) + cmd="$1" + OPTIONS="du:p:v" + LONGOPTS="defined,uuid:,dumpjson,parent:,verbose" + shift + ;; + types) + cmd="$1" + OPTIONS="p:" + LONGOPTS="parent:,dumpjson" + shift + ;; + *) + echo "Unknown command $1" >&2 + usage + ;; +esac + +PARSED=$(getopt --options="$OPTIONS" --longoptions="$LONGOPTS" --name "$(basename $0)" -- "$@") +if [ $? -ne 0 ]; then + exit 1 +fi + +eval set -- "$PARSED" + +while true; do + case "$1" in + -u|--uuid) + uuid="$2" + shift 2 + ;; + -p|--parent) + parent="$2" + shift 2 + ;; + -t|--type) + type="$2" + shift 2 + ;; + --jsonfile) + jsonfile="$2" + shift 2 + ;; + --addattr) + addattr="$2" + shift 2 + ;; + -i|--index) + index="$2" + shift 2 + ;; + --value) + value="$2" + shift 2 + ;; + --delattr) + delattr=y + shift 1 + ;; + --dumpjson) + dumpjson=y + shift + ;; + -a|--auto) + auto=y + shift 1 + ;; + -m|--manual) + manual=y + shift 1 + ;; + -d|--defined) + defined=y + shift 1 + ;; + -a|--available) + available=y + shift 1 + ;; + -v|--verbose) + verbose=y + shift 1 + ;; + --) + shift + break + ;; + *) + echo "Programming error" + exit 1 + ;; + esac +done + +if [ $# -ne 0 ]; then + echo "$(basename $0): Unknown options: $@" + exit 1 +fi + +case "$cmd" in + define) + if [ -n "$jsonfile" ]; then + if [ ! -r "$jsonfile" ]; then + echo "Unable to read file $jsonfile" >&2 + exit 1 + fi + + if [ -n "$type" ]; then + echo "Device type cannot be specified separately from $jsonfile" >&2 + exit 1 + fi + + if [ -z "$parent" ]; then + echo "Parent device required to define device via $jsonfile" >&2 + exit 1 + fi + + if [ -z "$uuid" ]; then + uuid=$(unique_uuid) + print_uuid="echo $uuid" + fi + + if [ -e "$persist_base/$parent/$uuid" ]; then + echo "Cowardly refusing to overwrite existing config for $parent/$uuid" >&2 + exit 1 + fi + + set -o errexit + + read_config "$jsonfile" + if [ $? -ne 0 ]; then + echo "Error reading $jsonfile" >&2 + exit 1 + fi + + write_config "$persist_base/$parent/$uuid" + if [ $? -ne 0 ]; then + exit 1 + fi + + $print_uuid + exit 0 + fi + + if [ -n "$uuid" ]; then + if [ -z "$parent" ]; then + if [ ! -L "$mdev_base/$uuid" ] || [ -n "$type" ]; then + usage + fi + + parent=$(basename $(realpath "$mdev_base/$uuid" | sed -s "s/\/$uuid//")) + type=$(basename $(realpath "$mdev_base/$uuid/mdev_type")) + fi + + if [ -e "$persist_base/$parent/$uuid" ]; then + echo "Device $uuid on $parent already defined, try modify?" >&2 + exit 1 + fi + else + uuid=$(unique_uuid) + print_uuid="echo $uuid" + fi + + if [ -z "$parent" ]; then + usage + fi + + if [ -z "$type" ]; then + usage + fi + + if [ -n "$auto" ]; then + start="auto" + else + start="manual" + fi + + set -o errexit + + mkdir -p "$persist_base/$parent" + set_config_key mdev_type "$type" + set_config_key start "$start" + write_config "$persist_base/$parent/$uuid" + if [ $? -eq 0 ]; then + $print_uuid + fi + ;; + undefine) + if [ -z "$uuid" ]; then + usage + fi + + set -o errexit + + if [ -n "$parent" ]; then + rm -f "$persist_base/$parent/$uuid" + else + find "$persist_base" -name "$uuid" -type f | xargs rm -f + fi + ;; + modify) + if [ -z "$uuid" ]; then + usage + fi + + if [ -n "$auto" ] && [ -n "$manual" ]; then + echo "Options --auto and --manual are mutually exclusive" >&2 + exit 1 + fi + + set -o errexit + + file=$(config_file "$uuid" "$parent") + if [ $? -ne 0 ]; then + exit 1 + fi + + read_config "$file" + if [ $? -ne 0 ]; then + echo "Config file $file invalid" >&2 + exit 1 + fi + + if [ -n "$type" ]; then + set_config_key mdev_type "$type" + fi + + if [ -n "$auto" ]; then + set_config_key start auto + fi + + if [ -n "$manual" ]; then + set_config_key start manual + fi + + if [ -n "$addattr" ] && [ -n "$delattr" ]; then + usage + fi + + if [ -n "$addattr" ]; then + if [ -n "$index" ]; then + if [ "$index" -eq "$index" ] 2>/dev/null; then + : + else + echo "Provided index is not a number" >&2 + usage + fi + fi + + if [ -z "$value" ]; then + echo "No attribute value provided" >&2 + usage + fi + + add_attr_index "$addattr" "$value" "$index" + fi + + if [ -n "$delattr" ]; then + if [ -n "$index" ]; then + if [ "$index" -eq "$index" ] 2>/dev/null; then + : + else + echo "Provided index is not a number" >&2 + usage + fi + fi + + del_attr_index "$index" + fi + + write_config "$file" + ;; + start) + set -o errexit + + if [ -n "$jsonfile" ]; then + if [ ! -r "$jsonfile" ]; then + echo "Unable to read file $jsonfile" >&2 + exit 1 + fi + + if [ -n "$type" ]; then + echo "Device type cannot be specified separately from $jsonfile" >&2 + exit 1 + fi + + if [ -z "$parent" ]; then + echo "Parent device required to start device via $jsonfile" >&2 + exit 1 + fi + + if [ -z "$uuid" ]; then + uuid=$(unique_uuid) + print_uuid="echo $uuid" + fi + + read_config "$jsonfile" + if [ $? -ne 0 ]; then + echo "Error reading $jsonfile" >&2 + exit 1 + fi + + type="$(get_config_key mdev_type)" + + start_mdev "$uuid" "$parent" "$type" "$print_uuid" + exit $? + fi + + # We don't implement a placement policy + if [ -n "$type" ] && [ -z "$parent" ]; then + usage + fi + + # The device is not fully specified without TYPE, we must find + # a config file, with optional PARENT for disambiguation + if [ -z "$type" ] && [ -n "$uuid" ]; then + count=$(find "$persist_base" -name "$uuid" -type f | wc -l) + if [ "$count" -eq 0 ]; then + echo "Config for $uuid does not exist, define it first?" >&2 + exit 1 + elif [ "$count" -gt 1 ]; then + if [ -z "$parent" ] || [ ! -e "$persist_base/$parent/$uuid" ]; then + echo "Multiple configs found for $uuid, specify a parent" >&2 + exit 1 + fi + file="$persist_base/$parent/$uuid" + else + file=$(find "$persist_base" -name "$uuid" -type f) + if [ -n "$parent" ]; then + cur_parent=$(basename $(echo "$file" | sed -s "s/\/$uuid//")) + if [ "$cur_parent" != "$parent" ]; then + echo "Config for $parent/$uuid does not exist, define it first?" >&2 + exit 1 + fi + fi + fi + + read_config "$file" + if [ $? -ne 0 ]; then + echo "Config file $file invalid" >&2 + exit 1 + fi + + if [ -z "$parent" ]; then + parent=$(basename $(echo "$file" | sed -s "s/\/$uuid//")) + fi + + type="$(get_config_key mdev_type)" + fi + + if [ -z "$uuid" ]; then + # Device must be full specified otherwise for generated UUID + if [ -z "$parent" ] || [ -z "$type" ]; then + echo "Device is insufficiently specified" >&2 + usage + fi + uuid=$(unique_uuid) + print_uuid="echo $uuid" + fi + + start_mdev "$uuid" "$parent" "$type" "$print_uuid" + exit $? + ;; + stop) + if [ -z "$uuid" ]; then + usage + fi + + set -o errexit + + remove_mdev "$uuid" + ;; + list) + json="[]" + txt="" + + if [ -n "$defined" ]; then + for dir in $(find "$persist_base/" -maxdepth 1 -mindepth 1 -type d | sort); do + p=$(basename "$dir") + if [ -n "$parent" ] && [ "$parent" != "$p" ]; then + continue + fi + + for mdev in $(find "$dir/" -maxdepth 1 -mindepth 1 -type f); do + u=$(basename "$mdev") + if [ -n "$uuid" ] && [ "$uuid" != "$u" ]; then + continue + fi + + read_config "$mdev" + if [ $? -ne 0 ]; then + continue + fi + + type="$(get_config_key mdev_type)" + start="$(get_config_key start)" + + txt+="$u $p $type $start" + + if [ -L "$mdev_base/$u" ]; then + cur_parent=$(basename $(realpath "$mdev_base/$u" | sed -s "s/\/$u//")) + if [ "$cur_parent" == "$p" ]; then + cur_type=$(basename $(realpath "$mdev_base/$u/mdev_type")) + if [ "$cur_type" == "$type" ]; then + txt+=" (active)" + fi + fi + fi + + json_tmp="{\"$p\":[{\"$u\":{"\"mdev_type\":\"$type\"",\"start\":\"$start\"" + txt+="\n" + + if [ -n "$verbose" ] || [ -n "$dumpjson" ]; then + count=$(( $(get_attr_length) - 1 )) + if [ $count -ge 0 ]; then + json_tmp+=",\"attrs\":$(get_attrs_raw)" + txt+=" Attrs:\n" + for i in $(seq 0 "$count"); do + txt+=" @{$i}: $(get_attr_index_raw $i)\n" + done + fi + fi + json_tmp+="}}]}" + json=$(echo "$json" | jq -c -M --argjson obj "$json_tmp" '. + [$obj]') + done + done + else + if [ ! -d "$mdev_base" ]; then + exit 0 + fi + + for mdev in $(find "$mdev_base/" -maxdepth 1 -mindepth 1 -type l); do + u=$(basename "$mdev") + if [ -n "$uuid" ] && [ "$uuid" != "$u" ]; then + continue + fi + + p=$(basename $(realpath "$mdev_base/$u" | sed -s "s/\/$u//")) + if [ -n "$parent" ] && [ "$parent" != "$p" ]; then + continue + fi + + type=$(basename $(realpath "$mdev/mdev_type")) + + json_tmp="{\"$p\":[{\"$u\":{\"mdev_type\":\"$type\"}}]}" + txt+="$u $p $type" + + if [ -f "$persist_base/$p/$u" ]; then + read_config "$persist_base/$p/$u" + if [ $? -eq 0 ] && [ "$(get_config_key mdev_type)" == "$type" ]; then + txt+=" (defined)" + fi + fi + + txt+="\n" + json=$(echo "$json" | jq -c -M --argjson obj "$json_tmp" '. + [$obj]') + + done + fi + + if [ -n "$dumpjson" ]; then + if [ $(echo "$json" | jq 'length') -gt 0 ]; then + # https://stackoverflow.com/a/43337323/4775714 + json=$(echo "$json" | jq -c -M '[reduce .[] as $o ({}; reduce ($o|keys)[] as $key (.; .[$key] += $o[$key] ))]') + fi + + # If specified to a single device, output such that it can be + # piped into a config file, else print entire hierarchy + if [ -n "$uuid" ] && [ $(echo "$json" | jq -M 'length') -eq 1 ] && + [ $(echo "$json" | jq -M '.[] | length') -eq 1 ] && + [ $(echo "$json" | jq -M '.[] | .[] | length') -eq 1 ]; then + key=$(echo "$json" | jq -r -M '.[] | .[] | .[] | keys | .[]') + key=$(jsonify $key) + echo "$json" | jq -M --argjson key "$key" '.[] | .[] | .[] | .[$key]' + else + echo "$json" | jq -M '.' + fi + else + echo -en "$txt" + fi + ;; + types) + if [ ! -d "$parent_base" ]; then + if [ -n "$dumpjson" ]; then + echo "[]" | jq -M '.' + fi + exit 0 + fi + + json="[]" + txt="" + + for dir in $(find "$parent_base/" -maxdepth 1 -mindepth 1 -type l | sort); do + p=$(basename "$dir") + if [ -n "$parent" ] && [ "$parent" != "$p" ]; then + continue + fi + + txt+="$p\n" + + for parent_type in $(find "$dir/mdev_supported_types/" -maxdepth 1 -mindepth 1 -type d | sort); do + type=$(basename "$parent_type") + txt+=" $type\n" + + avail=$(cat "$parent_type/available_instances") + txt+=" Available instances: $avail\n" + + api=$(cat "$parent_type/device_api") + txt+=" Device API: $api\n" + + json_tmp="{\"$p\":[{\"$type\":{\"available_instances\":$avail,\"device_api\":\"$api\"" + + if [ -e "$parent_type/name" ]; then + name=$(cat "$parent_type/name") + json_tmp+=",\"name\":\"$name\"" + txt+=" Name: $name\n" + fi + + if [ -e "$parent_type/description" ]; then + descr=$(cat "$parent_type/description" | sed -e ':a;N;$!ba;s/\n/, /g') + json_tmp+=",\"description\":\"$descr\"" + txt+=" Description: $descr\n" + fi + + json_tmp+="}}]}" + json=$(echo "$json" | jq -c -M --argjson obj "$json_tmp" '. + [$obj]') + done + done + + if [ -n "$dumpjson" ]; then + if [ $(echo "$json" | jq 'length') -gt 0 ]; then + # https://stackoverflow.com/a/43337323/4775714 + json=$(echo "$json" | jq -c -M '[reduce .[] as $o ({}; reduce ($o|keys)[] as $key (.; .[$key] += $o[$key] ))]') + fi + + echo "$json" | jq -M '.' + else + echo -en "$txt" + fi + ;; +esac diff --git a/mdevctl.8 b/mdevctl.8 new file mode 100644 index 0000000..07be5dd --- /dev/null +++ b/mdevctl.8 @@ -0,0 +1,407 @@ +.\" mdevctl - Mediated device management utility +.TH mdevctl 8 +.SH NAME +mdevctl, lsmdev \- Mediated device management utility +.SH SYNOPSIS +\fBmdevctl\fR {COMMAND} [OPTIONS...]\fR + +.SH DESCRIPTION + +\fBmdevctl\fR is a utility for managing and persisting devices in the +mediated device device framework of the Linux kernel. Mediated +devices are sub-devices of a parent device (ex. a vGPU) which +can be dynamically created and potentially used by drivers like +vfio-mdev for assignment to virtual machines. + +.SH OPTIONS + +.PP +The following options are understood: + +.PP +\fB--addattr=ATTRIBUTE\fR +.RS 4 +Add an attribute \fIATTRIBUTE\fR. Valid for the \fBmodify\fR +command. +.RE + +.PP +\fB-a|--auto\fR +.RS 4 +Automatically start the device on parent availability. Valid for +\fBdefine\fR and \fBmodify\fR commands. +.RE + +.PP +\fB-d|--defined\fR +.RS 4 +List all defined devices, even if not active. Valid for the \fBlist\fR +command. +.RE + +.PP +\fB--delattr\fR +.RS 4 +Delete an attribute entry. Valid for the \fBmodify\fR command. +.RE + +.PP +\fB--dumpjson\fR +.RS 4 +Dump the configuration for a device in JSON format when filtered to +as single device and used with the \fBlist\fR command. When used +with the \fBtypes\fR command, output machine readable type information. +.RE + +.PP +\fB-i|--index=INDEX\fR +.RS 4 +Act on the attribute \fIINDEX\fR. Valid for the \fBmodify\fR command. +.RE + +.PP +\fB--jsonfile=FILE\fR +.RS 4 +Read the configuration for a device from a JSON file \fIFILE\fR. +Valid for the \fBdefine\fR and \fBstart\fR commands. +.RE + +.PP +\fB-m|--manual\fR +.RS 4 +Do not start a device automatically on parent availability. Valid +for the \fBmodify\fR command. +.RE + +.PP +\fB-p|--parent=PARENT\fR +.RS 4 +Specify or identify the device by its parent device. +.RE + +.PP +\fB-t|--type=TYPE\fR +.RS 4 +Specify or identify the device by its type. +.RE + +.PP +\fB-u|--uuid=UUID\fR +.RS 4 +Specify or identify the device by its UUID. +.RE + +.PP +\fB--value=VALUE\fR +.RS 4 +Set an attribute to \fIVALUE\fR, in the format accepted by the attribute. +Valid for the \fBmodify\fR command. +.RE + +.PP +\fB-v|--verbose\fR +.RS 4 +Increase output verbosity, currently only adds attribute output to the +\fBtypes\fR command. +.RE + +.SH COMMANDS + +.PP +The following commands are understood: + +.PP +\fBdefine\fR \fIDEVICESPEC\fR +.RS 4 +Define a config for an mdev device, identified either by an UUID (if +the device already exists), or by the parent device and either the type +or a JSON configuration file, and, optionally, the UUID. If no UUID is +specified, one is autogenerated and printed. If no file is used, +\fI-a|--auto\fR may be used to specify that the device should be started +automatically. +.RE + +.PP +\fBlist\fR +.RS 4 +List mdev devices. With no options, currently running devices are listed. +With \fB-d|--defined\fR, previously defined devices are listed. +Can be restricted to list only devices for a given parent or UUID. With +\fB--dumpjson\fR output is provided in machine readable JSON format. +When a UUID is provided and the output results in a single device, the +JSON output format is compatible with the configuration file format. +.RE + +.PP +\fBmodify\fR \fIDEVICESPEC\fR +.RS 4 +Modify the configuration for an mdev device, identified via its UUID +and optionally its parent. +Type and startup mode (auto or manual) can be modified by this command. +Attributes can be added or deleted. Attributes to be deleted must be +specified by their index; if an attribute is specified without an +index, it is appended at the end of the attribute list. +Running devices are unaffected by this command; changes in the configuration +are applied the next time the device is started. +.RE + +.PP +\fBstart\fR \fIDEVICESPEC\fR +.RS 4 +Start an mdev device, identified by its UUID and optionally its parent, +or its parent and type and optionally its UUID, which is generated if +not given. +If specified via its parent and optionally its UUID, the type may be +specified in a JSON configuration file, alongside additional parameters. +.RE + +.PP +\fBstop\fR \fIDEVICESPEC\fR +.RS 4 +Stop an mdev device, specified via its UUID. +.RE + +.PP +\fBtypes\fR +.RS 4 +List the mdev device types known to the system by parent device. Output +may be limited to a single parent device with the \fB-p|--parent\fR option. +JSON output format is used with the \fB--dumpjson\fR option. +.RE + +.PP +\fBundefine\fR \fIDEVICESPEC\fR +.RS 4 +Undefine, or remove the configuration for an mdev device, specified by +its UUID and optionally its parent. If a UUID exists for multiple +parents, all of them will be removed unless restricted to a single parent. +Running devices are unaffected by this command. +.RE + +.SH "NOTE ON DEVICE SPECIFICATION" + +For a given UUID, only one device with that UUID may be running at the +same time. However, it is possible to define multiple devices with the +same UUID under different parent devices. Therefore, it is sometimes +necessary to specify the parent device alongside the UUID to uniquely +identify a device. + +.SH "EXIT STATUS" +On success, 0 is returned, a non-zero failure code otherwise. + +.SH EXAMPLES + +.nf +List running mdev devices: + +.EX +# mdevctl list +85006552-1b4b-45ef-ad62-de05be9171df 0000:00:02.0 i915-GVTg_V4_4 +83c32df7-d52e-4ec1-9668-1f3c7e4df107 0000:00:02.0 i915-GVTg_V4_8 (defined) +.EE + +List defined mdev devices: + +.EX +# mdevctl list -d +83c32df7-d52e-4ec1-9668-1f3c7e4df107 0000:00:02.0 i915-GVTg_V4_8 auto +b0a3989f-8138-4d49-b63a-59db28ec8b48 0000:00:02.0 i915-GVTg_V4_8 auto +5cf14a12-a437-4c82-a13f-70e945782d7b 0000:00:02.0 i915-GVTg_V4_4 manual +.EE + +List mdev types supported on the host system: + +.EX +# mdevctl types +0000:00:02.0 + i915-GVTg_V4_2 + Available instances: 1 + Device API: vfio-pci + Description: low_gm_size: 256MB high_gm_size: 1024MB fence: 4 resolution: 1920x1200 weight: 8 + i915-GVTg_V4_1 + Available instances: 0 + Device API: vfio-pci + Description: low_gm_size: 512MB high_gm_size: 2048MB fence: 4 resolution: 1920x1200 weight: 16 + i915-GVTg_V4_8 + Available instances: 4 + Device API: vfio-pci + Description: low_gm_size: 64MB high_gm_size: 384MB fence: 4 resolution: 1024x768 weight: 2 + i915-GVTg_V4_4 + Available instances: 3 + Device API: vfio-pci + Description: low_gm_size: 128MB high_gm_size: 512MB fence: 4 resolution: 1920x1200 weight: 4 +.EE + +Modify a defined device from automatic start to manual: + +.EX +# mdevctl modify --uuid 83c32df7-d52e-4ec1-9668-1f3c7e4df107 --manual +# mdevctl list -d +83c32df7-d52e-4ec1-9668-1f3c7e4df107 0000:00:02.0 i915-GVTg_V4_8 manual +b0a3989f-8138-4d49-b63a-59db28ec8b48 0000:00:02.0 i915-GVTg_V4_8 auto +5cf14a12-a437-4c82-a13f-70e945782d7b 0000:00:02.0 i915-GVTg_V4_4 manual +.EE + +Stop a running mdev device: + +.EX +# mdevctl stop -u 83c32df7-d52e-4ec1-9668-1f3c7e4df107 +.EE + +Start an mdev device that is not defined: + +.EX +# uuidgen +6eba5b41-176e-40db-b93e-7f18e04e0b93 +# mdevctl start -u 6eba5b41-176e-40db-b93e-7f18e04e0b93 -p 0000:00:02.0 --type i915-GVTg_V4_1 +# mdevctl list +85006552-1b4b-45ef-ad62-de05be9171df 0000:00:02.0 i915-GVTg_V4_4 +6eba5b41-176e-40db-b93e-7f18e04e0b93 0000:00:02.0 i915-GVTg_V4_1 +.EE + +Promote the new created mdev to a defined device: + +.EX +# mdevctl define --uuid 6eba5b41-176e-40db-b93e-7f18e04e0b93 +# mdevctl list -d +83c32df7-d52e-4ec1-9668-1f3c7e4df107 0000:00:02.0 i915-GVTg_V4_8 manual +6eba5b41-176e-40db-b93e-7f18e04e0b93 0000:00:02.0 i915-GVTg_V4_1 manual +b0a3989f-8138-4d49-b63a-59db28ec8b48 0000:00:02.0 i915-GVTg_V4_8 auto +5cf14a12-a437-4c82-a13f-70e945782d7b 0000:00:02.0 i915-GVTg_V4_4 manual +.EE + +.SS "ADVANCED EXAMPLES (ATTRIBUTES AND JSON)" + +.EX +# mdevctl list -d +783e6dbb-ea0e-411f-94e2-717eaad438bf matrix vfio_ap-passthrough manual +.EE + +Add some attributes: + +.EX +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_adapter --value=5 +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_adapter --value=6 +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_domain --value=0xab +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_control_domain --value=0xab +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_domain --value=4 +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --addattr=assign_control_domain --value=4 +# mdevctl list -dv +783e6dbb-ea0e-411f-94e2-717eaad438bf matrix vfio_ap-passthrough manual + Attrs: + @{0}: {"assign_adapter":"5"} + @{1}: {"assign_adapter":"6"} + @{2}: {"assign_domain":"0xab"} + @{3}: {"assign_control_domain":"0xab"} + @{4}: {"assign_domain":"4"} + @{5}: {"assign_control_domain":"4"} +.EE + +Dump the JSON configuration: + +.EX +# mdevctl list -d -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --dumpjson +{ + "mdev_type": "vfio_ap-passthrough", + "start": "manual", + "attrs": [ + { + "assign_adapter": "5" + }, + { + "assign_adapter": "6" + }, + { + "assign_domain": "0xab" + }, + { + "assign_control_domain": "0xab" + }, + { + "assign_domain": "4" + }, + { + "assign_control_domain": "4" + } + ] +} +.EE + +Remove some attributes: + +.EX +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --delattr --index=5 +# mdevctl modify -u 783e6dbb-ea0e-411f-94e2-717eaad438bf --delattr --index=4 +# mdevctl list -dv +783e6dbb-ea0e-411f-94e2-717eaad438bf matrix vfio_ap-passthrough manual + Attrs: + @{0}: {"assign_adapter":"5"} + @{1}: {"assign_adapter":"6"} + @{2}: {"assign_domain":"0xab"} + @{3}: {"assign_control_domain":"0xab"} +.EE + +Define an mdev device from a file: + +.EX +# cat vfio_ap_device.json +{ + "mdev_type": "vfio_ap-passthrough", + "start": "manual", + "attrs": [ + { + "assign_adapter": "5" + }, + { + "assign_domain": "0x47" + }, + { + "assign_domain": "0xff" + } + ] +} +# mdevctl define -p matrix --jsonfile vfio_ap_device.json +e2e73122-cc39-40ee-89eb-b0a47d334cae +# mdevctl list -dv +783e6dbb-ea0e-411f-94e2-717eaad438bf matrix vfio_ap-passthrough manual + Attrs: + @{0}: {"assign_adapter":"5"} + @{1}: {"assign_adapter":"6"} + @{2}: {"assign_domain":"0xab"} + @{3}: {"assign_control_domain":"0xab"} +e2e73122-cc39-40ee-89eb-b0a47d334cae matrix vfio_ap-passthrough manual + Attrs: + @{0}: {"assign_adapter":"5"} + @{1}: {"assign_domain":"0x47"} + @{2}: {"assign_domain":"0xff"} +.EE + +.SH FILES +\fI/etc/mdevctl.d/*\fR + +Configuration files are in one subdirectory per parent device and named +by UUID. + +.SH "CONFIGURATION FILE FORMAT" + +Configuration files are in JSON. Attributes in \fB"attrs"\fR are optional. + +.EX +{ + "mdev_type": \fI"TYPE"\fR, + "start": \fI"auto|manual"\fR, + "attrs": [ + { + \fI"attribute0"\fR: \fI"VALUE"\fR + }, + { + \fI"attribute1"\fR: \fI"VALUE"\fR + } + ] +} +.EE + +.SH "SEE ALSO" +\fBudev\fR(7) +\fBudevadm\fR(8) +\fBdriverctl\fR(8) diff --git a/mdevctl.spec.in b/mdevctl.spec.in new file mode 100644 index 0000000..696082a --- /dev/null +++ b/mdevctl.spec.in @@ -0,0 +1,40 @@ +Name: mdevctl +Version: #VERSION# +Release: 1%{?dist} +Summary: Mediated device management and persistence utility + +Group: System Environment/Kernel +License: LGPLv2 +URL: https://github.com/mdevctl/mdevctl +BuildArch: noarch + +Source0: https://github.com/mdevctl/mdevctl/archive/%{version}/%{name}-%{version}.tar.gz + +BuildRequires: systemd +Requires(post,postun): %{_sbindir}/udevadm +Requires: coreutils udev jq + +%description +mdevctl is a utility for managing and persisting devices in the +mediated device device framework of the Linux kernel. Mediated +devices are sub-devices of a parent device (ex. a vGPU) which +can be dynamically created and potentially used by drivers like +vfio-mdev for assignment to virtual machines. + +%prep +%setup -q -n %{name}-%{version} + +%install +%make_install + +%files +%license COPYING +%doc README.md +%{_sbindir}/mdevctl +%{_sbindir}/lsmdev +%{_udevrulesdir}/60-mdevctl.rules +%dir %{_sysconfdir}/mdevctl.d +%{_mandir}/man8/mdevctl.8* +%{_mandir}/man8/lsmdev.8* + +%changelog