blob: 388dadc64cf2dfdd3442c9f3655444b7e8ad0871 [file] [log] [blame]
Bart Van Assche0223cd82021-08-06 10:21:12 -07001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <android-base/file.h>
18#include <android-base/logging.h>
19#include <android-base/stringprintf.h>
20#include <android-base/strings.h>
21
22#include <dirent.h>
23#include <libdm/dm.h>
24#include <sys/stat.h>
25#include <sys/sysmacros.h>
26#include <sys/types.h>
27#include "blockdev.h"
28
29using android::base::Basename;
30using android::base::ErrnoError;
31using android::base::Error;
32using android::base::Result;
Bart Van Assche0223cd82021-08-06 10:21:12 -070033using android::base::StartsWith;
34using android::base::StringPrintf;
35using android::base::unique_fd;
36using android::dm::DeviceMapper;
37
38// Return the parent device of a partition. Converts e.g. "sda26" into "sda".
39static std::string PartitionParent(const std::string& blockdev) {
40 if (blockdev.find('/') != std::string::npos) {
41 LOG(ERROR) << __func__ << ": invalid argument " << blockdev;
42 return blockdev;
43 }
44 auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/sys/class/block"), closedir};
45 if (!dir) {
46 return blockdev;
47 }
48 for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
49 if (ent->d_name[0] == '.') {
50 continue;
51 }
52 std::string path = StringPrintf("/sys/class/block/%s/%s", ent->d_name, blockdev.c_str());
53 struct stat statbuf;
54 if (stat(path.c_str(), &statbuf) >= 0) {
55 return ent->d_name;
56 }
57 }
58 return blockdev;
59}
60
61// Convert a major:minor pair into a block device name.
62static std::string BlockdevName(dev_t dev) {
63 auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/dev/block"), closedir};
64 if (!dir) {
65 return {};
66 }
67 for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {
68 if (ent->d_name[0] == '.') {
69 continue;
70 }
71 const std::string path = std::string("/dev/block/") + ent->d_name;
72 struct stat statbuf;
73 if (stat(path.c_str(), &statbuf) >= 0 && dev == statbuf.st_rdev) {
74 return ent->d_name;
75 }
76 }
77 return {};
78}
79
80// Trim whitespace from the end of a string.
81static void rtrim(std::string& s) {
82 s.erase(s.find_last_not_of('\n') + 1, s.length());
83}
84
85// For file `file_path`, retrieve the block device backing the filesystem on
86// which the file exists and return the queue depth of the block device.
87static Result<uint32_t> BlockDeviceQueueDepth(const std::string& file_path) {
88 struct stat statbuf;
89 int res = stat(file_path.c_str(), &statbuf);
90 if (res < 0) {
91 return ErrnoError() << "stat(" << file_path << ")";
92 }
93 std::string blockdev = "/dev/block/" + BlockdevName(statbuf.st_dev);
94 LOG(DEBUG) << __func__ << ": " << file_path << " -> " << blockdev;
95 if (blockdev.empty()) {
Jiyong Parkd185d4a2021-12-11 10:45:20 +090096 return Errorf("Failed to convert {}:{} (path {})", major(statbuf.st_dev),
97 minor(statbuf.st_dev), file_path.c_str());
Bart Van Assche0223cd82021-08-06 10:21:12 -070098 }
99 auto& dm = DeviceMapper::Instance();
100 for (;;) {
101 std::optional<std::string> child = dm.GetParentBlockDeviceByPath(blockdev);
102 if (!child) {
103 break;
104 }
105 LOG(DEBUG) << __func__ << ": " << blockdev << " -> " << *child;
106 blockdev = *child;
107 }
108 std::optional<std::string> maybe_blockdev = android::dm::ExtractBlockDeviceName(blockdev);
109 if (!maybe_blockdev) {
Jiyong Parkd185d4a2021-12-11 10:45:20 +0900110 return Errorf("Failed to remove /dev/block/ prefix from {}", blockdev);
Bart Van Assche0223cd82021-08-06 10:21:12 -0700111 }
112 blockdev = PartitionParent(*maybe_blockdev);
113 LOG(DEBUG) << __func__ << ": "
114 << "Partition parent: " << blockdev;
115 const std::string nr_tags_path =
116 StringPrintf("/sys/class/block/%s/mq/0/nr_tags", blockdev.c_str());
117 std::string nr_tags;
118 if (!android::base::ReadFileToString(nr_tags_path, &nr_tags)) {
Jiyong Parkd185d4a2021-12-11 10:45:20 +0900119 return Errorf("Failed to read {}", nr_tags_path);
Bart Van Assche0223cd82021-08-06 10:21:12 -0700120 }
121 rtrim(nr_tags);
122 LOG(DEBUG) << __func__ << ": " << file_path << " is backed by /dev/" << blockdev
123 << " and that block device supports queue depth " << nr_tags;
124 return strtol(nr_tags.c_str(), NULL, 0);
125}
126
127// Set 'nr_requests' of `loop_device_path` to the queue depth of the block
128// device backing `file_path`.
129Result<void> ConfigureQueueDepth(const std::string& loop_device_path,
130 const std::string& file_path) {
131 if (!StartsWith(loop_device_path, "/dev/")) {
132 return Error() << "Invalid argument " << loop_device_path;
133 }
134
135 const std::string loop_device_name = Basename(loop_device_path);
136
Jiyong Parkd185d4a2021-12-11 10:45:20 +0900137 const auto qd = BlockDeviceQueueDepth(file_path);
Bart Van Assche0223cd82021-08-06 10:21:12 -0700138 if (!qd.ok()) {
Jiyong Parkd185d4a2021-12-11 10:45:20 +0900139 return qd.error();
Bart Van Assche0223cd82021-08-06 10:21:12 -0700140 }
141 const std::string nr_requests = StringPrintf("%u", *qd);
142 const std::string sysfs_path =
143 StringPrintf("/sys/class/block/%s/queue/nr_requests", loop_device_name.c_str());
144 unique_fd sysfs_fd(open(sysfs_path.c_str(), O_RDWR | O_CLOEXEC));
145 if (sysfs_fd == -1) {
146 return ErrnoError() << "Failed to open " << sysfs_path;
147 }
148
149 const int res = write(sysfs_fd.get(), nr_requests.data(), nr_requests.length());
150 if (res < 0) {
151 return ErrnoError() << "Failed to write to " << sysfs_path;
152 }
153 return {};
154}