Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9058719a authored by Abhishek Pandit-Subedi's avatar Abhishek Pandit-Subedi
Browse files

floss: Make inotify watchers more resilient

Sometimes during boot, /sys/class/bluetooth won't be available when
btmanagerd runs. In such cases, we should poll the directory rather than
panic right away.

Also refactor some logs to properly reflect why failures are occurring.

Bug: 225434492
Test: Reboot test on ChromeOS
Tag: #floss
Change-Id: Ie4d0ea161270b269a9dde0431767b4d75892c1ec
parent c2535e94
Loading
Loading
Loading
Loading
+85 −36
Original line number Diff line number Diff line
@@ -5,11 +5,12 @@ use log::{debug, error, info, warn};
use nix::sys::signal::{self, Signal};
use nix::unistd::Pid;
use regex::Regex;
use std::cmp;
use std::process::{Child, Command, Stdio};
use std::sync::Arc;
use std::time::Duration;
use tokio::io::unix::AsyncFd;
use tokio::sync::mpsc;
use tokio::time::{sleep, Duration};

// Directory for Bluetooth pid file
pub const PID_DIR: &str = "/var/run/bluetooth";
@@ -84,6 +85,10 @@ pub struct StateMachineProxy {
const TX_SEND_TIMEOUT_DURATION: Duration = Duration::from_secs(3);
const COMMAND_TIMEOUT_DURATION: Duration = Duration::from_secs(3);

/// Maximum amount of time (in seconds) we should wait before polling for
/// /sys/class/bluetooth to become available.
const HCI_DEVICE_SLEEP_MAX_SECONDS: u64 = 64;

impl StateMachineProxy {
    pub fn start_bluetooth(&self, hci_interface: i32) {
        let tx = self.tx.clone();
@@ -122,8 +127,8 @@ fn pid_inotify_async_fd() -> AsyncFd<inotify::Inotify> {
    let mut pid_detector = inotify::Inotify::init().expect("cannot use inotify");
    pid_detector
        .add_watch(PID_DIR, inotify::WatchMask::CREATE | inotify::WatchMask::DELETE)
        .expect("failed to add watch");
    AsyncFd::new(pid_detector).expect("failed to add async fd")
        .expect("failed to add watch on pid directory");
    AsyncFd::new(pid_detector).expect("failed to add async fd for pid detector")
}

/// Given an pid path, returns the adapter index for that pid path.
@@ -132,15 +137,29 @@ fn get_hci_index_from_pid_path(path: &str) -> Option<i32> {
    re.captures(path)?.get(1)?.as_str().parse().ok()
}

fn hci_devices_inotify_async_fd() -> AsyncFd<inotify::Inotify> {
    let mut detector = inotify::Inotify::init().expect("cannot use inotify");
    detector
        .add_watch(
fn hci_devices_inotify_async_fd() -> Option<AsyncFd<inotify::Inotify>> {
    let detector = inotify::Inotify::init().and_then(|mut detector| {
        match detector.add_watch(
            config_util::HCI_DEVICES_DIR,
            inotify::WatchMask::CREATE | inotify::WatchMask::DELETE,
        )
        .expect("failed to add watch");
    AsyncFd::new(detector).expect("failed to add async fd")
        ) {
            Ok(_) => Ok(detector),
            Err(e) => Err(e),
        }
    });
    match detector {
        Ok(d) => match AsyncFd::new(d) {
            Ok(afd) => Some(afd),
            Err(_) => {
                warn!("Could not init asyncfd for {}", config_util::HCI_DEVICES_DIR);
                None
            }
        },
        Err(_) => {
            warn!("Could not init inotify: {}", config_util::HCI_DEVICES_DIR);
            None
        }
    }
}

/// On startup, get and cache all hci devices by emitting the callback
@@ -250,7 +269,7 @@ pub async fn mainloop(
                            .unwrap();
                    }
                }
                Err(_) | Ok(Err(_)) => panic!("why can't we read while the asyncfd is ready?"),
                Err(_) | Ok(Err(_)) => panic!("Inotify watcher on {} failed.", PID_DIR),
            }
            fd_ready.clear_ready();
            drop(fd_ready);
@@ -258,13 +277,29 @@ pub async fn mainloop(
    });

    // Set up an HCI device listener to emit HCI device inotify messages
    let mut hci_devices_async_fd = hci_devices_inotify_async_fd();
    let hci_tx = context.tx.clone();

    tokio::spawn(async move {
        debug!("Spawned hci notify task");

        // Try to create an inotify on /sys/class/bluetooth and listen for any
        // changes. If we fail to create the inotify, we go into a polling mode
        // which will do exponential backoff waiting for Bluetooth to become
        // available.
        //
        // TODO(b/226644782) - Eventually we need to replace this inotify
        // listener with something that talks to MGMT via socket(AF_BLUETOOTH).
        // We should poll on INDEX_ADDED/INDEX_REMOVED rather than inotify the
        // /sys/class/bluetooth directory.
        let mut sleep_duration = 1;
        loop {
            match hci_devices_inotify_async_fd() {
                Some(mut hci_inotify) => {
                    sleep_duration = 1;

                    // This inner loop runs successfully as long as the hci inotify is valid.
                    loop {
            let r = hci_devices_async_fd.readable_mut();
                        let r = hci_inotify.readable_mut();
                        let mut fd_ready = r.await.unwrap();
                        let mut buffer: [u8; 1024] = [0; 1024];
                        debug!("Found new hci device entries. Reading them.");
@@ -283,11 +318,27 @@ pub async fn mainloop(
                                        .unwrap();
                                }
                            }
                Err(_) | Ok(Err(_)) => panic!("why can't we read while the asyncfd is ready?"),
                            // In the case where inotify fails, we want to reconfigure the inotify
                            // again.
                            Err(_) | Ok(Err(_)) => {
                                warn!(
                                    "Inotify watcher on {} failed.",
                                    config_util::HCI_DEVICES_DIR
                                );
                                break;
                            }
                        }
                        fd_ready.clear_ready();
                        drop(fd_ready);
                    }
                }
                None => {
                    // Exponential backoff until we succeed.
                    sleep_duration = cmp::min(sleep_duration * 2, HCI_DEVICE_SLEEP_MAX_SECONDS);
                    sleep(Duration::from_secs(sleep_duration)).await;
                }
            }
        }
    });

    // Listen for all messages and act on them
@@ -343,7 +394,7 @@ pub async fn mainloop(
                            true => {
                                command_timeout.cancel();
                            }
                            false => error!("unexpected BluetoothStarted pid{} hci{}", pid, hci),
                            false => warn!("unexpected BluetoothStarted pid{} hci{}", pid, hci),
                        }
                    }
                    AdapterStateActions::BluetoothStopped(i) => {
@@ -393,9 +444,7 @@ pub async fn mainloop(
                                .await
                                .unwrap();
                        }
                        (hci, s) => {
                            warn!("invalid file hci={:?} pid_file={:?}", hci, s)
                        }
                        _ => debug!("Invalid pid path: {}", fname),
                    }
                }
                (inotify::EventMask::DELETE, Some(fname)) => {