Repository: threefoldtech/zos
Pull request #1996 (2023-07-04 14:14:47)

Use SSID to identify PCI devices
Use SSID to further specify which device is connected to PCI.

Related: #1994 

Notes:
- Some GPUs will still not be identified accurately due to `pci.ids` not having SSID for all devices.


Patch:
diff --git a/cmds/modules/provisiond/api.go b/cmds/modules/provisiond/api.go
index 094ca054..bf6dd5e5 100644
--- a/cmds/modules/provisiond/api.go
+++ b/cmds/modules/provisiond/api.go
@@ -86,8 +86,8 @@ func setupGPURmb(router rmb.Router, store provision.Storage) {
 		}
 
 		var list []Info
-		for _, device := range devices {
-			id := device.ShortID()
+		for _, pciDevice := range devices {
+			id := pciDevice.ShortID()
 			info := Info{
 				ID:       id,
 				Vendor:   "unknown",
@@ -95,12 +95,17 @@ func setupGPURmb(router rmb.Router, store provision.Storage) {
 				Contract: used[id],
 			}
 
-			vendor, device, ok := device.GetDevice()
+			vendor, device, ok := pciDevice.GetDevice()
 			if ok {
 				info.Vendor = vendor.Name
 				info.Device = device.Name
 			}
 
+			subdevice, ok := pciDevice.GetSubdevice()
+			if ok {
+				info.Device = subdevice.Name
+			}
+
 			list = append(list, info)
 		}
 
diff --git a/docs/architecture/decisions/0007-smtp-drop.md b/docs/architecture/decisions/0007-smtp-drop.md
new file mode 100644
index 00000000..59c243ff
--- /dev/null
+++ b/docs/architecture/decisions/0007-smtp-drop.md
@@ -0,0 +1,21 @@
+# 1. Dynamic Cache
+
+Date: 2023-07-06
+
+## Status
+
+Accepted
+
+## Context
+
+Block SMTP traffic for private nodes
+
+## Decision
+
+Workloads that are hidden (has no public IPs) cannot send traffic over 25, 587, 465. This is to prevent reduce spam because
+many farmers (specially home farmers) got warnings because of detected spam traffic
+
+## Consequences
+
+Workloads in private farms can not send or receive SMTP traffic. A user willing to establish smtp traffic need to either use
+a public node or have public ip
diff --git a/docs/manual/zmachine/zmachine.md b/docs/manual/zmachine/zmachine.md
index 5419793f..ddbd102e 100644
--- a/docs/manual/zmachine/zmachine.md
+++ b/docs/manual/zmachine/zmachine.md
@@ -87,14 +87,14 @@ Next just remove this link and re-create the file with a valid name to be able t
 export PATH=/usr/local/sbin/:/usr/local/bin/:/usr/sbin/:/usr/bin/:/sbin:/bin
 
 rm /etc/resolv.conf
-echo 'nameserver 1.1.1.1' > /etc/resolv.conf`
+echo 'nameserver 1.1.1.1' > /etc/resolv.conf
 ```
 
 Install cloud-init
 
 ```bash
 apt-get update
-apt-get cloud-init openssh-server curl
+apt-get install cloud-init openssh-server curl
 # to make sure we have clean setup
 cloud-init clean
 ```
@@ -129,7 +129,7 @@ you should be out of the arch-chroot now
 
 ```bash
 curl -O https://raw.githubusercontent.com/torvalds/linux/master/scripts/extract-vmlinux
-chmox +x extract-vmlinux
+chmod +x extract-vmlinux
 
 sudo ./extract-vmlinux ubuntu:jammy/boot/vmlinuz | sudo tee ubuntu:jammy/boot/vmlinuz-5.15.0-25-generic.elf > /dev/null
 # then replace original kernel
diff --git a/pkg/capacity/pci.go b/pkg/capacity/pci.go
index 9662ad04..fb60b1cd 100644
--- a/pkg/capacity/pci.go
+++ b/pkg/capacity/pci.go
@@ -16,12 +16,24 @@ const (
 	pciDir = "/sys/bus/pci/devices"
 )
 
+// Subdevice is subdevice information to a PCI device
+type Subdevice struct {
+	// SubsystemVendorID is device subsystem vendor ID according to PCI database
+	SubsystemVendorID uint16
+	// SubsystemVendorID is device subsystem ID according to PCI database
+	SubsystemDeviceID uint16
+	// Name is subdevice name according to PCI database
+	Name string
+}
+
 // Device is a PCI device
 type Device struct {
 	// ID is device id according to PCI database
 	ID uint16
 	// Name is device name according to PCI database
 	Name string
+	// Subdevices is a slice of all subdevices according to PCI database
+	Subdevices []Subdevice
 }
 
 // Vendor is Vendor information
@@ -41,7 +53,7 @@ var (
 	//go:embed pci/pci.ids
 	src []byte
 
-	pattern = regexp.MustCompile(`^(\t*)([\d|a-f]{4})(.+)$`)
+	pattern = regexp.MustCompile(`^(\t*)([\d|a-f]{4}) ([\d|a-f]{4}){0,1}(.+)$`)
 	data    map[uint16]Vendor
 
 	gpuVendorsWhitelist = []uint16{
@@ -63,6 +75,7 @@ func loadVendors(src []byte) (map[uint16]Vendor, error) {
 
 	scanner := bufio.NewScanner(bytes.NewBuffer(src))
 	var current *Vendor
+	var deviceID uint16
 	for scanner.Scan() {
 		line := scanner.Text()
 		if strings.HasPrefix(line, "#") {
@@ -73,13 +86,13 @@ func loadVendors(src []byte) (map[uint16]Vendor, error) {
 		if matches == nil {
 			continue
 		}
-		// this should have `4 parts`
+		// this should have `5 parts`
 		tabs := matches[1]
 		id64, err := strconv.ParseUint(matches[2], 16, 16)
 		if err != nil {
 			return nil, fmt.Errorf("failed to parse vendor or device id: %w", err)
 		}
-		rest := strings.TrimSpace(matches[3])
+		rest := strings.TrimSpace(matches[4])
 
 		id := uint16(id64)
 		// the line has no leading tabs. this is a new vendor
@@ -99,9 +112,32 @@ func loadVendors(src []byte) (map[uint16]Vendor, error) {
 			}
 
 			current.Devices[id] = Device{
-				ID:   id,
-				Name: rest,
+				ID:         id,
+				Name:       rest,
+				Subdevices: make([]Subdevice, 0),
+			}
+			deviceID = id
+		} else if len(tabs) == 2 {
+			if current == nil {
+				return nil, fmt.Errorf("subsystem appeared before a vendor: %s", line)
+			}
+			if len(current.Devices) == 0 {
+				return nil, fmt.Errorf("subsystem appeared before a device: %s", line)
 			}
+			ssid64, err := strconv.ParseUint(matches[3], 16, 16)
+			if err != nil {
+				return nil, fmt.Errorf("failed to parse subsystem id: %w", err)
+			}
+			ssid := uint16(ssid64)
+
+			device := current.Devices[deviceID]
+			device.Subdevices = append(device.Subdevices, Subdevice{
+				SubsystemVendorID: id,
+				SubsystemDeviceID: ssid,
+				Name:              rest,
+			})
+			current.Devices[deviceID] = device
+
 		}
 	}
 
@@ -125,6 +161,20 @@ func GetDevice(vendor uint16, device uint16) (v Vendor, d Device, ok bool) {
 	return
 }
 
+// GetSubdevice looks up the subdevice using devices db
+func GetSubdevice(vendor uint16, device uint16, subsystemVendorID uint16, subsystemDeviceID uint16) (Subdevice, bool) {
+	_, d, ok := GetDevice(vendor, device)
+	if !ok {
+		return Subdevice{}, false
+	}
+	for _, subdevice := range d.Subdevices {
+		if subdevice.SubsystemDeviceID == subsystemDeviceID && subdevice.SubsystemVendorID == subsystemVendorID {
+			return subdevice, true
+		}
+	}
+	return Subdevice{}, false
+}
+
 // Filter over the PCI for listing
 type Filter func(pci *PCI) bool
 
@@ -138,6 +188,10 @@ type PCI struct {
 	Device uint16
 	// Class of the device
 	Class uint32
+	// Subsystem Vendor of the device
+	SubsystemVendor uint16
+	// Subsystem ID of the device
+	SubsystemDevice uint16
 }
 
 // GetDevice gets the attached PCI device information (vendor and device)
@@ -145,6 +199,11 @@ func (p *PCI) GetDevice() (Vendor, Device, bool) {
 	return GetDevice(p.Vendor, p.Device)
 }
 
+// GetSubdevice gets the attached PCI subdevice information
+func (p *PCI) GetSubdevice() (Subdevice, bool) {
+	return GetSubdevice(p.Vendor, p.Device, p.SubsystemVendor, p.SubsystemDevice)
+}
+
 // ShortID returns a short identification string
 // for the device in the format `slot/vendor/device`
 func (p *PCI) ShortID() string {
@@ -162,9 +221,11 @@ func (p *PCI) Flag(name string) (uint64, error) {
 
 func pciDeviceFromSlot(slot string) (PCI, error) {
 	const (
-		classFile  = "class"
-		vendorFile = "vendor"
-		deviceFile = "device"
+		classFile           = "class"
+		vendorFile          = "vendor"
+		deviceFile          = "device"
+		subsystemVendorFile = "subsystem_vendor"
+		subsystemDeviceFile = "subsystem_device"
 	)
 	class, err := readUint64(filepath.Join(pciDir, slot, classFile), 32)
 	if err != nil {
@@ -172,19 +233,29 @@ func pciDeviceFromSlot(slot string) (PCI, error) {
 	}
 	vendor, err := readUint64(filepath.Join(pciDir, slot, vendorFile), 16)
 	if err != nil {
-		return PCI{}, fmt.Errorf("failed to get device '%s' class: %w", slot, err)
+		return PCI{}, fmt.Errorf("failed to get device '%s' vendor: %w", slot, err)
 	}
 
 	device, err := readUint64(filepath.Join(pciDir, slot, deviceFile), 16)
 	if err != nil {
-		return PCI{}, fmt.Errorf("failed to get device '%s' class: %w", slot, err)
+		return PCI{}, fmt.Errorf("failed to get device '%s' device: %w", slot, err)
+	}
+	subsystemVendor, err := readUint64(filepath.Join(pciDir, slot, subsystemVendorFile), 16)
+	if err != nil {
+		return PCI{}, fmt.Errorf("failed to get device '%s' subsystem vendor: %w", slot, err)
+	}
+	subsystemDevice, err := readUint64(filepath.Join(pciDir, slot, subsystemDeviceFile), 16)
+	if err != nil {
+		return PCI{}, fmt.Errorf("failed to get device '%s' subsystem device: %w", slot, err)
 	}
 
 	pci := PCI{
-		Slot:   slot,
-		Class:  uint32(class),
-		Vendor: uint16(vendor),
-		Device: uint16(device),
+		Slot:            slot,
+		Class:           uint32(class),
+		Vendor:          uint16(vendor),
+		Device:          uint16(device),
+		SubsystemVendor: uint16(subsystemVendor),
+		SubsystemDevice: uint16(subsystemDevice),
 	}
 
 	return pci, err
diff --git a/pkg/registrar/registrar.go b/pkg/registrar/registrar.go
index 0deea142..98f3952b 100644
--- a/pkg/registrar/registrar.go
+++ b/pkg/registrar/registrar.go
@@ -152,6 +152,8 @@ func (r *Registrar) reActivate(ctx context.Context, cl zbus.Client, env environm
 		return err
 	}
 
+	defer sub.Close()
+
 	sk := ed25519.PrivateKey(stubs.NewIdentityManagerStub(cl).PrivateKey(ctx))
 	id, err := substrate.NewIdentityFromEd25519Key(sk)
 	if err != nil {


Test patch:
diff --git a/pkg/capacity/pci_test.go b/pkg/capacity/pci_test.go
index 3e28e639..4bf9c0c0 100644
--- a/pkg/capacity/pci_test.go
+++ b/pkg/capacity/pci_test.go
@@ -15,6 +15,18 @@ func TestGetDevice(t *testing.T) {
 	require.Equal(t, "Navi 10 [Radeon RX 5600 OEM/5600 XT / 5700/5700 XT]", device.Name)
 }
 
+func TestGetSubdevice(t *testing.T) {
+	subdevice, ok := GetSubdevice(0x10de, 0x1e30, 0x10de, 0x129e)
+
+	require.True(t, ok)
+	require.Equal(t, "Quadro RTX 8000", subdevice.Name)
+
+	subdevice, ok = GetSubdevice(0x10de, 0x1e30, 0x10de, 0x12ba)
+
+	require.True(t, ok)
+	require.Equal(t, "Quadro RTX 6000", subdevice.Name)
+}
+
 func TestListPCI(t *testing.T) {
 	devices, err := ListPCI()
 	require.NoError(t, err)