All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/5] CXL FM initial infrastructure
@ 2023-06-02 21:37 Viacheslav Dubeyko
  2023-06-02 21:37 ` [RFC PATCH 1/5] CXL FM: create initial project infrastructure Viacheslav Dubeyko
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Viacheslav Dubeyko @ 2023-06-02 21:37 UTC (permalink / raw)
  To: linux-cxl
  Cc: a.manzanares, Jonathan.Cameron, nilesh.shah, slava, Viacheslav Dubeyko

Implement intitial state of Fabric Manager (FM) project.

CXL Fabric Manager (FM) is the application logic responsible for
system composition and allocation of resources. The FM can be embedded
in the firmware of a device such as a CXL switch, reside on a host,
or could be run on a Baseboard Management Controller (BMC).
CXL Specification 3.0 defines Fabric Management as: "CXL devices can be
configured statically or dynamically via a Fabric Manager (FM),
an external logical process that queries and configures the system’s
operational state using the FM commands defined in this specification.
The FM is defined as the logical process that decides when
reconfiguration is necessary and initiates the commands to perform
configurations. It can take any form, including, but not limited to,
software running on a host machine, embedded software running on a BMC,
embedded firmware running on another CXL device or CXL switch,
or a state machine running within the CXL device itself.".

CXL devices are configured by FM through the Fabric Manager Application
Programming Interface (FM API) command sets through a CCI (Component
Command Interface). A CCI is exposed through a device’s Mailbox registers
or through an MCTP-capable (Management Component Transport Protocol)
interface.

FM API Commands (defined by CXL Specification 3.0):
(1) Physical switch
    - Identify Switch Device,
    - Get Physical Port State,
    - Physical Port Control,
    - Send PPB (PCI-to-PCI Bridge) CXL.io Configuration Request.
(2) Virtual Switch
    - Get Virtual CXL Switch Info,
    - Bind vPPB (Virtual PCI-to-PCI Bridge),
    - Unbind vPPB,
    - Generate AER (Advanced Error Reporting Event).
(3) MLD Port
    - Tunnel Management Command,
    - Send LD (Logical Device) or FMLD (Fabric Manager-owned Logical Device)
      CXL.io Configuration Request,
    - Send LD CXL.io Memory Request.
(4) MLD Components
    - Get LD (Logical Device) Info,
    - Get LD Allocations,
    - Set LD Allocations,
    - Get QoS Control,
    - Set QoS Control,
    - Get QoS Status,
    - Get QoS Allocated Bandwidth,
    - Set QoS Allocated Bandwidth,
    - Get QoS Bandwidth Limit,
    - Set QoS Bandwidth Limit.
(5) Multi- Headed Devices (Get Multi-Headed Info).
(6) DCD (Dynamic Capacity Device) Management
    - Get DCD Info,
    - Get Host Dynamic Capacity Region Configuration,
    - Set Dynamic Capacity Region Configuration,
    - Get DCD Extent Lists,
    - Initiate Dynamic Capacity Add,
    - Initiate Dynamic Capacity Release.

After the initial configuration is complete and a CCI on the switch is
operational, an FM can send Management Commands to the switch.

An FM may perform the following dynamic management actions on a CXL switch:
(1) Query switch information and configuration details,
(2) Bind or Unbind ports,
(3) Register to receive and handle event notifications from the switch
    (e.g., hot plug, surprise removal, and failures).

A switch with MLD (Multi-Logical Device) requires an FM to perform
the following management activities:
(1) MLD discovery,
(2) LD (Logical Device) binding/unbinding,
(3) Management Command Tunneling.

The FM can connect to an MLD (Multi-Logical Device) over a direct connection or
by tunneling its management commands through the CCI of the CXL switch
to which the device is connected. The FM can perform the following
operations:
(1) Memory allocation and QoS Telemetry management,
(2) Security (e.g., LD erasure after unbinding),
(3) Error handling.

fm_cli - FM configuration tool
Commands:

Discover - discover available agents
Subcommands:
    - fm_cli discover fm
         (discover FM instances)
    - fm_cli discover cxl_devices
         (discover CXL devices)
    - fm_cli discover cxl_switches
         (discover CXL switches)
    - fm_cli discover logical_devices
         (discover logical devices)

FM - manage Fabric Manager
Subcommands:
    - fm_cli fm get_info
         (get FM status/info)
    - fm_cli fm start
         (start FM instance)
    - fm_cli fm restart
         (restart FM instance)
    - fm_cli fm stop
         (stop FM instance)
    - fm_cli fm get_config
         (get FM configuration)
    - fm_cli fm set_config
         (set FM configuration)
    - fm_cli fm get_events
         (get event records)

Switch - manage CXL switch
Subcommands:
    - fm_cli switch get_info
         (get CXL switch info/status)
    - fm_cli switch get_config
         (get switch configuraiton)
    - fm_cli switch set_config
         (set switch configuration)

Logical Device - manage logical devices
Subcommands:
    - fm_cli multi_headed_device info
         (retrieves the number of heads, number of supported LDs,
          and Head-to-LD mapping of a Multi-Headed device)
    - fm_cli logical_device bind
         (bind logical device)
    - fm_cli logical_device unbind
         (unbind logical device)
    - fm_cli logical_device connect
         (connect Multi Logical Device to CXL switch)
    - fm_cli logical_device disconnect
         (disconnect Multi Logical Device from CXL switch)
    - fm_cli logical_device get_allocation
         (Get LD Allocations: retrieves the memory allocations of the MLD)
    - fm_cli logical_device set_allocation
         (Set LD Allocations: sets the memory allocation for each LD)
    - fm_cli logical_device get_qos_control
         (Get QoS Control: retrieves the MLD’s QoS control parameters)
    - fm_cli logical_device set_qos_control
         (Set QoS Control: sets the MLD’s QoS control parameters)
    - fm_cli logical_device get_qos_status
         (Get QoS Status: retrieves the MLD’s QoS Status)
    - fm_cli logical_device get_qos_allocated_bandwidth
         (Get QoS Allocated Bandwidth: retrieves the MLD’s QoS allocated
          bandwidth on a per-LD basis)
    - fm_cli logical_device set_qos_allocated_bandwidth
         (Set QoS Allocated Bandwidth: sets the MLD’s QoS allocated bandwidth
          on a per-LD basis)
    - fm_cli logical_device get_qos_bandwidth_limit
         (Get QoS Bandwidth Limit: retrieves the MLD’s QoS bandwidth limit
          on a per-LD basis)
    - fm_cli logical_device set_qos_bandwidth_limit
         (Set QoS Bandwidth Limit: sets the MLD’s QoS bandwidth limit
          on a per-LD basis)
    - fm_cli logical_device erase
         (secure erase after unbinding)

PCI-to-PCI Bridge - manage PPB (PCI-to-PCI Bridge)
Subcommands:
    - fm_cli ppb config
         (Send PPB (PCI-to-PCI Bridge) CXL.io Configuration Request)
    - fm_cli ppb bind
         (Bind vPPB: Virtual PCI-to-PCI Bridge inside a CXL switch
          that is host-owned)
    - fm_cli ppb unbind
         (Unbind vPPB: unbinds the physical port or LD from the virtual
          hierarchy PPB)

Physical Port - manage physical ports
Subcommands:
    - fm_cli physical_port get_info
         (get state of physical port)
    - fm_cli physical_port control
         (control unbound ports and MLD ports, including issuing
          resets and controlling sidebands)
    - fm_cli physical_port bind
         (bind physical port to vPPB (Virtual PCI-to-PCI Bridge))
    - fm_cli physical_port unbind
         (unbind physical port from vPPB (Virtual PCI-to-PCI Bridge))

MLD (Multi-Logical Device) Port - manage Multi-Logical Device ports
Subcommands:
    - fm_cli mld_port tunnel
         (Tunnel Management Command: tunnels the provided command to
          LD FFFFh of the MLD on the specified port)
    - fm_cli mld_port send_config
         (Send LD (Logical Device) or FMLD (Fabric Manager-owned
          Logical Device) CXL.io Configuration Request)
    - fm_cli mld_port send_memory_request
         (Send LD CXL.io Memory Request)

DCD (Dynamic Capacity Device) - manage Dynamic Capacity Device
Subcommands:
    - fm_cli dcd get_info
         (Get DCD Info: retrieves the number of supported hosts,
          total Dynamic Capacity of the device, and supported region
          configurations)
    - fm_cli dcd get_capacity_config
         (Get Host Dynamic Capacity Region Configuration: retrieves
          the Dynamic Capacity configuration for a specified host)
    - fm_cli dcd set_capacity_config
         (Set Dynamic Capacity Region Configuration: sets
          the configuration of a DC Region)
    - fm_cli dcd get_extent_list
         (Get DCD Extent Lists: retrieves the Dynamic Capacity Extent
          List for a specified host)
    - fm_cli dcd add_capacity
         (Initiate Dynamic Capacity Add: initiates the addition of
          Dynamic Capacity to the specified region on a host)
    - fm_cli dcd release_capacity
         (Initiate Dynamic Capacity Release: initiates the release of
          Dynamic Capacity from a host)

FM daemon receives requests from configuration tool and executes
commands by means of interaction with kernel-space subsystems.
The responsibility of FM daemon:
    - Execute configuration tool commands
    - Manage hot-add and hot-removal of devices
    - Manage surprise removal of devices
    - Receive and handle even notifications from the CXL switch
    - Logging events
    - Memory allocation and QoS Telemetry management
    - Error/Failure handling

BUILD PROJECT:

cargo build
   Compiling proc-macro2 v1.0.49
   Compiling version_check v0.9.4
   Compiling libc v0.2.139
   Compiling unicode-ident v1.0.6
   Compiling quote v1.0.23
   Compiling syn v1.0.107
   Compiling io-lifetimes v1.0.3
   Compiling rustix v0.36.6
   Compiling linux-raw-sys v0.1.4
   Compiling bitflags v1.3.2
   Compiling heck v0.4.0
   Compiling os_str_bytes v6.4.1
   Compiling strsim v0.10.0
   Compiling termcolor v1.1.3
   Compiling clap_lex v0.3.0
   Compiling once_cell v1.17.0
   Compiling proc-macro-error-attr v1.0.4
   Compiling proc-macro-error v1.0.4
   Compiling daemonize v0.5.0
   Compiling is-terminal v0.4.2
   Compiling clap_derive v4.0.21
   Compiling clap v4.0.32
   Compiling fabric_manager v0.1.0 (/home/slavad/RUST/FM_CLI/FABRIC-MANAGER-REPO/fabric_manager)
    Finished dev [unoptimized + debuginfo] target(s) in 8.79s

START FM DAEMON:

sudo ./fm_daemon -d --ip 127.0.0.1 --port 7878
fm_daemon 0.0.1

CLI tool:

./fm_cli --ip 127.0.0.1 --port 7878 -d dcd get_info
fm_cli 0.0.1
Get Dynamic Capacity Device (DCD) info
Successfully connected to server: 127.0.0.1:7878
COMMAND: "DCD_GET_INFO\n"
RESPONCE: "NO_DATA"

FM daemon log file (/tmp/fm_daemon.log):

cat /tmp/fm_daemon.log 
fm_daemon 0.0.1: Daemonized!
Ready to accept connections: 127.0.0.1:7878
Process request...
Request: "DCD_GET_INFO"
DCD_GET_INFO
RESPONCE: "NO_DATA\n"

Viacheslav Dubeyko (5):
  CXL FM: create initial project infrastructure
  CXL FM: [lib] introduce CXL FM library
  CXL FM: [fm_daemon] introduce CXL FM daemon
  CXL FM: [fm_orchestrator] introduce CXL FM orchestrator
  CXL FM: [fm_cli] introduce CXL FM CLI tool

-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC PATCH 1/5] CXL FM: create initial project infrastructure
  2023-06-02 21:37 [RFC PATCH 0/5] CXL FM initial infrastructure Viacheslav Dubeyko
@ 2023-06-02 21:37 ` Viacheslav Dubeyko
  2023-06-02 21:37 ` [RFC PATCH 2/5] CXL FM: [lib] introduce CXL FM library Viacheslav Dubeyko
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Viacheslav Dubeyko @ 2023-06-02 21:37 UTC (permalink / raw)
  To: linux-cxl
  Cc: a.manzanares, Jonathan.Cameron, nilesh.shah, slava, Viacheslav Dubeyko

This patch creates the initial project infrastructure.

Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
CC: Adam Manzanares <a.manzanares@samsung.com>
---
 AUTHORS    |   1 +
 COPYING    |  14 +++
 Cargo.lock | 333 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 LICENSE    | 201 ++++++++++++++++++++++++++++++++
 4 files changed, 549 insertions(+)
 create mode 100644 AUTHORS
 create mode 100644 COPYING
 create mode 100644 Cargo.lock
 create mode 100644 LICENSE

diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..8a6d84b
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Authors: Viacheslav Dubeyko <slava@dubeyko.com>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..28f81b7
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,14 @@
+Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..52b556e
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,333 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cc"
+version = "1.0.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
+
+[[package]]
+name = "clap"
+version = "4.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
+dependencies = [
+ "bitflags",
+ "clap_derive",
+ "clap_lex",
+ "is-terminal",
+ "once_cell",
+ "strsim",
+ "termcolor",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "daemonize"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "errno"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "fabric_manager"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "daemonize",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
+dependencies = [
+ "hermit-abi",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.139"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
+
+[[package]]
+name = "once_cell"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
+
+[[package]]
+name = "os_str_bytes"
+version = "6.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.49"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustix"
+version = "0.36.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH 2/5] CXL FM: [lib] introduce CXL FM library
  2023-06-02 21:37 [RFC PATCH 0/5] CXL FM initial infrastructure Viacheslav Dubeyko
  2023-06-02 21:37 ` [RFC PATCH 1/5] CXL FM: create initial project infrastructure Viacheslav Dubeyko
@ 2023-06-02 21:37 ` Viacheslav Dubeyko
  2023-06-02 21:37 ` [RFC PATCH 3/5] CXL FM: [fm_daemon] introduce CXL FM daemon Viacheslav Dubeyko
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Viacheslav Dubeyko @ 2023-06-02 21:37 UTC (permalink / raw)
  To: linux-cxl
  Cc: a.manzanares, Jonathan.Cameron, nilesh.shah, slava, Viacheslav Dubeyko

This patch creates the CXL FM library.

Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
CC: Adam Manzanares <a.manzanares@samsung.com>
---
 .gitignore            |   1 +
 Cargo.toml            |  12 ++++
 fm_library/Cargo.lock |   7 +++
 fm_library/Cargo.toml |  12 ++++
 fm_library/src/lib.rs | 133 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 165 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Cargo.toml
 create mode 100644 fm_library/Cargo.lock
 create mode 100644 fm_library/Cargo.toml
 create mode 100644 fm_library/src/lib.rs

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..96fc75e
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "fabric_manager"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lib]
+name = "fm_library"
+path = "fm_library/src/lib.rs"
+
+[dependencies]
diff --git a/fm_library/Cargo.lock b/fm_library/Cargo.lock
new file mode 100644
index 0000000..de5870a
--- /dev/null
+++ b/fm_library/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "fm_communication"
+version = "0.1.0"
diff --git a/fm_library/Cargo.toml b/fm_library/Cargo.toml
new file mode 100644
index 0000000..79dc6c4
--- /dev/null
+++ b/fm_library/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "fm_communication"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lib]
+name = "fm_communication"
+path = "src/lib.rs"
+
+[dependencies]
diff --git a/fm_library/src/lib.rs b/fm_library/src/lib.rs
new file mode 100644
index 0000000..c06fd9c
--- /dev/null
+++ b/fm_library/src/lib.rs
@@ -0,0 +1,133 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM library implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_fm_lib {
+	use std::net::{TcpStream};
+	use std::io::Write;
+	use std::io::BufReader;
+	use std::io::BufRead;
+
+	/*
+	 * Available commands
+	 */
+	pub const CXL_FM_DISCOVER_FM_COMMAND: &str		= "DISCOVER_FM";
+	pub const CXL_FM_DISCOVER_CXL_DEVICE_COMMAND: &str	= "DISCOVER_CXL_DEVICE";
+	pub const CXL_FM_DISCOVER_CXL_SWITCH_COMMAND: &str	= "DISCOVER_CXL_SWITCH";
+	pub const CXL_FM_DISCOVER_LD_COMMAND: &str		= "DISCOVER_LOGICAL_DEVICE";
+
+	pub const CXL_FM_GET_FM_INFO_COMMAND: &str		= "GET_FM_INFO";
+	pub const CXL_FM_START_FM_COMMAND: &str			= "START_FM";
+	pub const CXL_FM_RESTART_FM_COMMAND: &str		= "RESTART_FM";
+	pub const CXL_FM_STOP_FM_COMMAND: &str			= "STOP_FM";
+	pub const CXL_FM_GET_FM_CONFIG_COMMAND: &str		= "GET_FM_CONFIG";
+	pub const CXL_FM_SET_FM_CONFIG_COMMAND: &str		= "SET_FM_CONFIG";
+	pub const CXL_FM_GET_FM_EVENTS_COMMAND: &str		= "GET_FM_EVENTS";
+
+	pub const CXL_FM_GET_SWITCH_INFO_COMMAND: &str		= "GET_SWITCH_INFO";
+	pub const CXL_FM_GET_SWITCH_CONFIG_COMMAND: &str	= "GET_SWITCH_CONFIG";
+	pub const CXL_FM_SET_SWITCH_CONFIG_COMMAND: &str	= "SET_SWITCH_CONFIG";
+
+	pub const CXL_FM_GET_MHD_INFO_COMMAND: &str		= "GET_MHD_INFO";
+
+	pub const CXL_FM_BIND_LD_COMMAND: &str			= "BIND_LOGICAL_DEVICE";
+	pub const CXL_FM_UNBIND_LD_COMMAND: &str		= "UNBIND_LOGICAL_DEVICE";
+	pub const CXL_FM_CONNECT_MLD_COMMAND: &str		= "CONNECT_MLD";
+	pub const CXL_FM_DISCONNECT_MLD_COMMAND: &str		= "DISCONNECT_MLD";
+	pub const CXL_FM_GET_LD_ALLOCATION_COMMAND: &str	= "GET_LD_ALLOCATION";
+	pub const CXL_FM_SET_LD_ALLOCATION_COMMAND: &str	= "SET_LD_ALLOCATION";
+	pub const CXL_FM_GET_QOS_CONTROL_COMMAND: &str		= "GET_LD_QOS_CONTROL";
+	pub const CXL_FM_SET_QOS_CONTROL_COMMAND: &str		= "SET_LD_QOS_CONTROL";
+	pub const CXL_FM_GET_QOS_STATUS_COMMAND: &str		= "GET_LD_QOS_STATUS";
+	pub const CXL_FM_GET_QOS_BANDWIDTH_COMMAND: &str	= "GET_LD_QOS_BANDWIDTH";
+	pub const CXL_FM_SET_QOS_BANDWIDTH_COMMAND: &str	= "SET_LD_QOS_BANDWIDTH";
+	pub const CXL_FM_GET_QOS_BANDWIDTH_LIMIT_COMMAND: &str	= "GET_LD_QOS_BANDWIDTH_LIMIT";
+	pub const CXL_FM_SET_QOS_BANDWIDTH_LIMIT_COMMAND: &str	= "SET_LD_QOS_BANDWIDTH_LIMIT";
+	pub const CXL_FM_LD_ERASE: &str				= "LD_ERASE";
+
+	pub const CXL_FM_GET_PPB_CONFIG_COMMAND: &str		= "GET_PPB_CONFIG";
+	pub const CXL_FM_PPB_BIND_COMMAND: &str			= "PPB_BIND";
+	pub const CXL_FM_PPB_UNBIND_COMMAND: &str		= "PPB_UNBIND";
+
+	pub const CXL_FM_GET_PHYSICAL_PORT_INFO_COMMAND: &str	= "GET_PHYSICAL_PORT_INFO";
+	pub const CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND: &str	= "PHYSICAL_PORT_CONTROL";
+	pub const CXL_FM_BIND_PHYSICAL_PORT_COMMAND: &str	= "BIND_PHYSICAL_PORT";
+	pub const CXL_FM_UNBIND_PHYSICAL_PORT_COMMAND: &str	= "UNBIND_PHYSICAL_PORT";
+
+	pub const CXL_FM_MLD_PORT_TUNNEL_COMMAND: &str		= "MLD_PORT_TUNNEL";
+	pub const CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND: &str	= "MLD_PORT_SEND_CONFIG";
+	pub const CXL_FM_MLD_PORT_SEND_MEM_REQ_COMMAND: &str	= "MLD_PORT_SEND_MEM_REQ";
+
+	pub const CXL_FM_DCD_GET_INFO_COMMAND: &str		= "DCD_GET_INFO";
+	pub const CXL_FM_DCD_GET_CONFIG_COMMAND: &str		= "DCD_GET_CONFIG";
+	pub const CXL_FM_DCD_SET_CONFIG_COMMAND: &str		= "DCD_SET_CONFIG";
+	pub const CXL_FM_DCD_GET_EXTENT_COMMAND: &str		= "DCD_GET_EXTENT";
+	pub const CXL_FM_DCD_ADD_CAPACITY_COMMAND: &str		= "DCD_ADD_CAPACITY";
+	pub const CXL_FM_DCD_RELEASE_CAPACITY_COMMAND: &str	= "DCD_RELEASE_CAPACITY";
+
+	/*
+	 * Service responces
+	 */
+	pub const CXL_FM_UNKNOWN_COMMAND: &str			= "UNKNOWN_COMMAND";
+	pub const CXL_FM_NO_DATA: &str				= "NO_DATA";
+
+	/*
+	 * struct CxlFmOptions - configuration options
+	 * @ip_port: IP address + port
+	 * @is_debug: does it need to show debug output?
+	 */
+	pub struct CxlFmOptions {
+		pub ip_port: String,
+		pub is_debug: bool,
+	}
+
+	/*
+	 * Send command to FM
+	 */
+	pub fn send_command(mut stream: &TcpStream, command: &str, env: &CxlFmOptions) {
+		let full_command = format!("{command}\n");
+
+		if env.is_debug {
+			println!("COMMAND: {:#?}", full_command);
+		}
+
+		stream.write_all(full_command.as_bytes()).unwrap();
+
+		let buf_reader = BufReader::new(&mut stream);
+		let mut response_line = buf_reader.lines();
+
+		if env.is_debug {
+			println!("RESPONCE: {:#?}", response_line.next().unwrap().unwrap());
+		}
+	}
+
+	/*
+	 * Send responce from FM
+	 */
+	pub fn send_responce(mut stream: &TcpStream, responce: &str, env: &CxlFmOptions) {
+		let full_responce = format!("{responce}\n");
+
+		if env.is_debug {
+			println!("RESPONCE: {:#?}", full_responce);
+		}
+
+		stream.write_all(full_responce.as_bytes()).unwrap();
+	}
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH 3/5] CXL FM: [fm_daemon] introduce CXL FM daemon
  2023-06-02 21:37 [RFC PATCH 0/5] CXL FM initial infrastructure Viacheslav Dubeyko
  2023-06-02 21:37 ` [RFC PATCH 1/5] CXL FM: create initial project infrastructure Viacheslav Dubeyko
  2023-06-02 21:37 ` [RFC PATCH 2/5] CXL FM: [lib] introduce CXL FM library Viacheslav Dubeyko
@ 2023-06-02 21:37 ` Viacheslav Dubeyko
  2023-06-02 21:37 ` [RFC PATCH 4/5] CXL FM: [fm_orchestrator] introduce CXL FM orchestrator Viacheslav Dubeyko
  2023-06-02 21:37 ` [RFC PATCH 5/5] CXL FM: [fm_cli] introduce CXL FM CLI tool Viacheslav Dubeyko
  4 siblings, 0 replies; 6+ messages in thread
From: Viacheslav Dubeyko @ 2023-06-02 21:37 UTC (permalink / raw)
  To: linux-cxl
  Cc: a.manzanares, Jonathan.Cameron, nilesh.shah, slava, Viacheslav Dubeyko

This patch creates the intial state of CXL FM daemon.

Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
CC: Adam Manzanares <a.manzanares@samsung.com>
---
 Cargo.toml                               |   6 +
 fm_daemon/Cargo.toml                     |  10 +
 fm_daemon/src/discover.rs                |  63 +++++
 fm_daemon/src/dynamic_capacity_device.rs |  99 ++++++++
 fm_daemon/src/fm.rs                      |  75 ++++++
 fm_daemon/src/logical_device.rs          | 195 ++++++++++++++
 fm_daemon/src/main.rs                    | 307 +++++++++++++++++++++++
 fm_daemon/src/mld_port.rs                |  63 +++++
 fm_daemon/src/multi_headed_device.rs     |  39 +++
 fm_daemon/src/pci2pci_bridge.rs          |  63 +++++
 fm_daemon/src/physical_port.rs           |  75 ++++++
 fm_daemon/src/switch.rs                  |  63 +++++
 12 files changed, 1058 insertions(+)
 create mode 100644 fm_daemon/Cargo.toml
 create mode 100644 fm_daemon/src/discover.rs
 create mode 100644 fm_daemon/src/dynamic_capacity_device.rs
 create mode 100644 fm_daemon/src/fm.rs
 create mode 100644 fm_daemon/src/logical_device.rs
 create mode 100644 fm_daemon/src/main.rs
 create mode 100644 fm_daemon/src/mld_port.rs
 create mode 100644 fm_daemon/src/multi_headed_device.rs
 create mode 100644 fm_daemon/src/pci2pci_bridge.rs
 create mode 100644 fm_daemon/src/physical_port.rs
 create mode 100644 fm_daemon/src/switch.rs

diff --git a/Cargo.toml b/Cargo.toml
index 96fc75e..b8d169d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,4 +9,10 @@ edition = "2021"
 name = "fm_library"
 path = "fm_library/src/lib.rs"
 
+[[bin]]
+name = "fm_daemon"
+path = "fm_daemon/src/main.rs"
+
 [dependencies]
+clap = { version = "4.0.32", features = ["derive"] }
+daemonize = "0.5.0"
diff --git a/fm_daemon/Cargo.toml b/fm_daemon/Cargo.toml
new file mode 100644
index 0000000..70e2f28
--- /dev/null
+++ b/fm_daemon/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "fm_daemon"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+daemonize = "0.5.0"
+fm_library = { path = "../fm_library/" }
diff --git a/fm_daemon/src/discover.rs b/fm_daemon/src/discover.rs
new file mode 100644
index 0000000..6bda43d
--- /dev/null
+++ b/fm_daemon/src/discover.rs
@@ -0,0 +1,63 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM daemon implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_fm_discover_command {
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_responce;
+	use fm_library::cxl_fm_lib::CXL_FM_DISCOVER_CXL_DEVICE_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DISCOVER_CXL_SWITCH_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DISCOVER_LD_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_NO_DATA;
+
+	/*
+	 * Discover available CXL devices
+	 */
+	pub fn discover_cxl_devices(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_DISCOVER_CXL_DEVICE_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Discover available CXL switches
+	 */
+	pub fn discover_cxl_switches(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_DISCOVER_CXL_SWITCH_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Discover available logical devices
+	 */
+	pub fn discover_logical_devices(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_DISCOVER_LD_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+}
diff --git a/fm_daemon/src/dynamic_capacity_device.rs b/fm_daemon/src/dynamic_capacity_device.rs
new file mode 100644
index 0000000..6926ec7
--- /dev/null
+++ b/fm_daemon/src/dynamic_capacity_device.rs
@@ -0,0 +1,99 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_dcd_command {
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_responce;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_GET_INFO_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_GET_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_SET_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_GET_EXTENT_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_ADD_CAPACITY_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_RELEASE_CAPACITY_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_NO_DATA;
+
+	/*
+	 * Get Dynamic Capacity Device (DCD) info
+	 */
+	pub fn get_info(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_DCD_GET_INFO_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Get dynamic capacity region configuration
+	 */
+	pub fn get_capacity_config(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_DCD_GET_CONFIG_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Set dynamic capacity region configuration
+	 */
+	pub fn set_capacity_config(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_DCD_SET_CONFIG_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Get Dynamic Capacity Device (DCD) extent list
+	 */
+	pub fn get_extent_list(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_DCD_GET_EXTENT_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Initiate dynamic capacity add
+	 */
+	pub fn add_capacity(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_DCD_ADD_CAPACITY_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Initiate dynamic capacity release
+	 */
+	pub fn release_capacity(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_DCD_RELEASE_CAPACITY_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+}
diff --git a/fm_daemon/src/fm.rs b/fm_daemon/src/fm.rs
new file mode 100644
index 0000000..b40abb3
--- /dev/null
+++ b/fm_daemon/src/fm.rs
@@ -0,0 +1,75 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM daemon implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_fm_command {
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_responce;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_FM_INFO_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_FM_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_FM_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_FM_EVENTS_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_NO_DATA;
+
+	/*
+	 * Get Fabric Manager (FM) status/info
+	 */
+	pub fn get_info(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_FM_INFO_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Get Fabric Manager (FM) configuration
+	 */
+	pub fn get_config(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_FM_CONFIG_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Set Fabric Manager (FM) configuration
+	 */
+	pub fn set_config(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_SET_FM_CONFIG_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Get Fabric Manager (FM) event records
+	 */
+	pub fn get_events(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_FM_EVENTS_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+}
diff --git a/fm_daemon/src/logical_device.rs b/fm_daemon/src/logical_device.rs
new file mode 100644
index 0000000..c62727c
--- /dev/null
+++ b/fm_daemon/src/logical_device.rs
@@ -0,0 +1,195 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_logical_device_command {
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_responce;
+	use fm_library::cxl_fm_lib::CXL_FM_BIND_LD_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_UNBIND_LD_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_CONNECT_MLD_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DISCONNECT_MLD_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_LD_ALLOCATION_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_LD_ALLOCATION_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_QOS_CONTROL_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_QOS_CONTROL_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_QOS_STATUS_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_QOS_BANDWIDTH_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_QOS_BANDWIDTH_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_QOS_BANDWIDTH_LIMIT_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_QOS_BANDWIDTH_LIMIT_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_LD_ERASE;
+	use fm_library::cxl_fm_lib::CXL_FM_NO_DATA;
+
+	/*
+	 * Bind Logical Device (LD)
+	 */
+	pub fn bind(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_BIND_LD_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Unbind Logical Device (LD)
+	 */
+	pub fn unbind(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_UNBIND_LD_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Connect Multi Logical Device (MLD) to CXL switch
+	 */
+	pub fn connect(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_CONNECT_MLD_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Disconnect Multi Logical Device (MLD) from CXL switch
+	 */
+	pub fn disconnect(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_DISCONNECT_MLD_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Get Logical Device (LD) allocations
+	 */
+	pub fn get_allocation(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_LD_ALLOCATION_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Set Logical Device (LD) allocations
+	 */
+	pub fn set_allocation(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_SET_LD_ALLOCATION_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Get QoS control
+	 */
+	pub fn get_qos_control(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_QOS_CONTROL_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Set QoS control
+	 */
+	pub fn set_qos_control(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_SET_QOS_CONTROL_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Get QoS status
+	 */
+	pub fn get_qos_status(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_QOS_STATUS_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Get QoS allocated bandwidth
+	 */
+	pub fn get_qos_bandwidth(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_QOS_BANDWIDTH_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Set QoS allocated bandwidth
+	 */
+	pub fn set_qos_bandwidth(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_SET_QOS_BANDWIDTH_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Get QoS bandwidth limit
+	 */
+	pub fn get_qos_bandwidth_limit(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_QOS_BANDWIDTH_LIMIT_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Set QoS bandwidth limit
+	 */
+	pub fn set_qos_bandwidth_limit(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_SET_QOS_BANDWIDTH_LIMIT_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Secure erase after unbinding
+	 */
+	pub fn erase(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_LD_ERASE);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+}
diff --git a/fm_daemon/src/main.rs b/fm_daemon/src/main.rs
new file mode 100644
index 0000000..fef4997
--- /dev/null
+++ b/fm_daemon/src/main.rs
@@ -0,0 +1,307 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM daemon implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+mod discover;
+mod fm;
+mod switch;
+mod multi_headed_device;
+mod logical_device;
+mod pci2pci_bridge;
+mod physical_port;
+mod mld_port;
+mod dynamic_capacity_device;
+
+extern crate daemonize;
+
+use std::fs::File;
+use daemonize::Daemonize;
+use clap::{Arg, Command};
+use std::{
+	io::{prelude::*, BufReader},
+	net::{TcpListener, TcpStream},
+};
+pub use crate::discover::cxl_fm_discover_command;
+pub use crate::fm::cxl_fm_command;
+pub use crate::switch::cxl_switch_command;
+pub use crate::multi_headed_device::cxl_mh_device_command;
+pub use crate::logical_device::cxl_logical_device_command;
+pub use crate::pci2pci_bridge::cxl_ppb_command;
+pub use crate::physical_port::cxl_physical_port_command;
+pub use crate::mld_port::cxl_mld_port_command;
+pub use crate::dynamic_capacity_device::cxl_dcd_command;
+pub use fm_library::cxl_fm_lib::send_responce;
+pub use fm_library::cxl_fm_lib::CxlFmOptions;
+
+/*
+ * CXL FM daemon version
+ */
+const CXL_FM_DAEMON_VERSION: &str = "0.0.1";
+
+/*
+ * CXL FM daemon strings
+ */
+const CXL_FM_DAEMON_NAME: &str = "fm_daemon";
+const CXL_FM_DAEMON_DESCRIPTOR: &str = "CXL Fabric Manager (FM) daemon";
+const CXL_FM_DAEMON_DEBUG_OPTION: &str = "debug";
+const CXL_FM_DAEMON_DEBUG_OPTION_SHORT: char = 'd';
+const CXL_FM_DAEMON_IP_ADDRESS_OPTION: &str = "ip";
+const CXL_FM_DAEMON_IP_ADDRESS_OPTION_SHORT: char = 'i';
+const CXL_FM_DAEMON_PORT_OPTION: &str = "port";
+const CXL_FM_DAEMON_PORT_OPTION_SHORT: char = 'p';
+
+const CXL_FM_DAEMON_WORKING_DIRECTORY: &str = "/tmp";
+const CXL_FM_DAEMON_LOG_FILE_PATH: &str = "/tmp/fm_daemon.log";
+const CXL_FM_DAEMON_ERROR_MESSAGES_FILE_PATH: &str = "/tmp/fm_daemon.err";
+const CXL_FM_DAEMON_USER: &str = "nobody";
+const CXL_FM_DAEMON_GROUP: &str = "bin";
+const CXL_FM_DAEMON_GROUP_ID: u32 = 2;
+const CXL_FM_DAEMON_UMASK: u32 = 0o777;
+
+/*
+ * Command line interface definition
+ */
+fn cli() -> Command {
+	Command::new(CXL_FM_DAEMON_NAME)
+		.about(CXL_FM_DAEMON_DESCRIPTOR)
+		.version(CXL_FM_DAEMON_VERSION)
+		.arg_required_else_help(true)
+		.arg(Arg::new(CXL_FM_DAEMON_DEBUG_OPTION)
+			.short(CXL_FM_DAEMON_DEBUG_OPTION_SHORT)
+			.long(CXL_FM_DAEMON_DEBUG_OPTION)
+			.action(clap::ArgAction::SetTrue))
+		.arg(Arg::new(CXL_FM_DAEMON_IP_ADDRESS_OPTION)
+			.short(CXL_FM_DAEMON_IP_ADDRESS_OPTION_SHORT)
+			.long(CXL_FM_DAEMON_IP_ADDRESS_OPTION)
+			.action(clap::ArgAction::Set)
+			.required(true))
+		.arg(Arg::new(CXL_FM_DAEMON_PORT_OPTION)
+			.short(CXL_FM_DAEMON_PORT_OPTION_SHORT)
+			.long(CXL_FM_DAEMON_PORT_OPTION)
+			.action(clap::ArgAction::Set)
+			.required(true))
+}
+
+/*
+OD * Connection request processing logic
+ */
+fn handle_connection(mut stream: &TcpStream, env: &CxlFmOptions) {
+	if env.is_debug {
+		println!("Process request...");
+	}
+
+	let buf_reader = BufReader::new(&mut stream);
+	let request_line = buf_reader.lines().next().unwrap().unwrap();
+
+	if env.is_debug {
+		println!("Request: {:#?}", request_line);
+	}
+
+	match request_line.as_str() {
+		fm_library::cxl_fm_lib::CXL_FM_DISCOVER_CXL_DEVICE_COMMAND => {
+			cxl_fm_discover_command::discover_cxl_devices(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_DISCOVER_CXL_SWITCH_COMMAND => {
+			cxl_fm_discover_command::discover_cxl_switches(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_DISCOVER_LD_COMMAND => {
+			cxl_fm_discover_command::discover_logical_devices(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_FM_INFO_COMMAND => {
+			cxl_fm_command::get_info(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_FM_CONFIG_COMMAND => {
+			cxl_fm_command::get_config(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_SET_FM_CONFIG_COMMAND => {
+			cxl_fm_command::set_config(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_FM_EVENTS_COMMAND => {
+			cxl_fm_command::get_events(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_SWITCH_INFO_COMMAND => {
+			cxl_switch_command::get_info(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_SWITCH_CONFIG_COMMAND => {
+			cxl_switch_command::get_config(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_SET_SWITCH_CONFIG_COMMAND => {
+			cxl_switch_command::set_config(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_MHD_INFO_COMMAND => {
+			cxl_mh_device_command::get_info(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_BIND_LD_COMMAND => {
+			cxl_logical_device_command::bind(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_UNBIND_LD_COMMAND => {
+			cxl_logical_device_command::unbind(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_CONNECT_MLD_COMMAND => {
+			cxl_logical_device_command::connect(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_DISCONNECT_MLD_COMMAND => {
+			cxl_logical_device_command::disconnect(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_LD_ALLOCATION_COMMAND => {
+			cxl_logical_device_command::get_allocation(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_SET_LD_ALLOCATION_COMMAND => {
+			cxl_logical_device_command::set_allocation(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_QOS_CONTROL_COMMAND => {
+			cxl_logical_device_command::get_qos_control(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_SET_QOS_CONTROL_COMMAND => {
+			cxl_logical_device_command::set_qos_control(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_QOS_STATUS_COMMAND => {
+			cxl_logical_device_command::get_qos_status(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_QOS_BANDWIDTH_COMMAND => {
+			cxl_logical_device_command::get_qos_bandwidth(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_SET_QOS_BANDWIDTH_COMMAND => {
+			cxl_logical_device_command::set_qos_bandwidth(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_QOS_BANDWIDTH_LIMIT_COMMAND => {
+			cxl_logical_device_command::get_qos_bandwidth_limit(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_SET_QOS_BANDWIDTH_LIMIT_COMMAND => {
+			cxl_logical_device_command::set_qos_bandwidth_limit(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_LD_ERASE => {
+			cxl_logical_device_command::erase(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_PPB_CONFIG_COMMAND => {
+			cxl_ppb_command::config(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_PPB_BIND_COMMAND => {
+			cxl_ppb_command::bind(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_PPB_UNBIND_COMMAND => {
+			cxl_ppb_command::unbind(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_GET_PHYSICAL_PORT_INFO_COMMAND => {
+			cxl_physical_port_command::get_info(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND => {
+			cxl_physical_port_command::control(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_BIND_PHYSICAL_PORT_COMMAND => {
+			cxl_physical_port_command::bind(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_UNBIND_PHYSICAL_PORT_COMMAND => {
+			cxl_physical_port_command::unbind(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_MLD_PORT_TUNNEL_COMMAND => {
+			cxl_mld_port_command::tunnel(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND => {
+			cxl_mld_port_command::send_config(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_MLD_PORT_SEND_MEM_REQ_COMMAND => {
+			cxl_mld_port_command::send_memory_request(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_DCD_GET_INFO_COMMAND => {
+			cxl_dcd_command::get_info(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_DCD_GET_CONFIG_COMMAND => {
+			cxl_dcd_command::get_capacity_config(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_DCD_SET_CONFIG_COMMAND => {
+			cxl_dcd_command::set_capacity_config(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_DCD_GET_EXTENT_COMMAND => {
+			cxl_dcd_command::get_extent_list(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_DCD_ADD_CAPACITY_COMMAND => {
+			cxl_dcd_command::add_capacity(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_DCD_RELEASE_CAPACITY_COMMAND => {
+			cxl_dcd_command::release_capacity(stream, env);
+		},
+		_ => send_responce(stream, fm_library::cxl_fm_lib::CXL_FM_UNKNOWN_COMMAND, env),
+	}
+}
+
+/*
+ * Main logic of daemon
+ */
+fn fm_daemon_logic(env: &CxlFmOptions) {
+	if env.is_debug {
+		println!("{} {}: Daemonized!",
+			 CXL_FM_DAEMON_NAME, CXL_FM_DAEMON_VERSION);
+	}
+
+	loop {
+		let listener = TcpListener::bind(&env.ip_port).unwrap();
+
+		if env.is_debug {
+			println!("Ready to accept connections: {}",
+				 env.ip_port);
+		}
+
+		for stream in listener.incoming() {
+			handle_connection(&stream.unwrap(), env);
+		}
+	};
+}
+
+/*
+ * Application logic
+ */
+fn main() {
+	let stdout = File::create(CXL_FM_DAEMON_LOG_FILE_PATH).unwrap();
+	let stderr = File::create(CXL_FM_DAEMON_ERROR_MESSAGES_FILE_PATH).unwrap();
+
+	let matches = cli().get_matches();
+
+	let ip = matches.get_one::<String>(CXL_FM_DAEMON_IP_ADDRESS_OPTION).unwrap();
+	let port = matches.get_one::<String>(CXL_FM_DAEMON_PORT_OPTION).unwrap();
+	let ip_port = format!("{ip}:{port}");
+
+	let options = CxlFmOptions {
+		ip_port: String::from(ip_port),
+		is_debug: matches.get_flag(CXL_FM_DAEMON_DEBUG_OPTION) == true,
+	};
+
+	if options.is_debug {
+		println!("{} {}", CXL_FM_DAEMON_NAME, CXL_FM_DAEMON_VERSION);
+	}
+
+	let daemonize = Daemonize::new()
+			// Every method except `new` and `start`
+			// is optional, see `Daemonize` documentation
+			// for default behaviour.
+			.working_directory(CXL_FM_DAEMON_WORKING_DIRECTORY)
+			.user(CXL_FM_DAEMON_USER)
+			.group(CXL_FM_DAEMON_GROUP)	// Group name
+			.group(CXL_FM_DAEMON_GROUP_ID)	// or group id.
+			.umask(CXL_FM_DAEMON_UMASK)	// Set umask, `0o027` by default.
+			.stdout(stdout)			// Redirect stdout to log file.
+			.stderr(stderr)			// Redirect stderr to error messages file.
+			.privileged_action(|| "Executed before drop privileges");
+
+	match daemonize.start() {
+		Ok(_) => fm_daemon_logic(&options),
+		Err(e) => eprintln!("Error, {}", e),
+	}
+}
diff --git a/fm_daemon/src/mld_port.rs b/fm_daemon/src/mld_port.rs
new file mode 100644
index 0000000..ce1cb3d
--- /dev/null
+++ b/fm_daemon/src/mld_port.rs
@@ -0,0 +1,63 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_mld_port_command {
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_responce;
+	use fm_library::cxl_fm_lib::CXL_FM_MLD_PORT_TUNNEL_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_MLD_PORT_SEND_MEM_REQ_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_NO_DATA;
+
+	/*
+	 * Tunnel Management Command
+	 */
+	pub fn tunnel(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_MLD_PORT_TUNNEL_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Send CXL.io configuration request
+	 */
+	pub fn send_config(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Send CXL.io memory request
+	 */
+	pub fn send_memory_request(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_MLD_PORT_SEND_MEM_REQ_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+}
diff --git a/fm_daemon/src/multi_headed_device.rs b/fm_daemon/src/multi_headed_device.rs
new file mode 100644
index 0000000..1b63797
--- /dev/null
+++ b/fm_daemon/src/multi_headed_device.rs
@@ -0,0 +1,39 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_mh_device_command {
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_responce;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_MHD_INFO_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_NO_DATA;
+
+	/*
+	 * Get Multi Headed Device (MHD) status/info
+	 */
+	pub fn get_info(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_MHD_INFO_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+}
diff --git a/fm_daemon/src/pci2pci_bridge.rs b/fm_daemon/src/pci2pci_bridge.rs
new file mode 100644
index 0000000..8b40a44
--- /dev/null
+++ b/fm_daemon/src/pci2pci_bridge.rs
@@ -0,0 +1,63 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_ppb_command {
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_responce;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_PPB_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_PPB_BIND_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_PPB_UNBIND_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_NO_DATA;
+
+	/*
+	 * Send PCI-to-PCI Bridge (PPB) configuration request
+	 */
+	pub fn config(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_PPB_CONFIG_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Bind Virtual PCI-to-PCI Bridge (vPPB) inside a CXL switch
+	 */
+	pub fn bind(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_PPB_BIND_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Unbind Virtual PCI-to-PCI Bridge (vPPB) inside a CXL switch
+	 */
+	pub fn unbind(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_PPB_UNBIND_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+}
diff --git a/fm_daemon/src/physical_port.rs b/fm_daemon/src/physical_port.rs
new file mode 100644
index 0000000..bc8b55d
--- /dev/null
+++ b/fm_daemon/src/physical_port.rs
@@ -0,0 +1,75 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_physical_port_command {
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_responce;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_PHYSICAL_PORT_INFO_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_BIND_PHYSICAL_PORT_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_UNBIND_PHYSICAL_PORT_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_NO_DATA;
+
+	/*
+	 * Get state of physical port
+	 */
+	pub fn get_info(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_PHYSICAL_PORT_INFO_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Control physical port
+	 */
+	pub fn control(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Bind physical port to Virtual PCI-to-PCI Bridge (vPPB)
+	 */
+	pub fn bind(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_BIND_PHYSICAL_PORT_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Unbind physical port from Virtual PCI-to-PCI Bridge (vPPB)
+	 */
+	pub fn unbind(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_UNBIND_PHYSICAL_PORT_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+}
diff --git a/fm_daemon/src/switch.rs b/fm_daemon/src/switch.rs
new file mode 100644
index 0000000..acbb923
--- /dev/null
+++ b/fm_daemon/src/switch.rs
@@ -0,0 +1,63 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_switch_command {
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_responce;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_SWITCH_INFO_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_SWITCH_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_SWITCH_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_NO_DATA;
+
+	/*
+	 * Get CXL switch status/info
+	 */
+	pub fn get_info(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_SWITCH_INFO_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Get CXL switch configuration
+	 */
+	pub fn get_config(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_GET_SWITCH_CONFIG_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+
+	/*
+	 * Set CXL switch configuration
+	 */
+	pub fn set_config(stream: &TcpStream, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", CXL_FM_SET_SWITCH_CONFIG_COMMAND);
+		}
+
+		send_responce(stream, CXL_FM_NO_DATA, env);
+	}
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH 4/5] CXL FM: [fm_orchestrator] introduce CXL FM orchestrator
  2023-06-02 21:37 [RFC PATCH 0/5] CXL FM initial infrastructure Viacheslav Dubeyko
                   ` (2 preceding siblings ...)
  2023-06-02 21:37 ` [RFC PATCH 3/5] CXL FM: [fm_daemon] introduce CXL FM daemon Viacheslav Dubeyko
@ 2023-06-02 21:37 ` Viacheslav Dubeyko
  2023-06-02 21:37 ` [RFC PATCH 5/5] CXL FM: [fm_cli] introduce CXL FM CLI tool Viacheslav Dubeyko
  4 siblings, 0 replies; 6+ messages in thread
From: Viacheslav Dubeyko @ 2023-06-02 21:37 UTC (permalink / raw)
  To: linux-cxl
  Cc: a.manzanares, Jonathan.Cameron, nilesh.shah, slava, Viacheslav Dubeyko

This patch creates the initial state of CXL FM orchestrator.

Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
CC: Adam Manzanares <a.manzanares@samsung.com>
---
 Cargo.toml               |   4 +
 orchestrator/Cargo.toml  |  10 ++
 orchestrator/src/main.rs | 221 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 235 insertions(+)
 create mode 100644 orchestrator/Cargo.toml
 create mode 100644 orchestrator/src/main.rs

diff --git a/Cargo.toml b/Cargo.toml
index b8d169d..ed50825 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,10 @@ path = "fm_library/src/lib.rs"
 name = "fm_daemon"
 path = "fm_daemon/src/main.rs"
 
+[[bin]]
+name = "orchestrator"
+path = "orchestrator/src/main.rs"
+
 [dependencies]
 clap = { version = "4.0.32", features = ["derive"] }
 daemonize = "0.5.0"
diff --git a/orchestrator/Cargo.toml b/orchestrator/Cargo.toml
new file mode 100644
index 0000000..64fe7a4
--- /dev/null
+++ b/orchestrator/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "orchestrator"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+daemonize = "0.5.0"
+fm_library = { path = "../fm_library/" }
diff --git a/orchestrator/src/main.rs b/orchestrator/src/main.rs
new file mode 100644
index 0000000..f4cec1a
--- /dev/null
+++ b/orchestrator/src/main.rs
@@ -0,0 +1,221 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM orchestrator implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern crate daemonize;
+
+use std::fs::File;
+use daemonize::Daemonize;
+use clap::{Arg, Command};
+use std::{
+	io::{prelude::*, BufReader},
+	net::{TcpListener, TcpStream},
+};
+pub use fm_library::cxl_fm_lib::send_responce;
+pub use fm_library::cxl_fm_lib::CxlFmOptions;
+
+/*
+ * CXL FM orchestrator version
+ */
+const CXL_FM_ORCHESTRATOR_VERSION: &str = "0.0.1";
+
+/*
+ * CXL FM orchestrator strings
+ */
+const CXL_FM_ORCHESTRATOR_NAME: &str = "fm_orchestrator";
+const CXL_FM_ORCHESTRATOR_DESCRIPTOR: &str = "CXL Fabric Manager (FM) orchestrator";
+const CXL_FM_ORCHESTRATOR_DEBUG_OPTION: &str = "debug";
+const CXL_FM_ORCHESTRATOR_DEBUG_OPTION_SHORT: char = 'd';
+const CXL_FM_ORCHESTRATOR_IP_ADDRESS_OPTION: &str = "ip";
+const CXL_FM_ORCHESTRATOR_IP_ADDRESS_OPTION_SHORT: char = 'i';
+const CXL_FM_ORCHESTRATOR_PORT_OPTION: &str = "port";
+const CXL_FM_ORCHESTRATOR_PORT_OPTION_SHORT: char = 'p';
+
+const CXL_FM_ORCHESTRATOR_WORKING_DIRECTORY: &str = "/tmp";
+const CXL_FM_ORCHESTRATOR_LOG_FILE_PATH: &str = "/tmp/fm_orchestrator.log";
+const CXL_FM_ORCHESTRATOR_ERROR_MESSAGES_FILE_PATH: &str = "/tmp/fm_orchestrator.err";
+const CXL_FM_ORCHESTRATOR_USER: &str = "nobody";
+const CXL_FM_ORCHESTRATOR_GROUP: &str = "bin";
+const CXL_FM_ORCHESTRATOR_GROUP_ID: u32 = 2;
+const CXL_FM_ORCHESTRATOR_UMASK: u32 = 0o777;
+
+/*
+ * Command line interface definition
+ */
+fn cli() -> Command {
+	Command::new(CXL_FM_ORCHESTRATOR_NAME)
+		.about(CXL_FM_ORCHESTRATOR_DESCRIPTOR)
+		.version(CXL_FM_ORCHESTRATOR_VERSION)
+		.arg_required_else_help(true)
+		.arg(Arg::new(CXL_FM_ORCHESTRATOR_DEBUG_OPTION)
+			.short(CXL_FM_ORCHESTRATOR_DEBUG_OPTION_SHORT)
+			.long(CXL_FM_ORCHESTRATOR_DEBUG_OPTION)
+			.action(clap::ArgAction::SetTrue))
+		.arg(Arg::new(CXL_FM_ORCHESTRATOR_IP_ADDRESS_OPTION)
+			.short(CXL_FM_ORCHESTRATOR_IP_ADDRESS_OPTION_SHORT)
+			.long(CXL_FM_ORCHESTRATOR_IP_ADDRESS_OPTION)
+			.action(clap::ArgAction::Set)
+			.required(true))
+		.arg(Arg::new(CXL_FM_ORCHESTRATOR_PORT_OPTION)
+			.short(CXL_FM_ORCHESTRATOR_PORT_OPTION_SHORT)
+			.long(CXL_FM_ORCHESTRATOR_PORT_OPTION)
+			.action(clap::ArgAction::Set)
+			.required(true))
+}
+
+/*
+ * Discover available FM instances
+ */
+pub fn discover_fm(stream: &TcpStream, env: &CxlFmOptions) {
+	if env.is_debug {
+		println!("{}", fm_library::cxl_fm_lib::CXL_FM_DISCOVER_FM_COMMAND);
+	}
+
+	send_responce(stream, fm_library::cxl_fm_lib::CXL_FM_NO_DATA, env);
+}
+
+/*
+ * Start FM instance
+ */
+pub fn start_fm(stream: &TcpStream, env: &CxlFmOptions) {
+	if env.is_debug {
+		println!("{}", fm_library::cxl_fm_lib::CXL_FM_START_FM_COMMAND);
+	}
+
+	send_responce(stream, fm_library::cxl_fm_lib::CXL_FM_NO_DATA, env);
+}
+
+/*
+ * Restart FM instance
+ */
+pub fn restart_fm(stream: &TcpStream, env: &CxlFmOptions) {
+	if env.is_debug {
+		println!("{}", fm_library::cxl_fm_lib::CXL_FM_RESTART_FM_COMMAND);
+	}
+
+	send_responce(stream, fm_library::cxl_fm_lib::CXL_FM_NO_DATA, env);
+}
+
+/*
+ * Stop FM instance
+ */
+pub fn stop_fm(stream: &TcpStream, env: &CxlFmOptions) {
+	if env.is_debug {
+		println!("{}", fm_library::cxl_fm_lib::CXL_FM_STOP_FM_COMMAND);
+	}
+
+	send_responce(stream, fm_library::cxl_fm_lib::CXL_FM_NO_DATA, env);
+}
+
+/*
+ * Connection request processing logic
+ */
+fn handle_connection(stream: &TcpStream, env: &CxlFmOptions) {
+	if env.is_debug {
+		println!("Process request...");
+	}
+
+	let buf_reader = BufReader::new(stream);
+	let request_line = buf_reader.lines().next().unwrap().unwrap();
+
+	if env.is_debug {
+		println!("Request: {:#?}", request_line);
+	}
+
+	match request_line.as_str() {
+		fm_library::cxl_fm_lib::CXL_FM_DISCOVER_FM_COMMAND => {
+			discover_fm(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_START_FM_COMMAND => {
+			start_fm(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_RESTART_FM_COMMAND => {
+			restart_fm(stream, env);
+		},
+		fm_library::cxl_fm_lib::CXL_FM_STOP_FM_COMMAND => {
+			stop_fm(stream, env);
+		},
+		_ => send_responce(stream, fm_library::cxl_fm_lib::CXL_FM_UNKNOWN_COMMAND, env),
+	}
+}
+
+/*
+ * Main logic of daemon
+ */
+fn fm_daemon_logic(env: &CxlFmOptions) {
+	if env.is_debug {
+		println!("{} {}: Daemonized!",
+			 CXL_FM_ORCHESTRATOR_NAME, CXL_FM_ORCHESTRATOR_VERSION);
+	}
+
+	loop {
+		let listener = TcpListener::bind(&env.ip_port).unwrap();
+
+		if env.is_debug {
+			println!("Ready to accept connections: {}",
+				 env.ip_port);
+		}
+
+		for stream in listener.incoming() {
+			handle_connection(&stream.unwrap(), env);
+		}
+	};
+}
+
+/*
+ * Application logic
+ */
+fn main() {
+	let stdout = File::create(CXL_FM_ORCHESTRATOR_LOG_FILE_PATH).unwrap();
+	let stderr = File::create(CXL_FM_ORCHESTRATOR_ERROR_MESSAGES_FILE_PATH).unwrap();
+
+	let matches = cli().get_matches();
+
+	let ip = matches.get_one::<String>(CXL_FM_ORCHESTRATOR_IP_ADDRESS_OPTION).unwrap();
+	let port = matches.get_one::<String>(CXL_FM_ORCHESTRATOR_PORT_OPTION).unwrap();
+	let ip_port = format!("{ip}:{port}");
+
+	let options = CxlFmOptions {
+		ip_port: String::from(ip_port),
+		is_debug: matches.get_flag(CXL_FM_ORCHESTRATOR_DEBUG_OPTION) == true,
+	};
+
+	if options.is_debug {
+		println!("{} {}", CXL_FM_ORCHESTRATOR_NAME, CXL_FM_ORCHESTRATOR_VERSION);
+	}
+
+	let daemonize = Daemonize::new()
+			// Every method except `new` and `start`
+			// is optional, see `Daemonize` documentation
+			// for default behaviour.
+			.working_directory(CXL_FM_ORCHESTRATOR_WORKING_DIRECTORY)
+			.user(CXL_FM_ORCHESTRATOR_USER)
+			.group(CXL_FM_ORCHESTRATOR_GROUP)	// Group name
+			.group(CXL_FM_ORCHESTRATOR_GROUP_ID)	// or group id.
+			.umask(CXL_FM_ORCHESTRATOR_UMASK)	// Set umask, `0o027` by default.
+			.stdout(stdout)				// Redirect stdout to log file.
+			.stderr(stderr)				// Redirect stderr to error messages file.
+			.privileged_action(|| "Executed before drop privileges");
+
+	match daemonize.start() {
+		Ok(_) => fm_daemon_logic(&options),
+		Err(e) => eprintln!("Error, {}", e),
+	}
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH 5/5] CXL FM: [fm_cli] introduce CXL FM CLI tool
  2023-06-02 21:37 [RFC PATCH 0/5] CXL FM initial infrastructure Viacheslav Dubeyko
                   ` (3 preceding siblings ...)
  2023-06-02 21:37 ` [RFC PATCH 4/5] CXL FM: [fm_orchestrator] introduce CXL FM orchestrator Viacheslav Dubeyko
@ 2023-06-02 21:37 ` Viacheslav Dubeyko
  4 siblings, 0 replies; 6+ messages in thread
From: Viacheslav Dubeyko @ 2023-06-02 21:37 UTC (permalink / raw)
  To: linux-cxl
  Cc: a.manzanares, Jonathan.Cameron, nilesh.shah, slava, Viacheslav Dubeyko

This patch creates the initial state of CXL FM CLI tool.

Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
CC: Adam Manzanares <a.manzanares@samsung.com>
---
 Cargo.toml                            |   4 +
 ChangeLog                             |   6 +
 README                                | 226 ++++++++
 fm_cli/Cargo.toml                     |  10 +
 fm_cli/src/discover.rs                | 159 ++++++
 fm_cli/src/dynamic_capacity_device.rs | 225 ++++++++
 fm_cli/src/fm.rs                      | 258 +++++++++
 fm_cli/src/logical_device.rs          | 489 +++++++++++++++++
 fm_cli/src/main.rs                    | 721 ++++++++++++++++++++++++++
 fm_cli/src/mld_port.rs                | 126 +++++
 fm_cli/src/multi_headed_device.rs     |  60 +++
 fm_cli/src/pci2pci_bridge.rs          | 126 +++++
 fm_cli/src/physical_port.rs           | 159 ++++++
 fm_cli/src/switch.rs                  | 126 +++++
 14 files changed, 2695 insertions(+)
 create mode 100644 ChangeLog
 create mode 100644 README
 create mode 100644 fm_cli/Cargo.toml
 create mode 100644 fm_cli/src/discover.rs
 create mode 100644 fm_cli/src/dynamic_capacity_device.rs
 create mode 100644 fm_cli/src/fm.rs
 create mode 100644 fm_cli/src/logical_device.rs
 create mode 100644 fm_cli/src/main.rs
 create mode 100644 fm_cli/src/mld_port.rs
 create mode 100644 fm_cli/src/multi_headed_device.rs
 create mode 100644 fm_cli/src/pci2pci_bridge.rs
 create mode 100644 fm_cli/src/physical_port.rs
 create mode 100644 fm_cli/src/switch.rs

diff --git a/Cargo.toml b/Cargo.toml
index ed50825..6eac9cd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,6 +9,10 @@ edition = "2021"
 name = "fm_library"
 path = "fm_library/src/lib.rs"
 
+[[bin]]
+name = "fm_cli"
+path = "fm_cli/src/main.rs"
+
 [[bin]]
 name = "fm_daemon"
 path = "fm_daemon/src/main.rs"
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..590513a
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,6 @@
+********************************************************************************
+*                            CHANGELOG SECTION                                 *
+********************************************************************************
+
+v.0.01 [June 02, 2023]
+    (*) Create initial state [Viacheslav Dubeyko].
diff --git a/README b/README
new file mode 100644
index 0000000..b93d2af
--- /dev/null
+++ b/README
@@ -0,0 +1,226 @@
+CXL Fabric Manager (FM) infrastructure
+
+CXL Fabric Manager (FM) is the application logic responsible for
+system composition and allocation of resources. The FM can be embedded
+in the firmware of a device such as a CXL switch, reside on a host,
+or could be run on a Baseboard Management Controller (BMC).
+CXL Specification 3.0 defines Fabric Management as: "CXL devices can be
+configured statically or dynamically via a Fabric Manager (FM),
+an external logical process that queries and configures the system’s
+operational state using the FM commands defined in this specification.
+The FM is defined as the logical process that decides when
+reconfiguration is necessary and initiates the commands to perform
+configurations. It can take any form, including, but not limited to,
+software running on a host machine, embedded software running on a BMC,
+embedded firmware running on another CXL device or CXL switch,
+or a state machine running within the CXL device itself.".
+
+CXL devices are configured by FM through the Fabric Manager Application
+Programming Interface (FM API) command sets through a CCI (Component
+Command Interface). A CCI is exposed through a device’s Mailbox registers
+or through an MCTP-capable (Management Component Transport Protocol)
+interface.
+
+FM API Commands (defined by CXL Specification 3.0):
+(1) Physical switch
+    - Identify Switch Device,
+    - Get Physical Port State,
+    - Physical Port Control,
+    - Send PPB (PCI-to-PCI Bridge) CXL.io Configuration Request.
+(2) Virtual Switch
+    - Get Virtual CXL Switch Info,
+    - Bind vPPB (Virtual PCI-to-PCI Bridge),
+    - Unbind vPPB,
+    - Generate AER (Advanced Error Reporting Event).
+(3) MLD Port
+    - Tunnel Management Command,
+    - Send LD (Logical Device) or FMLD (Fabric Manager-owned Logical Device)
+      CXL.io Configuration Request,
+    - Send LD CXL.io Memory Request.
+(4) MLD Components
+    - Get LD (Logical Device) Info,
+    - Get LD Allocations,
+    - Set LD Allocations,
+    - Get QoS Control,
+    - Set QoS Control,
+    - Get QoS Status,
+    - Get QoS Allocated Bandwidth,
+    - Set QoS Allocated Bandwidth,
+    - Get QoS Bandwidth Limit,
+    - Set QoS Bandwidth Limit.
+(5) Multi- Headed Devices (Get Multi-Headed Info).
+(6) DCD (Dynamic Capacity Device) Management
+    - Get DCD Info,
+    - Get Host Dynamic Capacity Region Configuration,
+    - Set Dynamic Capacity Region Configuration,
+    - Get DCD Extent Lists,
+    - Initiate Dynamic Capacity Add,
+    - Initiate Dynamic Capacity Release.
+
+After the initial configuration is complete and a CCI on the switch is
+operational, an FM can send Management Commands to the switch.
+
+An FM may perform the following dynamic management actions on a CXL switch:
+(1) Query switch information and configuration details,
+(2) Bind or Unbind ports,
+(3) Register to receive and handle event notifications from the switch
+    (e.g., hot plug, surprise removal, and failures).
+
+A switch with MLD (Multi-Logical Device) requires an FM to perform
+the following management activities:
+(1) MLD discovery,
+(2) LD (Logical Device) binding/unbinding,
+(3) Management Command Tunneling.
+
+The FM can connect to an MLD (Multi-Logical Device) over a direct connection or
+by tunneling its management commands through the CCI of the CXL switch
+to which the device is connected. The FM can perform the following
+operations:
+(1) Memory allocation and QoS Telemetry management,
+(2) Security (e.g., LD erasure after unbinding),
+(3) Error handling.
+
+fm_cli - FM configuration tool
+Commands:
+
+Discover - discover available agents
+Subcommands:
+    - fm_cli discover fm
+         (discover FM instances)
+    - fm_cli discover cxl_devices
+         (discover CXL devices)
+    - fm_cli discover cxl_switches
+         (discover CXL switches)
+    - fm_cli discover logical_devices
+         (discover logical devices)
+
+FM - manage Fabric Manager
+Subcommands:
+    - fm_cli fm get_info
+         (get FM status/info)
+    - fm_cli fm start
+         (start FM instance)
+    - fm_cli fm restart
+         (restart FM instance)
+    - fm_cli fm stop
+         (stop FM instance)
+    - fm_cli fm get_config
+         (get FM configuration)
+    - fm_cli fm set_config
+         (set FM configuration)
+    - fm_cli fm get_events
+         (get event records)
+
+Switch - manage CXL switch
+Subcommands:
+    - fm_cli switch get_info
+         (get CXL switch info/status)
+    - fm_cli switch get_config
+         (get switch configuraiton)
+    - fm_cli switch set_config
+         (set switch configuration)
+
+Logical Device - manage logical devices
+Subcommands:
+    - fm_cli multi_headed_device info
+         (retrieves the number of heads, number of supported LDs,
+          and Head-to-LD mapping of a Multi-Headed device)
+    - fm_cli logical_device bind
+         (bind logical device)
+    - fm_cli logical_device unbind
+         (unbind logical device)
+    - fm_cli logical_device connect
+         (connect Multi Logical Device to CXL switch)
+    - fm_cli logical_device disconnect
+         (disconnect Multi Logical Device from CXL switch)
+    - fm_cli logical_device get_allocation
+         (Get LD Allocations: retrieves the memory allocations of the MLD)
+    - fm_cli logical_device set_allocation
+         (Set LD Allocations: sets the memory allocation for each LD)
+    - fm_cli logical_device get_qos_control
+         (Get QoS Control: retrieves the MLD’s QoS control parameters)
+    - fm_cli logical_device set_qos_control
+         (Set QoS Control: sets the MLD’s QoS control parameters)
+    - fm_cli logical_device get_qos_status
+         (Get QoS Status: retrieves the MLD’s QoS Status)
+    - fm_cli logical_device get_qos_allocated_bandwidth
+         (Get QoS Allocated Bandwidth: retrieves the MLD’s QoS allocated
+          bandwidth on a per-LD basis)
+    - fm_cli logical_device set_qos_allocated_bandwidth
+         (Set QoS Allocated Bandwidth: sets the MLD’s QoS allocated bandwidth
+          on a per-LD basis)
+    - fm_cli logical_device get_qos_bandwidth_limit
+         (Get QoS Bandwidth Limit: retrieves the MLD’s QoS bandwidth limit
+          on a per-LD basis)
+    - fm_cli logical_device set_qos_bandwidth_limit
+         (Set QoS Bandwidth Limit: sets the MLD’s QoS bandwidth limit
+          on a per-LD basis)
+    - fm_cli logical_device erase
+         (secure erase after unbinding)
+
+PCI-to-PCI Bridge - manage PPB (PCI-to-PCI Bridge)
+Subcommands:
+    - fm_cli ppb config
+         (Send PPB (PCI-to-PCI Bridge) CXL.io Configuration Request)
+    - fm_cli ppb bind
+         (Bind vPPB: Virtual PCI-to-PCI Bridge inside a CXL switch
+          that is host-owned)
+    - fm_cli ppb unbind
+         (Unbind vPPB: unbinds the physical port or LD from the virtual
+          hierarchy PPB)
+
+Physical Port - manage physical ports
+Subcommands:
+    - fm_cli physical_port get_info
+         (get state of physical port)
+    - fm_cli physical_port control
+         (control unbound ports and MLD ports, including issuing
+          resets and controlling sidebands)
+    - fm_cli physical_port bind
+         (bind physical port to vPPB (Virtual PCI-to-PCI Bridge))
+    - fm_cli physical_port unbind
+         (unbind physical port from vPPB (Virtual PCI-to-PCI Bridge))
+
+MLD (Multi-Logical Device) Port - manage Multi-Logical Device ports
+Subcommands:
+    - fm_cli mld_port tunnel
+         (Tunnel Management Command: tunnels the provided command to
+          LD FFFFh of the MLD on the specified port)
+    - fm_cli mld_port send_config
+         (Send LD (Logical Device) or FMLD (Fabric Manager-owned
+          Logical Device) CXL.io Configuration Request)
+    - fm_cli mld_port send_memory_request
+         (Send LD CXL.io Memory Request)
+
+DCD (Dynamic Capacity Device) - manage Dynamic Capacity Device
+Subcommands:
+    - fm_cli dcd get_info
+         (Get DCD Info: retrieves the number of supported hosts,
+          total Dynamic Capacity of the device, and supported region
+          configurations)
+    - fm_cli dcd get_capacity_config
+         (Get Host Dynamic Capacity Region Configuration: retrieves
+          the Dynamic Capacity configuration for a specified host)
+    - fm_cli dcd set_capacity_config
+         (Set Dynamic Capacity Region Configuration: sets
+          the configuration of a DC Region)
+    - fm_cli dcd get_extent_list
+         (Get DCD Extent Lists: retrieves the Dynamic Capacity Extent
+          List for a specified host)
+    - fm_cli dcd add_capacity
+         (Initiate Dynamic Capacity Add: initiates the addition of
+          Dynamic Capacity to the specified region on a host)
+    - fm_cli dcd release_capacity
+         (Initiate Dynamic Capacity Release: initiates the release of
+          Dynamic Capacity from a host)
+
+FM daemon receives requests from configuration tool and executes
+commands by means of interaction with kernel-space subsystems.
+The responsibility of FM daemon:
+    - Execute configuration tool commands
+    - Manage hot-add and hot-removal of devices
+    - Manage surprise removal of devices
+    - Receive and handle even notifications from the CXL switch
+    - Logging events
+    - Memory allocation and QoS Telemetry management
+    - Error/Failure handling
diff --git a/fm_cli/Cargo.toml b/fm_cli/Cargo.toml
new file mode 100644
index 0000000..e619a63
--- /dev/null
+++ b/fm_cli/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "fm_cli"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clap = { version = "4.0.32", features = ["derive"] }
+fm_library = { path = "../fm_library/" }
\ No newline at end of file
diff --git a/fm_cli/src/discover.rs b/fm_cli/src/discover.rs
new file mode 100644
index 0000000..a06889f
--- /dev/null
+++ b/fm_cli/src/discover.rs
@@ -0,0 +1,159 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_fm_discover_command {
+	use clap::{ArgMatches};
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_command;
+	use fm_library::cxl_fm_lib::CXL_FM_DISCOVER_FM_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DISCOVER_CXL_DEVICE_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DISCOVER_CXL_SWITCH_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DISCOVER_LD_COMMAND;
+
+	/*
+	 * Discover available FM instances
+	 */
+	pub fn discover_fms(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_DISCOVER_FM_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_DISCOVER_FM_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Discover available CXL devices
+	 */
+	pub fn discover_cxl_devices(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_DISCOVER_DEVICES_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_DISCOVER_CXL_DEVICE_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Discover available CXL switches
+	 */
+	pub fn discover_cxl_switches(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_DISCOVER_SWITCHES_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_DISCOVER_CXL_SWITCH_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Discover available logical devices
+	 */
+	pub fn discover_logical_devices(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_DISCOVER_LOGICAL_DEVICES_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_DISCOVER_LD_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+}
diff --git a/fm_cli/src/dynamic_capacity_device.rs b/fm_cli/src/dynamic_capacity_device.rs
new file mode 100644
index 0000000..db4692f
--- /dev/null
+++ b/fm_cli/src/dynamic_capacity_device.rs
@@ -0,0 +1,225 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_dcd_command {
+	use clap::{ArgMatches};
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_command;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_GET_INFO_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_GET_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_SET_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_GET_EXTENT_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_ADD_CAPACITY_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DCD_RELEASE_CAPACITY_COMMAND;
+
+	/*
+	 * Get Dynamic Capacity Device (DCD) info
+	 */
+	pub fn get_info(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_DCD_GET_INFO_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_DCD_GET_INFO_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Get dynamic capacity region configuration
+	 */
+	pub fn get_capacity_config(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_DCD_GET_CONFIG_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_DCD_GET_CONFIG_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Set dynamic capacity region configuration
+	 */
+	pub fn set_capacity_config(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_DCD_SET_CONFIG_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_DCD_SET_CONFIG_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Get Dynamic Capacity Device (DCD) extent list
+	 */
+	pub fn get_extent_list(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_DCD_GET_EXTENT_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_DCD_GET_EXTENT_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Initiate dynamic capacity add
+	 */
+	pub fn add_capacity(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_DCD_ADD_CAPACITY_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_DCD_ADD_CAPACITY_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Initiate dynamic capacity release
+	 */
+	pub fn release_capacity(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_DCD_RELEASE_CAPACITY_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_DCD_RELEASE_CAPACITY_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+}
diff --git a/fm_cli/src/fm.rs b/fm_cli/src/fm.rs
new file mode 100644
index 0000000..c6ce77d
--- /dev/null
+++ b/fm_cli/src/fm.rs
@@ -0,0 +1,258 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_fm_command {
+	use clap::{ArgMatches};
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_command;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_FM_INFO_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_START_FM_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_RESTART_FM_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_STOP_FM_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_FM_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_FM_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_FM_EVENTS_COMMAND;
+
+	/*
+	 * Get Fabric Manager (FM) status/info
+	 */
+	pub fn get_info(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_GET_INFO_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_FM_INFO_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Start Fabric Manager (FM) instance
+	 */
+	pub fn start(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_START_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_START_FM_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Restart Fabric Manager (FM) instance
+	 */
+	pub fn restart(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_RESTART_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_RESTART_FM_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Stop Fabric Manager (FM) instance
+	 */
+	pub fn stop(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_STOP_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_STOP_FM_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Get Fabric Manager (FM) configuration
+	 */
+	pub fn get_config(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_GET_CONFIG_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_FM_CONFIG_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Set Fabric Manager (FM) configuration
+	 */
+	pub fn set_config(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_SET_CONFIG_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_SET_FM_CONFIG_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Get Fabric Manager (FM) event records
+	 */
+	pub fn get_events(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_GET_EVENTS_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_FM_EVENTS_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+}
diff --git a/fm_cli/src/logical_device.rs b/fm_cli/src/logical_device.rs
new file mode 100644
index 0000000..9ef7ddb
--- /dev/null
+++ b/fm_cli/src/logical_device.rs
@@ -0,0 +1,489 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_logical_device_command {
+	use clap::{ArgMatches};
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_command;
+	use fm_library::cxl_fm_lib::CXL_FM_BIND_LD_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_UNBIND_LD_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_CONNECT_MLD_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_DISCONNECT_MLD_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_LD_ALLOCATION_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_LD_ALLOCATION_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_QOS_CONTROL_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_QOS_CONTROL_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_QOS_STATUS_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_QOS_BANDWIDTH_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_QOS_BANDWIDTH_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_QOS_BANDWIDTH_LIMIT_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_QOS_BANDWIDTH_LIMIT_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_LD_ERASE;
+
+	/*
+	 * Bind Logical Device (LD)
+	 */
+	pub fn bind(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_BIND_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_BIND_LD_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Unbind Logical Device (LD)
+	 */
+	pub fn unbind(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_UNBIND_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_UNBIND_LD_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Connect Multi Logical Device (MLD) to CXL switch
+	 */
+	pub fn connect(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_CONNECT_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_CONNECT_MLD_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Disconnect Multi Logical Device (MLD) from CXL switch
+	 */
+	pub fn disconnect(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_DISCONNECT_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_DISCONNECT_MLD_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Get Logical Device (LD) allocations
+	 */
+	pub fn get_allocation(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_GET_ALLOCATION_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_LD_ALLOCATION_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Set Logical Device (LD) allocations
+	 */
+	pub fn set_allocation(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_SET_ALLOCATION_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_SET_LD_ALLOCATION_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Get QoS control
+	 */
+	pub fn get_qos_control(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_GET_QOS_CONTROL_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_QOS_CONTROL_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Set QoS control
+	 */
+	pub fn set_qos_control(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_SET_QOS_CONTROL_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_SET_QOS_CONTROL_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Get QoS status
+	 */
+	pub fn get_qos_status(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_GET_QOS_STATUS_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_QOS_STATUS_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Get QoS allocated bandwidth
+	 */
+	pub fn get_qos_bandwidth(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_QOS_BANDWIDTH_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Set QoS allocated bandwidth
+	 */
+	pub fn set_qos_bandwidth(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_SET_QOS_BANDWIDTH_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Get QoS bandwidth limit
+	 */
+	pub fn get_qos_bandwidth_limit(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_LIMIT_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_QOS_BANDWIDTH_LIMIT_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Set QoS bandwidth limit
+	 */
+	pub fn set_qos_bandwidth_limit(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_LIMIT_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_SET_QOS_BANDWIDTH_LIMIT_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Secure erase after unbinding
+	 */
+	pub fn erase(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_LOGICAL_DEVICE_ERASE_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_LD_ERASE,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+}
diff --git a/fm_cli/src/main.rs b/fm_cli/src/main.rs
new file mode 100644
index 0000000..b580a84
--- /dev/null
+++ b/fm_cli/src/main.rs
@@ -0,0 +1,721 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+mod discover;
+mod fm;
+mod switch;
+mod multi_headed_device;
+mod logical_device;
+mod pci2pci_bridge;
+mod physical_port;
+mod mld_port;
+mod dynamic_capacity_device;
+
+use clap::{Arg, Command};
+pub use crate::discover::cxl_fm_discover_command;
+pub use crate::fm::cxl_fm_command;
+pub use crate::switch::cxl_switch_command;
+pub use crate::multi_headed_device::cxl_mh_device_command;
+pub use crate::logical_device::cxl_logical_device_command;
+pub use crate::pci2pci_bridge::cxl_ppb_command;
+pub use crate::physical_port::cxl_physical_port_command;
+pub use crate::mld_port::cxl_mld_port_command;
+pub use crate::dynamic_capacity_device::cxl_dcd_command;
+pub use fm_library::cxl_fm_lib::CxlFmOptions;
+
+/*
+ * CXL FM configuration tool version
+ */
+const CXL_FM_TOOL_VERSION: &str = "0.0.1";
+
+/*
+ * CXL FM configuration tool strings
+ */
+const CXL_FM_TOOL_NAME: &str = "fm_cli";
+const CXL_FM_TOOL_DESCRIPTOR: &str = "CXL Fabric Manager (FM) CLI";
+const CXL_FM_TOOL_DEBUG_OPTION: &str = "debug";
+const CXL_FM_TOOL_DEBUG_OPTION_SHORT: char = 'd';
+const CXL_FM_TOOL_IP_ADDRESS_OPTION: &str = "ip";
+const CXL_FM_TOOL_IP_ADDRESS_OPTION_SHORT: char = 'i';
+const CXL_FM_TOOL_PORT_OPTION: &str = "port";
+const CXL_FM_TOOL_PORT_OPTION_SHORT: char = 'p';
+
+/*
+ * Discover command strings
+ */
+const CXL_FM_DISCOVER_COMMAND: &str = "discover";
+const CXL_FM_DISCOVER_COMMAND_DESCRIPTOR: &str = "Discover available CXL agents";
+const CXL_FM_DISCOVER_FM_COMMAND: &str = "fm";
+const CXL_FM_DISCOVER_FM_COMMAND_DESCRIPTOR: &str = "Discover FM instances";
+const CXL_FM_DISCOVER_DEVICES_COMMAND: &str = "cxl_devices";
+const CXL_FM_DISCOVER_DEVICES_COMMAND_DESCRIPTOR: &str = "Discover CXL devices";
+const CXL_FM_DISCOVER_SWITCHES_COMMAND: &str = "cxl_switch";
+const CXL_FM_DISCOVER_SWITCHES_COMMAND_DESCRIPTOR: &str = "Discover CXL switches";
+const CXL_FM_DISCOVER_LOGICAL_DEVICES_COMMAND: &str = "logical_devices";
+const CXL_FM_DISCOVER_LOGICAL_DEVICES_COMMAND_DESCRIPTOR: &str = "Discover logical devices";
+
+/*
+ * FM command strings
+ */
+const CXL_FM_COMMAND: &str = "fm";
+const CXL_FM_COMMAND_DESCRIPTOR: &str = "Manage Fabric Manager (FM)";
+const CXL_FM_GET_INFO_COMMAND: &str = "get_info";
+const CXL_FM_GET_INFO_COMMAND_DESCRIPTOR: &str = "Get Fabric Manager (FM) status/info";
+const CXL_FM_START_COMMAND: &str = "start";
+const CXL_FM_START_COMMAND_DESCRIPTOR: &str = "Start Fabric Manager (FM) instance";
+const CXL_FM_RESTART_COMMAND: &str = "restart";
+const CXL_FM_RESTART_COMMAND_DESCRIPTOR: &str = "Restart Fabric Manager (FM) instance";
+const CXL_FM_STOP_COMMAND: &str = "stop";
+const CXL_FM_STOP_COMMAND_DESCRIPTOR: &str = "Stop Fabric Manager (FM) instance";
+const CXL_FM_GET_CONFIG_COMMAND: &str = "get_config";
+const CXL_FM_GET_CONFIG_COMMAND_DESCRIPTOR: &str = "Get Fabric Manager (FM) configuration";
+const CXL_FM_SET_CONFIG_COMMAND: &str = "set_config";
+const CXL_FM_SET_CONFIG_COMMAND_DESCRIPTOR: &str = "Set Fabric Manager (FM) configuration";
+const CXL_FM_GET_EVENTS_COMMAND: &str = "get_events";
+const CXL_FM_GET_EVENTS_COMMAND_DESCRIPTOR: &str = "Get Fabric Manager (FM) event records";
+
+/*
+ * Switch command strings
+ */
+const CXL_FM_SWITCH_COMMAND: &str = "switch";
+const CXL_FM_SWITCH_COMMAND_DESCRIPTOR: &str = "Manage CXL switch";
+const CXL_FM_SWITCH_GET_INFO_COMMAND: &str = "get_info";
+const CXL_FM_SWITCH_GET_INFO_COMMAND_DESCRIPTOR: &str = "Get CXL switch status/info";
+const CXL_FM_SWITCH_GET_CONFIG_COMMAND: &str = "get_config";
+const CXL_FM_SWITCH_GET_CONFIG_COMMAND_DESCRIPTOR: &str = "Get CXL switch configuration";
+const CXL_FM_SWITCH_SET_CONFIG_COMMAND: &str = "set_config";
+const CXL_FM_SWITCH_SET_CONFIG_COMMAND_DESCRIPTOR: &str = "Set CXL switch configuration";
+
+/*
+ * Multi Headed Device (MHD) command strings
+ */
+const CXL_FM_MH_DEVICE_COMMAND: &str = "mh_device";
+const CXL_FM_MH_DEVICE_COMMAND_DESCRIPTOR: &str = "Manage Multi Headed Device (MHD)";
+const CXL_FM_MH_DEVICE_GET_INFO_COMMAND: &str = "get_info";
+const CXL_FM_MH_DEVICE_GET_INFO_COMMAND_DESCRIPTOR: &str = "Get Multi Headed Device (MHD) status/info";
+
+/*
+ * Logical Device (LD) command strings
+ */
+const CXL_FM_LOGICAL_DEVICE_COMMAND: &str = "logical_device";
+const CXL_FM_LOGICAL_DEVICE_COMMAND_DESCRIPTOR: &str = "Manage Logical Device (LD)";
+const CXL_FM_LOGICAL_DEVICE_BIND_COMMAND: &str = "bind";
+const CXL_FM_LOGICAL_DEVICE_BIND_COMMAND_DESCRIPTOR: &str = "Bind Logical Device (LD)";
+const CXL_FM_LOGICAL_DEVICE_UNBIND_COMMAND: &str = "unbind";
+const CXL_FM_LOGICAL_DEVICE_UNBIND_COMMAND_DESCRIPTOR: &str = "Unbind Logical Device (LD)";
+const CXL_FM_LOGICAL_DEVICE_CONNECT_COMMAND: &str = "connect";
+const CXL_FM_LOGICAL_DEVICE_CONNECT_COMMAND_DESCRIPTOR: &str = "Connect Multi Logical Device (MLD) to CXL switch";
+const CXL_FM_LOGICAL_DEVICE_DISCONNECT_COMMAND: &str = "disconnect";
+const CXL_FM_LOGICAL_DEVICE_DISCONNECT_COMMAND_DESCRIPTOR: &str = "Disconnect Multi Logical Device (MLD) from CXL switch";
+const CXL_FM_LOGICAL_DEVICE_GET_ALLOCATION_COMMAND: &str = "get_allocation";
+const CXL_FM_LOGICAL_DEVICE_GET_ALLOCATION_COMMAND_DESCRIPTOR: &str = "Get Logical Device (LD) allocations";
+const CXL_FM_LOGICAL_DEVICE_SET_ALLOCATION_COMMAND: &str = "set_allocation";
+const CXL_FM_LOGICAL_DEVICE_SET_ALLOCATION_COMMAND_DESCRIPTOR: &str = "Set Logical Device (LD) allocations";
+const CXL_FM_LOGICAL_DEVICE_GET_QOS_CONTROL_COMMAND: &str = "get_qos_control";
+const CXL_FM_LOGICAL_DEVICE_GET_QOS_CONTROL_COMMAND_DESCRIPTOR: &str = "Get QoS control";
+const CXL_FM_LOGICAL_DEVICE_SET_QOS_CONTROL_COMMAND: &str = "set_qos_control";
+const CXL_FM_LOGICAL_DEVICE_SET_QOS_CONTROL_COMMAND_DESCRIPTOR: &str = "Set QoS control";
+const CXL_FM_LOGICAL_DEVICE_GET_QOS_STATUS_COMMAND: &str = "get_qos_status";
+const CXL_FM_LOGICAL_DEVICE_GET_QOS_STATUS_COMMAND_DESCRIPTOR: &str = "Get QoS status";
+const CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_COMMAND: &str = "get_qos_allocated_bandwidth";
+const CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_COMMAND_DESCRIPTOR: &str = "Get QoS allocated bandwidth";
+const CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_COMMAND: &str = "set_qos_allocated_bandwidth";
+const CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_COMMAND_DESCRIPTOR: &str = "Set QoS allocated bandwidth";
+const CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_LIMIT_COMMAND: &str = "get_qos_bandwidth_limit";
+const CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_LIMIT_COMMAND_DESCRIPTOR: &str = "Get QoS bandwidth limit";
+const CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_LIMIT_COMMAND: &str = "set_qos_bandwidth_limit";
+const CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_LIMIT_COMMAND_DESCRIPTOR: &str = "Set QoS bandwidth limit";
+const CXL_FM_LOGICAL_DEVICE_ERASE_COMMAND: &str = "erase";
+const CXL_FM_LOGICAL_DEVICE_ERASE_COMMAND_DESCRIPTOR: &str = "Secure erase after unbinding";
+
+/*
+ * PCI-to-PCI Bridge (PPB) command strings
+ */
+const CXL_FM_PPB_COMMAND: &str = "ppb";
+const CXL_FM_PPB_COMMAND_DESCRIPTOR: &str = "Manage PCI-to-PCI Bridge (PPB)";
+const CXL_FM_PPB_CONFIG_COMMAND: &str = "config";
+const CXL_FM_PPB_CONFIG_COMMAND_DESCRIPTOR: &str = "Send PCI-to-PCI Bridge (PPB) configuration request";
+const CXL_FM_PPB_BIND_COMMAND: &str = "bind";
+const CXL_FM_PPB_BIND_COMMAND_DESCRIPTOR: &str = "Bind Virtual PCI-to-PCI Bridge (vPPB) inside a CXL switch";
+const CXL_FM_PPB_UNBIND_COMMAND: &str = "unbind";
+const CXL_FM_PPB_UNBIND_COMMAND_DESCRIPTOR: &str = "Unbind Virtual PCI-to-PCI Bridge (vPPB) inside a CXL switch";
+
+/*
+ * Physical port command strings
+ */
+const CXL_FM_PHYSICAL_PORT_COMMAND: &str = "physical_port";
+const CXL_FM_PHYSICAL_PORT_COMMAND_DESCRIPTOR: &str = "Manage physical ports";
+const CXL_FM_PHYSICAL_PORT_GET_INFO_COMMAND: &str = "get_info";
+const CXL_FM_PHYSICAL_PORT_GET_INFO_COMMAND_DESCRIPTOR: &str = "Get state of physical port";
+const CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND: &str = "control";
+const CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND_DESCRIPTOR: &str = "Control physical port";
+const CXL_FM_PHYSICAL_PORT_BIND_COMMAND: &str = "bind";
+const CXL_FM_PHYSICAL_PORT_BIND_COMMAND_DESCRIPTOR: &str = "Bind physical port to Virtual PCI-to-PCI Bridge (vPPB)";
+const CXL_FM_PHYSICAL_PORT_UNBIND_COMMAND: &str = "unbind";
+const CXL_FM_PHYSICAL_PORT_UNBIND_COMMAND_DESCRIPTOR: &str = "Unbind physical port from Virtual PCI-to-PCI Bridge (vPPB)";
+
+/*
+ * Multi-Logical Device (MLD) ports command strings
+ */
+const CXL_FM_MLD_PORT_COMMAND: &str = "mld_port";
+const CXL_FM_MLD_PORT_COMMAND_DESCRIPTOR: &str = "Manage Multi-Logical Device (MLD) ports";
+const CXL_FM_MLD_PORT_TUNNEL_COMMAND: &str = "tunnel";
+const CXL_FM_MLD_PORT_TUNNEL_COMMAND_DESCRIPTOR: &str = "Tunnel Management Command";
+const CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND: &str = "send_config";
+const CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND_DESCRIPTOR: &str = "Send CXL.io configuration request";
+const CXL_FM_MLD_PORT_SEND_MEM_REQUEST_COMMAND: &str = "send_memory_request";
+const CXL_FM_MLD_PORT_SEND_MEM_REQUEST_COMMAND_DESCRIPTOR: &str = "Send CXL.io memory request";
+
+/*
+ * Dynamic Capacity Device (DCD) command strings
+ */
+const CXL_FM_DCD_COMMAND: &str = "dcd";
+const CXL_FM_DCD_COMMAND_DESCRIPTOR: &str = "Manage Dynamic Capacity Device (DCD)";
+const CXL_FM_DCD_GET_INFO_COMMAND: &str = "get_info";
+const CXL_FM_DCD_GET_INFO_COMMAND_DESCRIPTOR: &str = "Get Dynamic Capacity Device (DCD) info";
+const CXL_FM_DCD_GET_CONFIG_COMMAND: &str = "get_capacity_config";
+const CXL_FM_DCD_GET_CONFIG_COMMAND_DESCRIPTOR: &str = "Get dynamic capacity region configuration";
+const CXL_FM_DCD_SET_CONFIG_COMMAND: &str = "set_capacity_config";
+const CXL_FM_DCD_SET_CONFIG_COMMAND_DESCRIPTOR: &str = "Set dynamic capacity region configuration";
+const CXL_FM_DCD_GET_EXTENT_COMMAND: &str = "get_extent_list";
+const CXL_FM_DCD_GET_EXTENT_COMMAND_DESCRIPTOR: &str = "Get Dynamic Capacity Device (DCD) extent list";
+const CXL_FM_DCD_ADD_CAPACITY_COMMAND: &str = "add_capacity";
+const CXL_FM_DCD_ADD_CAPACITY_COMMAND_DESCRIPTOR: &str = "Initiate dynamic capacity add";
+const CXL_FM_DCD_RELEASE_CAPACITY_COMMAND: &str = "release_capacity";
+const CXL_FM_DCD_RELEASE_CAPACITY_COMMAND_DESCRIPTOR: &str = "Initiate dynamic capacity release";
+
+/*
+ * Command line interface definition
+ */
+fn cli() -> Command {
+	Command::new(CXL_FM_TOOL_NAME)
+		.about(CXL_FM_TOOL_DESCRIPTOR)
+		.version(CXL_FM_TOOL_VERSION)
+		.subcommand_required(true)
+		.arg_required_else_help(true)
+		.allow_external_subcommands(true)
+		.arg(Arg::new(CXL_FM_TOOL_DEBUG_OPTION)
+			.short(CXL_FM_TOOL_DEBUG_OPTION_SHORT)
+			.long(CXL_FM_TOOL_DEBUG_OPTION)
+			.action(clap::ArgAction::SetTrue))
+		.arg(Arg::new(CXL_FM_TOOL_IP_ADDRESS_OPTION)
+			.short(CXL_FM_TOOL_IP_ADDRESS_OPTION_SHORT)
+			.long(CXL_FM_TOOL_IP_ADDRESS_OPTION)
+			.action(clap::ArgAction::Set)
+			.required(true))
+		.arg(Arg::new(CXL_FM_TOOL_PORT_OPTION)
+			.short(CXL_FM_TOOL_PORT_OPTION_SHORT)
+			.long(CXL_FM_TOOL_PORT_OPTION)
+			.action(clap::ArgAction::Set)
+			.required(true))
+		.subcommand(
+			Command::new(CXL_FM_DISCOVER_COMMAND)
+				.about(CXL_FM_DISCOVER_COMMAND_DESCRIPTOR)
+				.subcommand_required(true)
+				.arg_required_else_help(true)
+				.allow_external_subcommands(true)
+				.subcommand(
+					Command::new(CXL_FM_DISCOVER_FM_COMMAND)
+						.about(CXL_FM_DISCOVER_FM_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_DISCOVER_DEVICES_COMMAND)
+						.about(CXL_FM_DISCOVER_DEVICES_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_DISCOVER_SWITCHES_COMMAND)
+						.about(CXL_FM_DISCOVER_SWITCHES_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_DISCOVER_LOGICAL_DEVICES_COMMAND)
+						.about(CXL_FM_DISCOVER_LOGICAL_DEVICES_COMMAND_DESCRIPTOR)
+				)
+		)
+		.subcommand(
+			Command::new(CXL_FM_COMMAND)
+				.about(CXL_FM_COMMAND_DESCRIPTOR)
+				.subcommand_required(true)
+				.arg_required_else_help(true)
+				.allow_external_subcommands(true)
+				.subcommand(
+					Command::new(CXL_FM_GET_INFO_COMMAND)
+						.about(CXL_FM_GET_INFO_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_START_COMMAND)
+						.about(CXL_FM_START_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_RESTART_COMMAND)
+						.about(CXL_FM_RESTART_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_STOP_COMMAND)
+						.about(CXL_FM_STOP_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_GET_CONFIG_COMMAND)
+						.about(CXL_FM_GET_CONFIG_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_SET_CONFIG_COMMAND)
+						.about(CXL_FM_SET_CONFIG_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_GET_EVENTS_COMMAND)
+						.about(CXL_FM_GET_EVENTS_COMMAND_DESCRIPTOR)
+				)
+		)
+		.subcommand(
+			Command::new(CXL_FM_SWITCH_COMMAND)
+				.about(CXL_FM_SWITCH_COMMAND_DESCRIPTOR)
+				.subcommand_required(true)
+				.arg_required_else_help(true)
+				.allow_external_subcommands(true)
+				.subcommand(
+					Command::new(CXL_FM_SWITCH_GET_INFO_COMMAND)
+						.about(CXL_FM_SWITCH_GET_INFO_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_SWITCH_GET_CONFIG_COMMAND)
+						.about(CXL_FM_SWITCH_GET_CONFIG_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_SWITCH_SET_CONFIG_COMMAND)
+						.about(CXL_FM_SWITCH_SET_CONFIG_COMMAND_DESCRIPTOR)
+				)
+		)
+		.subcommand(
+			Command::new(CXL_FM_MH_DEVICE_COMMAND)
+				.about(CXL_FM_MH_DEVICE_COMMAND_DESCRIPTOR)
+				.subcommand_required(true)
+				.arg_required_else_help(true)
+				.allow_external_subcommands(true)
+				.subcommand(
+					Command::new(CXL_FM_MH_DEVICE_GET_INFO_COMMAND)
+						.about(CXL_FM_MH_DEVICE_GET_INFO_COMMAND_DESCRIPTOR)
+				)
+		)
+		.subcommand(
+			Command::new(CXL_FM_LOGICAL_DEVICE_COMMAND)
+				.about(CXL_FM_LOGICAL_DEVICE_COMMAND_DESCRIPTOR)
+				.subcommand_required(true)
+				.arg_required_else_help(true)
+				.allow_external_subcommands(true)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_BIND_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_BIND_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_UNBIND_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_UNBIND_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_CONNECT_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_CONNECT_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_DISCONNECT_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_DISCONNECT_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_GET_ALLOCATION_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_GET_ALLOCATION_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_SET_ALLOCATION_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_SET_ALLOCATION_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_GET_QOS_CONTROL_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_GET_QOS_CONTROL_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_SET_QOS_CONTROL_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_SET_QOS_CONTROL_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_GET_QOS_STATUS_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_GET_QOS_STATUS_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_LIMIT_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_LIMIT_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_LIMIT_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_LIMIT_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_LOGICAL_DEVICE_ERASE_COMMAND)
+						.about(CXL_FM_LOGICAL_DEVICE_ERASE_COMMAND_DESCRIPTOR)
+				)
+		)
+		.subcommand(
+			Command::new(CXL_FM_PPB_COMMAND)
+				.about(CXL_FM_PPB_COMMAND_DESCRIPTOR)
+				.subcommand_required(true)
+				.arg_required_else_help(true)
+				.allow_external_subcommands(true)
+				.subcommand(
+					Command::new(CXL_FM_PPB_CONFIG_COMMAND)
+						.about(CXL_FM_PPB_CONFIG_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_PPB_BIND_COMMAND)
+						.about(CXL_FM_PPB_BIND_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_PPB_UNBIND_COMMAND)
+						.about(CXL_FM_PPB_UNBIND_COMMAND_DESCRIPTOR)
+				)
+		)
+		.subcommand(
+			Command::new(CXL_FM_PHYSICAL_PORT_COMMAND)
+				.about(CXL_FM_PHYSICAL_PORT_COMMAND_DESCRIPTOR)
+				.subcommand_required(true)
+				.arg_required_else_help(true)
+				.allow_external_subcommands(true)
+				.subcommand(
+					Command::new(CXL_FM_PHYSICAL_PORT_GET_INFO_COMMAND)
+						.about(CXL_FM_PHYSICAL_PORT_GET_INFO_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND)
+						.about(CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_PHYSICAL_PORT_BIND_COMMAND)
+						.about(CXL_FM_PHYSICAL_PORT_BIND_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_PHYSICAL_PORT_UNBIND_COMMAND)
+						.about(CXL_FM_PHYSICAL_PORT_UNBIND_COMMAND_DESCRIPTOR)
+				)
+		)
+		.subcommand(
+			Command::new(CXL_FM_MLD_PORT_COMMAND)
+				.about(CXL_FM_MLD_PORT_COMMAND_DESCRIPTOR)
+				.subcommand_required(true)
+				.arg_required_else_help(true)
+				.allow_external_subcommands(true)
+				.subcommand(
+					Command::new(CXL_FM_MLD_PORT_TUNNEL_COMMAND)
+						.about(CXL_FM_MLD_PORT_TUNNEL_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND)
+						.about(CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_MLD_PORT_SEND_MEM_REQUEST_COMMAND)
+						.about(CXL_FM_MLD_PORT_SEND_MEM_REQUEST_COMMAND_DESCRIPTOR)
+				)
+		)
+		.subcommand(
+			Command::new(CXL_FM_DCD_COMMAND)
+				.about(CXL_FM_DCD_COMMAND_DESCRIPTOR)
+				.subcommand_required(true)
+				.arg_required_else_help(true)
+				.allow_external_subcommands(true)
+				.subcommand(
+					Command::new(CXL_FM_DCD_GET_INFO_COMMAND)
+						.about(CXL_FM_DCD_GET_INFO_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_DCD_GET_CONFIG_COMMAND)
+						.about(CXL_FM_DCD_GET_CONFIG_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_DCD_SET_CONFIG_COMMAND)
+						.about(CXL_FM_DCD_SET_CONFIG_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_DCD_GET_EXTENT_COMMAND)
+						.about(CXL_FM_DCD_GET_EXTENT_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_DCD_ADD_CAPACITY_COMMAND)
+						.about(CXL_FM_DCD_ADD_CAPACITY_COMMAND_DESCRIPTOR)
+				)
+				.subcommand(
+					Command::new(CXL_FM_DCD_RELEASE_CAPACITY_COMMAND)
+						.about(CXL_FM_DCD_RELEASE_CAPACITY_COMMAND_DESCRIPTOR)
+				)
+		)
+}
+
+/*
+ * Application logic
+ */
+fn main() {
+	let matches = cli().get_matches();
+
+	let ip = matches.get_one::<String>(CXL_FM_TOOL_IP_ADDRESS_OPTION).unwrap();
+	let port = matches.get_one::<String>(CXL_FM_TOOL_PORT_OPTION).unwrap();
+	let ip_port = format!("{ip}:{port}");
+
+	let options = CxlFmOptions {
+		ip_port: String::from(ip_port),
+		is_debug: matches.get_flag(CXL_FM_TOOL_DEBUG_OPTION) == true,
+	};
+
+	if options.is_debug {
+		println!("{} {}", CXL_FM_TOOL_NAME, CXL_FM_TOOL_VERSION);
+	}
+
+	match matches.subcommand() {
+		Some((CXL_FM_DISCOVER_COMMAND, discover)) => {
+			match discover.subcommand() {
+				Some((CXL_FM_DISCOVER_FM_COMMAND, fm)) => {
+					cxl_fm_discover_command::discover_fms(&fm,
+									      &options);
+				},
+				Some((CXL_FM_DISCOVER_DEVICES_COMMAND, devices)) => {
+					cxl_fm_discover_command::discover_cxl_devices(&devices,
+										      &options);
+				},
+				Some((CXL_FM_DISCOVER_SWITCHES_COMMAND, switch)) => {
+					cxl_fm_discover_command::discover_cxl_switches(&switch,
+											&options);
+				},
+				Some((CXL_FM_DISCOVER_LOGICAL_DEVICES_COMMAND, logical_devices)) => {
+					cxl_fm_discover_command::discover_logical_devices(&logical_devices,
+											  &options);
+				},
+				_ => unreachable!(),
+			}
+		},
+		Some((CXL_FM_COMMAND, fm)) => {
+			match fm.subcommand() {
+				Some((CXL_FM_GET_INFO_COMMAND, get_info)) => {
+					cxl_fm_command::get_info(&get_info,
+								 &options);
+				},
+				Some((CXL_FM_START_COMMAND, start)) => {
+					cxl_fm_command::start(&start,
+							      &options);
+				},
+				Some((CXL_FM_RESTART_COMMAND, restart)) => {
+					cxl_fm_command::restart(&restart,
+								&options);
+				},
+				Some((CXL_FM_STOP_COMMAND, stop)) => {
+					cxl_fm_command::stop(&stop,
+							     &options);
+				},
+				Some((CXL_FM_GET_CONFIG_COMMAND, get_config)) => {
+					cxl_fm_command::get_config(&get_config,
+								   &options);
+				},
+				Some((CXL_FM_SET_CONFIG_COMMAND, set_config)) => {
+					cxl_fm_command::set_config(&set_config,
+								   &options);
+				},
+				Some((CXL_FM_GET_EVENTS_COMMAND, get_events)) => {
+					cxl_fm_command::get_events(&get_events,
+								   &options);
+				},
+				_ => unreachable!(),
+			}
+		},
+		Some((CXL_FM_SWITCH_COMMAND, switch)) => {
+			match switch.subcommand() {
+				Some((CXL_FM_SWITCH_GET_INFO_COMMAND, get_info)) => {
+					cxl_switch_command::get_info(&get_info,
+								     &options);
+				},
+				Some((CXL_FM_SWITCH_GET_CONFIG_COMMAND, get_config)) => {
+					cxl_switch_command::get_config(&get_config,
+								       &options);
+				},
+				Some((CXL_FM_SWITCH_SET_CONFIG_COMMAND, set_config)) => {
+					cxl_switch_command::set_config(&set_config,
+									&options);
+				},
+				_ => unreachable!(),
+			}
+		},
+		Some((CXL_FM_MH_DEVICE_COMMAND, mh_device)) => {
+			match mh_device.subcommand() {
+				Some((CXL_FM_MH_DEVICE_GET_INFO_COMMAND, get_info)) => {
+					cxl_mh_device_command::get_info(&get_info,
+									&options);
+				},
+				_ => unreachable!(),
+			}
+		},
+		Some((CXL_FM_LOGICAL_DEVICE_COMMAND, logical_device)) => {
+			match logical_device.subcommand() {
+				Some((CXL_FM_LOGICAL_DEVICE_BIND_COMMAND, bind)) => {
+					cxl_logical_device_command::bind(&bind,
+									 &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_UNBIND_COMMAND, unbind)) => {
+					cxl_logical_device_command::unbind(&unbind,
+									   &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_CONNECT_COMMAND, connect)) => {
+					cxl_logical_device_command::connect(&connect,
+									    &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_DISCONNECT_COMMAND, disconnect)) => {
+					cxl_logical_device_command::disconnect(&disconnect,
+										&options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_GET_ALLOCATION_COMMAND, get_allocation)) => {
+					cxl_logical_device_command::get_allocation(&get_allocation,
+										   &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_SET_ALLOCATION_COMMAND, set_allocation)) => {
+					cxl_logical_device_command::set_allocation(&set_allocation,
+										   &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_GET_QOS_CONTROL_COMMAND, get_qos_control)) => {
+					cxl_logical_device_command::get_qos_control(&get_qos_control,
+										    &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_SET_QOS_CONTROL_COMMAND, set_qos_control)) => {
+					cxl_logical_device_command::set_qos_control(&set_qos_control,
+										    &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_GET_QOS_STATUS_COMMAND, get_qos_status)) => {
+					cxl_logical_device_command::get_qos_status(&get_qos_status,
+										   &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_COMMAND, get_qos_bandwidth)) => {
+					cxl_logical_device_command::get_qos_bandwidth(&get_qos_bandwidth,
+										      &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_COMMAND, set_qos_bandwidth)) => {
+					cxl_logical_device_command::set_qos_bandwidth(&set_qos_bandwidth,
+										      &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_GET_QOS_BANDWIDTH_LIMIT_COMMAND, get_qos_bandwidth_limit)) => {
+					cxl_logical_device_command::get_qos_bandwidth_limit(&get_qos_bandwidth_limit,
+											    &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_SET_QOS_BANDWIDTH_LIMIT_COMMAND, set_qos_bandwidth_limit)) => {
+					cxl_logical_device_command::set_qos_bandwidth_limit(&set_qos_bandwidth_limit,
+											    &options);
+				},
+				Some((CXL_FM_LOGICAL_DEVICE_ERASE_COMMAND, erase)) => {
+					cxl_logical_device_command::erase(&erase,
+									  &options);
+				},
+				_ => unreachable!(),
+			}
+		},
+		Some((CXL_FM_PPB_COMMAND, ppb)) => {
+			match ppb.subcommand() {
+				Some((CXL_FM_PPB_CONFIG_COMMAND, config)) => {
+					cxl_ppb_command::config(&config,
+								&options);
+				},
+				Some((CXL_FM_PPB_BIND_COMMAND, bind)) => {
+					cxl_ppb_command::bind(&bind,
+							      &options);
+				},
+				Some((CXL_FM_PPB_UNBIND_COMMAND, unbind)) => {
+					cxl_ppb_command::unbind(&unbind,
+								&options);
+				},
+				_ => unreachable!(),
+			}
+		},
+		Some((CXL_FM_PHYSICAL_PORT_COMMAND, physical_port)) => {
+			match physical_port.subcommand() {
+				Some((CXL_FM_PHYSICAL_PORT_GET_INFO_COMMAND, get_info)) => {
+					cxl_physical_port_command::get_info(&get_info,
+									    &options);
+				},
+				Some((CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND, control)) => {
+					cxl_physical_port_command::control(&control,
+									   &options);
+				},
+				Some((CXL_FM_PHYSICAL_PORT_BIND_COMMAND, bind)) => {
+					cxl_physical_port_command::bind(&bind,
+									&options);
+				},
+				Some((CXL_FM_PHYSICAL_PORT_UNBIND_COMMAND, unbind)) => {
+					cxl_physical_port_command::unbind(&unbind,
+									  &options);
+				},
+				_ => unreachable!(),
+			}
+		},
+		Some((CXL_FM_MLD_PORT_COMMAND, mld_port)) => {
+			match mld_port.subcommand() {
+				Some((CXL_FM_MLD_PORT_TUNNEL_COMMAND, tunnel)) => {
+					cxl_mld_port_command::tunnel(&tunnel,
+								     &options);
+				},
+				Some((CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND, send_config)) => {
+					cxl_mld_port_command::send_config(&send_config,
+									  &options);
+				},
+				Some((CXL_FM_MLD_PORT_SEND_MEM_REQUEST_COMMAND, send_memory_request)) => {
+					cxl_mld_port_command::send_memory_request(&send_memory_request,
+										  &options);
+				},
+				_ => unreachable!(),
+			}
+		},
+		Some((CXL_FM_DCD_COMMAND, dcd)) => {
+			match dcd.subcommand() {
+				Some((CXL_FM_DCD_GET_INFO_COMMAND, get_info)) => {
+					cxl_dcd_command::get_info(&get_info,
+								  &options);
+				},
+				Some((CXL_FM_DCD_GET_CONFIG_COMMAND, get_capacity_config)) => {
+					cxl_dcd_command::get_capacity_config(&get_capacity_config,
+									     &options);
+				},
+				Some((CXL_FM_DCD_SET_CONFIG_COMMAND, set_capacity_config)) => {
+					cxl_dcd_command::set_capacity_config(&set_capacity_config,
+									     &options);
+				},
+				Some((CXL_FM_DCD_GET_EXTENT_COMMAND, get_extent_list)) => {
+					cxl_dcd_command::get_extent_list(&get_extent_list,
+									 &options);
+				},
+				Some((CXL_FM_DCD_ADD_CAPACITY_COMMAND, add_capacity)) => {
+					cxl_dcd_command::add_capacity(&add_capacity,
+								      &options);
+				},
+				Some((CXL_FM_DCD_RELEASE_CAPACITY_COMMAND, release_capacity)) => {
+					cxl_dcd_command::release_capacity(&release_capacity,
+									  &options);
+				},
+				_ => unreachable!(),
+			}
+		},
+		_ => unreachable!(),
+	}
+}
diff --git a/fm_cli/src/mld_port.rs b/fm_cli/src/mld_port.rs
new file mode 100644
index 0000000..f8ecbb3
--- /dev/null
+++ b/fm_cli/src/mld_port.rs
@@ -0,0 +1,126 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_mld_port_command {
+	use clap::{ArgMatches};
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_command;
+	use fm_library::cxl_fm_lib::CXL_FM_MLD_PORT_TUNNEL_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_MLD_PORT_SEND_MEM_REQ_COMMAND;
+
+	/*
+	 * Tunnel Management Command
+	 */
+	pub fn tunnel(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_MLD_PORT_TUNNEL_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_MLD_PORT_TUNNEL_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Send CXL.io configuration request
+	 */
+	pub fn send_config(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_MLD_PORT_SEND_CONFIG_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Send CXL.io memory request
+	 */
+	pub fn send_memory_request(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_MLD_PORT_SEND_MEM_REQUEST_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_MLD_PORT_SEND_MEM_REQ_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+}
diff --git a/fm_cli/src/multi_headed_device.rs b/fm_cli/src/multi_headed_device.rs
new file mode 100644
index 0000000..00563fe
--- /dev/null
+++ b/fm_cli/src/multi_headed_device.rs
@@ -0,0 +1,60 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_mh_device_command {
+	use clap::{ArgMatches};
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_command;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_MHD_INFO_COMMAND;
+
+	/*
+	 * Get Multi Headed Device (MHD) status/info
+	 */
+	pub fn get_info(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_MH_DEVICE_GET_INFO_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_MHD_INFO_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+}
diff --git a/fm_cli/src/pci2pci_bridge.rs b/fm_cli/src/pci2pci_bridge.rs
new file mode 100644
index 0000000..e7b82a5
--- /dev/null
+++ b/fm_cli/src/pci2pci_bridge.rs
@@ -0,0 +1,126 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_ppb_command {
+	use clap::{ArgMatches};
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_command;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_PPB_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_PPB_BIND_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_PPB_UNBIND_COMMAND;
+
+	/*
+	 * Send PCI-to-PCI Bridge (PPB) configuration request
+	 */
+	pub fn config(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_PPB_CONFIG_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_PPB_CONFIG_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Bind Virtual PCI-to-PCI Bridge (vPPB) inside a CXL switch
+	 */
+	pub fn bind(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_PPB_BIND_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_PPB_BIND_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Unbind Virtual PCI-to-PCI Bridge (vPPB) inside a CXL switch
+	 */
+	pub fn unbind(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_PPB_UNBIND_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_PPB_UNBIND_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+}
diff --git a/fm_cli/src/physical_port.rs b/fm_cli/src/physical_port.rs
new file mode 100644
index 0000000..6e887ed
--- /dev/null
+++ b/fm_cli/src/physical_port.rs
@@ -0,0 +1,159 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_physical_port_command {
+	use clap::{ArgMatches};
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_command;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_PHYSICAL_PORT_INFO_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_BIND_PHYSICAL_PORT_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_UNBIND_PHYSICAL_PORT_COMMAND;
+
+	/*
+	 * Get state of physical port
+	 */
+	pub fn get_info(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_PHYSICAL_PORT_GET_INFO_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_PHYSICAL_PORT_INFO_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Control physical port
+	 */
+	pub fn control(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_PHYSICAL_PORT_CONTROL_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Bind physical port to Virtual PCI-to-PCI Bridge (vPPB)
+	 */
+	pub fn bind(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_PHYSICAL_PORT_BIND_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_BIND_PHYSICAL_PORT_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Unbind physical port from Virtual PCI-to-PCI Bridge (vPPB)
+	 */
+	pub fn unbind(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_PHYSICAL_PORT_UNBIND_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_UNBIND_PHYSICAL_PORT_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+}
diff --git a/fm_cli/src/switch.rs b/fm_cli/src/switch.rs
new file mode 100644
index 0000000..404bb07
--- /dev/null
+++ b/fm_cli/src/switch.rs
@@ -0,0 +1,126 @@
+/*
+ * CXL FM Infrastructure -- CXl Fabric Manager (FM) Infrastructure.
+ *
+ * CXL FM configuration tool implementation.
+ *
+ * Copyright (c) 2023 Viacheslav Dubeyko <slava@dubeyko.com>,
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+pub mod cxl_switch_command {
+	use clap::{ArgMatches};
+	use std::net::{TcpStream};
+	use fm_library::cxl_fm_lib::CxlFmOptions;
+	use fm_library::cxl_fm_lib::send_command;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_SWITCH_INFO_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_GET_SWITCH_CONFIG_COMMAND;
+	use fm_library::cxl_fm_lib::CXL_FM_SET_SWITCH_CONFIG_COMMAND;
+
+	/*
+	 * Get CXL switch status/info
+	 */
+	pub fn get_info(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_SWITCH_GET_INFO_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_SWITCH_INFO_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Get CXL switch configuration
+	 */
+	pub fn get_config(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_SWITCH_GET_CONFIG_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_GET_SWITCH_CONFIG_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+
+	/*
+	 * Set CXL switch configuration
+	 */
+	pub fn set_config(options: &ArgMatches, env: &CxlFmOptions) {
+		if env.is_debug {
+			println!("{}", crate::CXL_FM_SWITCH_SET_CONFIG_COMMAND_DESCRIPTOR);
+		}
+
+		if options.args_present() {
+			/*
+			 * Ignore currently.
+			 * Add code later.
+			 */
+		}
+
+		match TcpStream::connect(&env.ip_port) {
+			Ok(mut stream) => {
+				if env.is_debug {
+					println!("Successfully connected to server: {}",
+						 env.ip_port);
+				}
+
+				send_command(&mut stream,
+					     CXL_FM_SET_SWITCH_CONFIG_COMMAND,
+					     &env);
+			},
+			Err(e) => {
+				println!("Failed to connect: {}", e);
+			}
+		}
+	}
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-06-02 21:38 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-06-02 21:37 [RFC PATCH 0/5] CXL FM initial infrastructure Viacheslav Dubeyko
2023-06-02 21:37 ` [RFC PATCH 1/5] CXL FM: create initial project infrastructure Viacheslav Dubeyko
2023-06-02 21:37 ` [RFC PATCH 2/5] CXL FM: [lib] introduce CXL FM library Viacheslav Dubeyko
2023-06-02 21:37 ` [RFC PATCH 3/5] CXL FM: [fm_daemon] introduce CXL FM daemon Viacheslav Dubeyko
2023-06-02 21:37 ` [RFC PATCH 4/5] CXL FM: [fm_orchestrator] introduce CXL FM orchestrator Viacheslav Dubeyko
2023-06-02 21:37 ` [RFC PATCH 5/5] CXL FM: [fm_cli] introduce CXL FM CLI tool Viacheslav Dubeyko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.