├── .gitignore ├── CMakeLists.txt ├── LICENSE.Apache2 ├── LICENSE.LGPLv2.1 ├── README.md ├── alua.c ├── alua.h ├── api.c ├── be_byteshift.h ├── ccan └── ccan │ ├── build_assert │ └── build_assert.h │ ├── check_type │ └── check_type.h │ ├── container_of │ └── container_of.h │ ├── list │ └── list.h │ └── str │ ├── str.h │ └── str_debug.h ├── configfs.c ├── consumer.c ├── darray.h ├── extra ├── install_dep.sh └── make_runnerrpms.sh ├── file_example.c ├── file_optical.c ├── file_zbc.c ├── glfs.c ├── libtcmu-register.c ├── libtcmu.c ├── libtcmu.h ├── libtcmu_common.h ├── libtcmu_config.c ├── libtcmu_config.h ├── libtcmu_log.c ├── libtcmu_log.h ├── libtcmu_priv.h ├── libtcmu_time.c ├── libtcmu_time.h ├── logrotate.conf ├── logrotate.conf_install.cmake.in ├── main-syms.txt ├── main.c ├── org.kernel.TCMUService1.service ├── qcow.c ├── qcow.h ├── qcow2.h ├── rbd.c ├── scsi.c ├── scsi.h ├── scsi_defs.h ├── string_priv.h ├── strlcpy.c ├── target.c ├── target.h ├── target_core_user_local.h ├── tcmu-handler.xml ├── tcmu-runner.8 ├── tcmu-runner.conf ├── tcmu-runner.h ├── tcmu-runner.service ├── tcmu-runner.spec ├── tcmu-synthesizer.c ├── tcmu.conf ├── tcmu.conf_install.cmake.in ├── tcmu_runner_priv.h ├── tcmur_aio.c ├── tcmur_aio.h ├── tcmur_cmd_handler.c ├── tcmur_cmd_handler.h ├── tcmur_device.c ├── tcmur_device.h ├── tcmur_work.c ├── tcmur_work.h └── version.h.in /.gitignore: -------------------------------------------------------------------------------- 1 | tcmu-runner 2 | tcmu-synthesizer 3 | consumer 4 | *.o 5 | *.a 6 | *.swp 7 | *.patch 8 | handler_*.so 9 | libtcmu.so* 10 | *generated.[ch] 11 | Makefile 12 | version.h 13 | CMakeCache* 14 | CMakeFiles/* 15 | cmake_install.cmake 16 | install_manifest.txt 17 | cscope.* 18 | ncscope.* 19 | tcmu.conf_install.cmake 20 | core.* 21 | extra/rpmbuild 22 | logrotate.conf_install.cmake 23 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8 FATAL_ERROR) 2 | project (tcmu-runner C) 3 | set(VERSION 1.6.2) 4 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wall -Wdeclaration-after-statement -std=c99") 5 | 6 | include(GNUInstallDirs) 7 | include(CheckIncludeFile) 8 | 9 | set(tcmu-runner_HANDLER_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/tcmu-runner") 10 | 11 | option(with-glfs "build Gluster glfs handler" true) 12 | option(with-qcow "build qcow handler" true) 13 | option(with-rbd "build Ceph rbd handler" true) 14 | option(with-zbc "build zbc handler" true) 15 | option(with-fbo "build fbo handler" true) 16 | option(with-tcmalloc "link against tcmalloc" true) 17 | 18 | find_library(LIBNL_LIB nl-3) 19 | find_library(LIBNL_GENL_LIB nl-genl-3) 20 | set(LIBNL_LIBS 21 | ${LIBNL_LIB} 22 | ${LIBNL_GENL_LIB} 23 | ) 24 | 25 | find_path (LIBNL_INCLUDE_DIR 26 | NAMES 27 | netlink/netlink.h 28 | PATH_SUFFIXES 29 | libnl3 30 | ) 31 | 32 | find_package(PkgConfig) 33 | pkg_check_modules(GLIB REQUIRED gio-unix-2.0) 34 | pkg_check_modules(KMOD REQUIRED libkmod) 35 | 36 | find_library(PTHREAD pthread) 37 | find_library(DL dl) 38 | 39 | if (with-tcmalloc) 40 | find_library(TCMALLOC_LIB tcmalloc) 41 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free") 42 | endif(with-tcmalloc) 43 | 44 | # Stuff for building the shared library 45 | add_library(tcmu 46 | SHARED 47 | strlcpy.c 48 | configfs.c 49 | api.c 50 | libtcmu.c 51 | libtcmu-register.c 52 | tcmuhandler-generated.c 53 | libtcmu_log.c 54 | libtcmu_config.c 55 | libtcmu_time.c 56 | ) 57 | set_target_properties(tcmu 58 | PROPERTIES 59 | VERSION 2.2 60 | SOVERSION "2" 61 | ) 62 | target_include_directories(tcmu 63 | PUBLIC ${LIBNL_INCLUDE_DIR} 64 | PUBLIC ${GLIB_INCLUDE_DIRS} 65 | PUBLIC ${PROJECT_SOURCE_DIR}/ccan 66 | ) 67 | target_link_libraries(tcmu 68 | ${LIBNL_LIB} 69 | ${LIBNL_GENL_LIB} 70 | ${GLIB_LIBRARIES} 71 | ${PTHREAD} 72 | ${TCMALLOC_LIB} 73 | ) 74 | install(TARGETS tcmu LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) 75 | 76 | # Stuff for building the static library 77 | add_library(tcmu_static 78 | strlcpy.c 79 | configfs.c 80 | api.c 81 | libtcmu.c 82 | libtcmu-register.c 83 | tcmuhandler-generated.c 84 | libtcmu_log.c 85 | libtcmu_config.c 86 | libtcmu_time.c 87 | ) 88 | target_include_directories(tcmu_static 89 | PUBLIC ${LIBNL_INCLUDE_DIR} 90 | PUBLIC ${GLIB_INCLUDE_DIRS} 91 | PUBLIC ${PROJECT_SOURCE_DIR}/ccan 92 | ) 93 | target_link_libraries(tcmu_static 94 | ${GLIB_LIBRARIES} 95 | ${TCMALLOC_LIB} 96 | ) 97 | 98 | # Stuff for building the main binary 99 | add_executable(tcmu-runner 100 | tcmur_work.c 101 | tcmur_cmd_handler.c 102 | tcmur_aio.c 103 | tcmur_device.c 104 | target.c 105 | alua.c 106 | scsi.c 107 | main.c 108 | tcmuhandler-generated.c 109 | ) 110 | target_link_libraries(tcmu-runner tcmu) 111 | target_include_directories(tcmu-runner 112 | PUBLIC ${PROJECT_BINARY_DIR} 113 | PUBLIC ${GLIB_INCLUDE_DIRS} 114 | PUBLIC ${KMOD_INCLUDE_DIRS} 115 | PUBLIC ${PROJECT_SOURCE_DIR}/ccan 116 | ) 117 | target_link_libraries(tcmu-runner 118 | ${GLIB_LIBRARIES} 119 | ${PTHREAD} 120 | ${DL} 121 | ${KMOD_LIBRARIES} 122 | ${TCMALLOC_LIB} 123 | -Wl,--no-export-dynamic 124 | -Wl,--dynamic-list=${CMAKE_SOURCE_DIR}/main-syms.txt 125 | ) 126 | install(TARGETS tcmu-runner RUNTIME DESTINATION bin) 127 | 128 | add_executable(tcmu-synthesizer 129 | scsi.c 130 | tcmu-synthesizer.c 131 | ) 132 | target_link_libraries(tcmu-synthesizer tcmu) 133 | target_include_directories(tcmu-synthesizer 134 | PUBLIC ${PROJECT_BINARY_DIR} 135 | PUBLIC ${GLIB_INCLUDE_DIRS} 136 | PUBLIC ${PROJECT_SOURCE_DIR}/ccan 137 | ) 138 | target_link_libraries(tcmu-synthesizer 139 | ${GLIB_LIBRARIES} 140 | ${TCMALLOC_LIB} 141 | ) 142 | 143 | install(TARGETS RUNTIME DESTINATION bin) 144 | 145 | add_custom_command( 146 | OUTPUT ${CMAKE_SOURCE_DIR}/tcmuhandler-generated.c ${CMAKE_SOURCE_DIR}/tcmuhandler-generated.h 147 | COMMAND gdbus-codegen ${CMAKE_SOURCE_DIR}/tcmu-handler.xml --generate-c-code ${CMAKE_SOURCE_DIR}/tcmuhandler-generated --c-generate-object-manager --interface-prefix org.kernel 148 | MAIN_DEPENDENCY tcmu-handler.xml 149 | ) 150 | 151 | add_custom_target( 152 | cscope 153 | COMMAND find -name '*.[ch]' > cscope.files 154 | COMMAND cscope -bq 155 | ) 156 | set_directory_properties( 157 | PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES 158 | "cscope.files;cscope.in.out;cscope.out;cscope.po.out" 159 | ) 160 | 161 | # Stuff for building the file handler 162 | add_library(handler_file 163 | SHARED 164 | file_example.c 165 | ) 166 | set_target_properties(handler_file 167 | PROPERTIES 168 | PREFIX "" 169 | ) 170 | target_include_directories(handler_file 171 | PUBLIC ${PROJECT_SOURCE_DIR}/ccan 172 | ) 173 | 174 | if (with-fbo) 175 | # Stuff for building the file optical handler 176 | add_library(handler_file_optical 177 | SHARED 178 | scsi.c 179 | file_optical.c 180 | ) 181 | 182 | set_target_properties(handler_file_optical 183 | PROPERTIES 184 | PREFIX "" 185 | ) 186 | 187 | target_include_directories(handler_file_optical 188 | PUBLIC ${PROJECT_SOURCE_DIR}/ccan 189 | ) 190 | target_link_libraries(handler_file_optical ${PTHREAD} ${TCMALLOC_LIB}) 191 | install(TARGETS handler_file_optical DESTINATION ${CMAKE_INSTALL_LIBDIR}/tcmu-runner) 192 | endif (with-fbo) 193 | 194 | # The minimal library consumer 195 | add_executable(consumer 196 | scsi.c 197 | consumer.c 198 | ) 199 | target_link_libraries(consumer tcmu) 200 | 201 | if (with-zbc) 202 | # Stuff for building the file zbc handler 203 | add_library(handler_file_zbc 204 | SHARED 205 | scsi.c 206 | file_zbc.c 207 | ) 208 | set_target_properties(handler_file_zbc 209 | PROPERTIES 210 | PREFIX "" 211 | ) 212 | target_include_directories(handler_file_zbc 213 | PUBLIC ${PROJECT_SOURCE_DIR}/ccan 214 | ) 215 | target_link_libraries(handler_file_zbc ${TCMALLOC_LIB}) 216 | install(TARGETS handler_file_zbc DESTINATION ${CMAKE_INSTALL_LIBDIR}/tcmu-runner) 217 | endif (with-zbc) 218 | 219 | if (with-rbd) 220 | find_library(LIBRBD rbd) 221 | 222 | # Stuff for building the rbd handler 223 | add_library(handler_rbd 224 | SHARED 225 | rbd.c 226 | ) 227 | set_target_properties(handler_rbd 228 | PROPERTIES 229 | PREFIX "" 230 | ) 231 | target_include_directories(handler_rbd 232 | PUBLIC ${PROJECT_SOURCE_DIR}/ccan 233 | ) 234 | target_link_libraries(handler_rbd 235 | ${LIBRBD} 236 | ${TCMALLOC_LIB} 237 | ) 238 | install(TARGETS handler_rbd DESTINATION ${CMAKE_INSTALL_LIBDIR}/tcmu-runner) 239 | endif (with-rbd) 240 | 241 | if (with-glfs) 242 | find_library(GFAPI gfapi) 243 | 244 | set(GFAPI_VERSION760 0) 245 | 246 | pkg_check_modules(GFAPI760 glusterfs-api>=7.6 QUIET) 247 | if (GFAPI760_FOUND) 248 | set(GFAPI_VERSION760 1) 249 | endif (GFAPI760_FOUND) 250 | 251 | set(GFAPI_VERSION766 0) 252 | 253 | pkg_check_modules(GFAPI766 glusterfs-api>=7.6.6 QUIET) 254 | if (GFAPI766_FOUND) 255 | set(GFAPI_VERSION766 1) 256 | endif (GFAPI766_FOUND) 257 | 258 | # Stuff for building the glfs handler 259 | add_library(handler_glfs 260 | SHARED 261 | glfs.c 262 | ) 263 | set_target_properties(handler_glfs 264 | PROPERTIES 265 | PREFIX "" 266 | ) 267 | target_include_directories(handler_glfs 268 | PUBLIC ${PROJECT_SOURCE_DIR}/ccan 269 | ) 270 | target_link_libraries(handler_glfs 271 | ${GFAPI} 272 | ${TCMALLOC_LIB} 273 | ) 274 | install(TARGETS handler_glfs DESTINATION ${CMAKE_INSTALL_LIBDIR}/tcmu-runner) 275 | endif (with-glfs) 276 | 277 | if (with-qcow) 278 | find_package(ZLIB REQUIRED) 279 | 280 | # Stuff for building the qcow handler 281 | add_library(handler_qcow 282 | SHARED 283 | qcow.c 284 | ) 285 | set_target_properties(handler_qcow 286 | PROPERTIES 287 | PREFIX "" 288 | ) 289 | target_include_directories(handler_qcow 290 | PUBLIC ${PROJECT_SOURCE_DIR}/ccan 291 | ) 292 | 293 | CHECK_INCLUDE_FILE("linux/falloc.h" HAVE_LINUX_FALLOC) 294 | if (HAVE_LINUX_FALLOC) 295 | set_target_properties(handler_qcow 296 | PROPERTIES 297 | COMPILE_FLAGS "-DHAVE_LINUX_FALLOC" 298 | ) 299 | endif (HAVE_LINUX_FALLOC) 300 | target_link_libraries(handler_qcow 301 | ${ZLIB_LIBRARIES} 302 | ${TCMALLOC_LIB} 303 | ) 304 | install(TARGETS handler_qcow DESTINATION ${CMAKE_INSTALL_LIBDIR}/tcmu-runner) 305 | endif (with-qcow) 306 | 307 | # stamp out a header file to pass some of the CMake settings 308 | # to the source code 309 | configure_file ( 310 | "${PROJECT_SOURCE_DIR}/version.h.in" 311 | "${PROJECT_SOURCE_DIR}/version.h" 312 | ) 313 | 314 | configure_file ( 315 | "${PROJECT_SOURCE_DIR}/tcmu.conf_install.cmake.in" 316 | "${PROJECT_SOURCE_DIR}/tcmu.conf_install.cmake" 317 | ) 318 | install(SCRIPT tcmu.conf_install.cmake) 319 | 320 | configure_file ( 321 | "${PROJECT_SOURCE_DIR}/logrotate.conf_install.cmake.in" 322 | "${PROJECT_SOURCE_DIR}/logrotate.conf_install.cmake" 323 | ) 324 | install(SCRIPT logrotate.conf_install.cmake) 325 | 326 | install(FILES org.kernel.TCMUService1.service 327 | DESTINATION /usr/share/dbus-1/system-services) 328 | install(FILES tcmu-runner.conf DESTINATION /etc/dbus-1/system.d) 329 | if (SUPPORT_SYSTEMD) 330 | install(FILES tcmu-runner.service DESTINATION /usr/lib/systemd/system/) 331 | endif (SUPPORT_SYSTEMD) 332 | install(FILES tcmu-runner.8 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man8) 333 | -------------------------------------------------------------------------------- /LICENSE.Apache2: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tcmu-runner 2 | 3 | A daemon that handles the userspace side of the LIO TCM-User backstore. 4 | 5 | ## Background 6 | 7 | [LIO](http://linux-iscsi.org/wiki/Main_Page) is the [SCSI](http://en.wikipedia.org/wiki/SCSI) [target](http://en.wikipedia.org/wiki/SCSI_initiator_and_target) in the [Linux kernel](http://kernel.org). It is entirely kernel code, and allows exported SCSI [logical units (LUNs)](http://en.wikipedia.org/wiki/Logical_unit_number) to be backed by regular files or block devices. But, if we want to get fancier with the capabilities of the device we're emulating, the kernel is not necessarily the right place. While there are userspace libraries for compression, encryption, and clustered storage solutions like [Ceph](http://ceph.com/) or [Gluster](http://www.gluster.org/), these are not accessible from the kernel. 8 | 9 | The TCMU userspace-passthrough backstore allows a userspace process to handle requests to a LUN. But since the kernel-user interface that TCMU provides must be fast and flexible, it is complex enough that we'd like to avoid each userspace handler having to write boilerplate code. 10 | 11 | **tcmu-runner** handles the messy details of the TCMU interface -- UIO, netlink, pthreads, and DBus -- and exports a more friendly C plugin module API. Modules using this API are called "TCMU handlers". Handler authors can write code just to handle the SCSI commands as desired, and can also link with whatever userspace libraries they like. 12 | 13 | ## Usage example 14 | 15 | One goal of TCMU is that configuring a userspace-backed LUN should be as easy as configuring a kernel-backed LUN. We're not quite there yet. This will require cooperation with the LIO configuration tool, `targetcli`. `targetcli` should list user-backed backstores along with the built-in kernel backstores, and ensure tcmu-runner is started if a user-backed backstore is created. 16 | 17 | ## Info for potential contributors and handler authors 18 | 19 | ### License 20 | 21 | tcmu-runner is LGPLv2.1 or Apache License 2.0. 22 | 23 | ### Releases 24 | 25 | Tarballs are available from https://github.com/open-iscsi/tcmu-runner/releases. 26 | 27 | ### Development 28 | 29 | We encourage pull requests and issues tracking via Github, and the [target-devel mailing list](mailto:target-devel@vger.kernel.org) ([list info](http://vger.kernel.org/vger-lists.html#target-devel)) may be used for discussion. 30 | 31 | ### Getting started 32 | 33 | ##### Building tcmu-runner 34 | 35 | 1. Clone this repo. 36 | 1. Type `./extra/install_dep.sh` to install development packages for dependencies, or you can do it manually: 37 | * *Note:* Install cmake and other packages which usually ending with "-devel" or "-dev": libnl3, libglib2 (or glib2-devel on Fedora), libpthread, libdl, libkmod, libgfapi (Gluster), librbd1 (Ceph), zlib. 38 | 1. Type `cd ./extra && ./make_runnerrpms.sh [--without (rbd|glfs|qcow|zbc|fbo)]` to build the RPM packages automatically. 39 | 1. Type `cmake . [-Dwith-=false]` 40 | * *Note:* If using systemd, `-DSUPPORT_SYSTEMD=ON -DCMAKE_INSTALL_PREFIX=/usr` should be passed to cmake, so files are installed to the correct location. 41 | 1. Type `make` 42 | 1. Type `make install` 43 | 1. Type `xargs rm < install_manifest.txt` to uninstall from source 44 | 45 | 46 | ##### Running tcmu-runner 47 | 48 | 1. Copy `tcmu-runner.conf` to `/etc/dbus-1/system.d/`. This allows tcmu-runner to be on the system bus, which is privileged. 49 | 1. If using systemd, copy `org.kernel.TCMUService1.service` to `/usr/share/dbus-1/system-services/` and `tcmu-runner.service` to `/lib/systemd/system`. 50 | 1. Or, run it from the command line as root. It should print the number of handlers and devices found. 51 | 52 | 53 | ##### Creating a LIO user-backed storage object with backstore specific tools 54 | 55 | - Ceph: 56 | 57 | If setting up tcmu-runner in a HA configuration, the ceph-iscsi-cli 58 | (https://github.com/ceph/ceph-iscsi-cli) tool is the preferred management 59 | tool. 60 | 61 | Bug reports should be made to the tcmu-runner github: 62 | https://github.com/open-iscsi/tcmu-runner/issues, but can be made to 63 | ceph-users@ceph.com mailing list. 64 | 65 | - Gluster: 66 | 67 | Gluster management must be done with the gluster-block tools 68 | (https://github.com/gluster/gluster-block). 69 | 70 | Bug reports must be made to the gluster-block github: 71 | https://github.com/gluster/gluster-block/issues 72 | 73 | ##### Creating a LIO user-backed storage object with targetcli-fb or configfs 74 | 75 | Support for the user/tcmu backstore is supported in targetcli-fb/rtslib-fb: 76 | 77 | https://github.com/open-iscsi/targetcli-fb 78 | https://github.com/open-iscsi/rtslib-fb 79 | 80 | 1. Start targetcli 81 | 82 | \# targetcli 83 | 84 | 2. Go to the user/tcmu backstore dir. 85 | 86 | /> cd /backstores/ 87 | 88 | 3. By default, tcmu-runner installs the file, zbc, glfs, qcow and rbd tcmu-runner handlers: 89 | 90 | ``` 91 | /backstores> ls 92 | o- backstores .......................................................... [...] 93 | o- user:glfs .......................................... [Storage Objects: 0] 94 | o- user:qcow .......................................... [Storage Objects: 0] 95 | o- user:rbd ........................................... [Storage Objects: 0] 96 | o- user:file .......................................... [Storage Objects: 0] 97 | o- user:zbc ........................................... [Storage Objects: 0] 98 | ``` 99 | 100 | 4. 'cd' to the handler you want to setup: 101 | 102 | /backstores> cd user:rbd 103 | 104 | /backstores/user:rbd> create cfgstring=pool/rbd1;osd_op_timeout=30 name=rbd0 size=1G 105 | Created user-backed storage object rbd0 size 1073741824. 106 | 107 | The cfgstrng format is: 108 | 109 | cfgstring=;tcmu-runner daemon arguments;handler arguments 110 | 111 | The following tcmu-runner daemon arguments are optional: 112 | 113 | - tcmur_cmd_time_out: Number of seconds before logging the command as timed out, 114 | and executing a handler specific timeout handler if supported. 115 | 116 | If passed in they must start before the handler specific arguments and each 117 | argument must start and end with a semicolon ";". 118 | 119 | Example using the rbd cfgstring with the optional tcmur_cmd_timeout arg: 120 | 121 | create ;tcmur_cmd_timeout=20;pool/rbd1;osd_op_timeout=30 name=rbd0 size=1G 122 | 123 | The handler specific arguments and their formats are: 124 | 125 | - **rbd**: /pool_name/image_name[;osd_op_timeout=N;conf=N;id=N] 126 | (osd_op_timeout is optional and N is in seconds) 127 | (conf is optional and N is the path to the conf file) 128 | (id is optional and N is the id to connect to the cluster as) 129 | - **qcow**: /path_to_file 130 | - **glfs**: /volume@hostname/filename 131 | - **file**: /path_to_file 132 | - **zbc**: /[opt1[/opt2][...]@]path_to_file 133 | 134 | For the zbc handler, the available options are shown in the table below. 135 | 136 | | Option | Description | Default value | 137 | | --- | --- | --- | 138 | | model-**_type_** | Device model type, _HA_ for host aware or _HM_ for host managed | _HM_ 139 | | lba-**_size (B)_** | LBA size in bytes (512 or 4096) | 512 140 | | zsize-**_size (MiB)_** | Zone size in MiB | 256 MiB 141 | | conv-**_num_** | Number of conventional zones at LBA 0 (can be 0) | Number of zones corresponding to 1% of the device capacity 142 | | open-**_num_** | Optimal (for host aware) or maximum (for host managed) number of open zones | 128 143 | 144 | Example: 145 | ``` 146 | cfgstring=model-HM/zsize-128/conv-100@/var/local/zbc.raw 147 | ``` 148 | 149 | will create a host-managed disk with 128 MiB zones and 100 conventional zones, 150 | stored in the file /var/local/zbc.raw. 151 | 152 | 5. The created backstore device can then be mapped to a LUN like traditional 153 | backstores. 154 | 155 | ##### Logger setting and system configuration 156 | 157 | - Logger setting: 158 | 159 | # There are 6 logging levels supported: 160 | 0. CRIT 161 | 1. ERROR 162 | 2. WARNING 163 | 3. INFO 164 | 4. DEBUG 165 | 5. DEBUG SCSI CMD 166 | 167 | And the default logging level is 3, if you want to change the default level, 168 | uncomment the following line in /etc/tcmu/tcmu.conf and set your level number: 169 | 170 | \# log_level = 3 171 | 172 | The precedence of the config settings is as mentioned belows: 173 | 1. Options set through config file /etc/tcmu/tcmu.conf [Top Prio] 174 |
eg: uncommenting and adjusting key:value at /etc/tcmu/tcmu.conf 175 | 2. Arguments passed at daemon 176 |
eg: -l/--tcmu-log-dir, -d/--debug 177 | 3. Environment variable. 178 |
eg: export TCMU_LOGDIR="/var/log/mylogdir/" 179 | 4. Code level defaults. [Least Prio] 180 |
eg: TCMU_LOG_DIR_DEFAULT = '/var/log/' & TCMU_CONF_LOG_INFO = INFO 181 | 182 | - System configuration: 183 | 184 | The default configuration file is installed into /etc/tcmu/tcmu.conf. 185 | 186 | Tcmu-runner's configuration systems supports dynamic reloading without restarting 187 | the daemon. To change values open /etc/tcmu/tcmu.conf, update the value, and then 188 | close the file. 189 | 190 | ------------------------------ 191 | 192 | If your version of targetcli/rtslib does not support tcmu, setup can be done 193 | manually through configfs: 194 | 195 | 1. Ensure `target_core_user` kernel module is loaded. 196 | 2. Create the HBA (user_1) and the storage object (test): `mkdir -p /sys/kernel/config/target/core/user_1/test` 197 | 3. Go to that directory: `cd /sys/kernel/config/target/core/user_1/test` 198 | 4. Set configuration values 199 | 1. Set size (in bytes): `echo -n dev_size=16777216 > control` 200 | 3. Set configstring. See [tcmu-design.txt](https://github.com/torvalds/linux/blob/master/Documentation/target/tcmu-design.txt#L177), but note that the TCMU backstore driver already knows and will prepend the "tcm-user/hba_num/device_name" part. Therefore, if we wanted our new device to be handled by the "baz" handler, we would give subtype and path by running: `echo -n dev_config=baz/addl_info_for_baz_handler > control` 201 | 4. Enable the storage object: `echo -n 1 > enable` 202 | 5. Verify everything worked. There should be an entry in `/sys/class/uio`. 203 | 204 | To delete: 205 | 206 | 1. `rmdir /sys/kernel/config/target/core/user_1/test` 207 | 2. `rmdir /sys/kernel/config/target/core/user_1` 208 | 209 | ------------------------------ 210 | 211 | ### Writing a TCMU handler 212 | 213 | #### libtcmu and tcmu-runner 214 | 215 | There are two different ways to write a TCMU handler. The primary 216 | difference is who is responsible for the event loop. 217 | 218 | ##### tcmu-runner plugin handler 219 | 220 | There are two different ways to write a tcmu-runner plugin handler: 221 | 222 | 1. one can register .handle_cmd to take the full control of command handling 223 | 2. or else one can register .{read, write, flush, ...} to handle specific 224 | operations and .handle_cmd to override the generic handling if needed. 225 | 226 | With the option 1, tcmu-runner is in charge of the event loop 227 | for your plugin, and your handler's `handle_cmd` function is called 228 | repeatedly to respond to each incoming SCSI command. While your 229 | handler sees all SCSI commands, there are helper functions provided 230 | that save each handler from writing boilerplate code for mandatory 231 | SCSI commands, if desired. 232 | 233 | The `file_optical` handler is an examples of this type. 234 | 235 | With the option 2, tcmu-runner will be partially or fully in charge of the event 236 | loop and SCSI command handling for your plugin, and your handler's registered 237 | functions are called repeatedly to handle storage requests as required by the 238 | upper SCSI layer, which will handle most of the SCSI commands for you. 239 | 240 | * *Note:* If the .handle_cmd is also implemented by the handler, tcmu-runner 241 | will try to pass through the commands to the handler first. If and only when 242 | the handler won't support the commands it should return TCMU_STS_NOT_HANDLED, 243 | then the tcmu-runner will handle them in the generic handler. 244 | 245 | The `file_example` handler is an example of this type. 246 | 247 | ##### tcmulib 248 | 249 | If you want to add handling of TCMU devices to an existing daemon or 250 | other program that already is processing its own event loop, the best 251 | option is to use tcmulib directly. This requires your code to keep 252 | track of tcmulib's file descriptors. While tcmulib's 'master' file 253 | descriptor must be handled with `tcmulib_master_fd_ready()` 254 | single-threadedly, per-device fds can be handled on the main thread 255 | (with `tcmulib_get_next_command` and `tcmulib_command_complete`) or 256 | separate threads if desired. SCSI command-processing helper functions 257 | are still available for use. 258 | 259 | `tcmu-runner` itself uses tcmulib in this manner and may be used as an 260 | example of multi-threaded tcmulib use. The `consumer.c` example 261 | demonstrates single-threaded tcmulib processing. 262 | -------------------------------------------------------------------------------- /alua.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #ifndef __TCMU_ALUA_H 10 | #define __TCMU_ALUA_H 11 | 12 | #include "ccan/list/list.h" 13 | 14 | struct tcmu_device; 15 | struct tcmulibc_cmd; 16 | 17 | struct alua_grp { 18 | /* ALUA spec values */ 19 | uint8_t state; 20 | uint8_t supported_states; 21 | uint8_t tpgs; 22 | uint8_t status; 23 | uint8_t implicit_trans_secs; 24 | bool pref; 25 | uint16_t id; 26 | 27 | /* LIO settings */ 28 | char *name; 29 | unsigned nonop_delay_msecs; 30 | unsigned trans_delay_msecs; 31 | 32 | struct tcmu_device *dev; 33 | uint8_t num_tgt_ports; 34 | /* entry on list returned by lib */ 35 | struct list_node entry; 36 | struct list_head tgt_ports; 37 | }; 38 | 39 | int tcmu_emulate_report_tgt_port_grps(struct tcmu_device *dev, 40 | struct list_head *group_list, 41 | struct tcmulib_cmd *cmd); 42 | int tcmu_emulate_set_tgt_port_grps(struct tcmu_device *dev, 43 | struct list_head *group_list, 44 | struct tcmulib_cmd *cmd); 45 | struct tgt_port *tcmu_get_enabled_port(struct list_head *); 46 | int tcmu_get_alua_grps(struct tcmu_device *, struct list_head *); 47 | void tcmu_release_alua_grps(struct list_head *); 48 | int alua_implicit_transition(struct tcmu_device *dev, struct tcmulib_cmd *cmd, 49 | bool is_read); 50 | bool lock_is_required(struct tcmu_device *dev); 51 | int alua_check_state(struct tcmu_device *dev, struct tcmulib_cmd *cmd, bool is_read); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /api.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #define _GNU_SOURCE 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "libtcmu_log.h" 27 | #include "libtcmu_common.h" 28 | #include "libtcmu_priv.h" 29 | #include "be_byteshift.h" 30 | 31 | __thread int __tcmu_is_ework_thread = 0; 32 | 33 | int tcmu_cdb_get_length(uint8_t *cdb) 34 | { 35 | uint8_t group_code = cdb[0] >> 5; 36 | 37 | /* See spc-4 4.2.5.1 operation code */ 38 | switch (group_code) { 39 | case 0: /*000b for 6 bytes commands */ 40 | return 6; 41 | case 1: /*001b for 10 bytes commands */ 42 | case 2: /*010b for 10 bytes commands */ 43 | return 10; 44 | case 3: /*011b Reserved ? */ 45 | if (cdb[0] == 0x7f) 46 | return 8 + cdb[7]; 47 | goto cdb_not_supp; 48 | case 4: /*100b for 16 bytes commands */ 49 | return 16; 50 | case 5: /*101b for 12 bytes commands */ 51 | return 12; 52 | case 6: /*110b Vendor Specific */ 53 | case 7: /*111b Vendor Specific */ 54 | default: 55 | /* TODO: */ 56 | goto cdb_not_supp; 57 | } 58 | 59 | cdb_not_supp: 60 | tcmu_err("CDB %x0x not supported.\n", cdb[0]); 61 | return -EINVAL; 62 | } 63 | 64 | uint64_t tcmu_cdb_get_lba(uint8_t *cdb) 65 | { 66 | uint16_t val; 67 | 68 | switch (tcmu_cdb_get_length(cdb)) { 69 | case 6: 70 | val = get_unaligned_be16(&cdb[2]); 71 | return ((cdb[1] & 0x1f) << 16) | val; 72 | case 10: 73 | return get_unaligned_be32(&cdb[2]); 74 | case 12: 75 | return get_unaligned_be32(&cdb[2]); 76 | case 16: 77 | return get_unaligned_be64(&cdb[2]); 78 | default: 79 | assert(0); 80 | return 0; /* not reached */ 81 | } 82 | } 83 | 84 | uint32_t tcmu_cdb_get_xfer_length(uint8_t *cdb) 85 | { 86 | switch (tcmu_cdb_get_length(cdb)) { 87 | case 6: 88 | return cdb[4]; 89 | case 10: 90 | return get_unaligned_be16(&cdb[7]); 91 | case 12: 92 | return get_unaligned_be32(&cdb[6]); 93 | case 16: 94 | return get_unaligned_be32(&cdb[10]); 95 | default: 96 | assert(0); 97 | return 0; /* not reached */ 98 | } 99 | } 100 | 101 | /* 102 | * Returns location of first mismatch between bytes in mem and the iovec. 103 | * If they are the same, return -1. 104 | */ 105 | off_t tcmu_iovec_compare(void *mem, struct iovec *iovec, size_t size) 106 | { 107 | off_t mem_off; 108 | int ret; 109 | 110 | mem_off = 0; 111 | while (size) { 112 | size_t part = min(size, iovec->iov_len); 113 | 114 | ret = memcmp(mem + mem_off, iovec->iov_base, part); 115 | if (ret) { 116 | size_t pos; 117 | char *spos = mem + mem_off; 118 | char *dpos = iovec->iov_base; 119 | 120 | /* 121 | * Data differed, this is assumed to be 'rare' 122 | * so use a much more expensive byte-by-byte 123 | * comparison to find out at which offset the 124 | * data differs. 125 | */ 126 | for (pos = 0; pos < part && *spos++ == *dpos++; 127 | pos++) 128 | ; 129 | 130 | return pos + mem_off; 131 | } 132 | 133 | size -= part; 134 | mem_off += part; 135 | iovec++; 136 | } 137 | 138 | return -1; 139 | } 140 | 141 | /* 142 | * Consume an iovec. Count must not exceed the total iovec[] size. 143 | */ 144 | size_t tcmu_iovec_seek(struct iovec *iovec, size_t count) 145 | { 146 | size_t consumed = 0; 147 | 148 | while (count) { 149 | if (count >= iovec->iov_len) { 150 | count -= iovec->iov_len; 151 | iovec->iov_len = 0; 152 | iovec++; 153 | consumed++; 154 | } else { 155 | iovec->iov_base += count; 156 | iovec->iov_len -= count; 157 | count = 0; 158 | } 159 | } 160 | 161 | return consumed; 162 | } 163 | 164 | /* 165 | * Consume an iovec. Count must not exceed the total iovec[] size. 166 | * iove count should be updated. 167 | */ 168 | void tcmu_cmd_seek(struct tcmulib_cmd *cmd, size_t count) 169 | { 170 | cmd->iov_cnt -= tcmu_iovec_seek(cmd->iovec, count); 171 | } 172 | 173 | size_t tcmu_iovec_length(struct iovec *iovec, size_t iov_cnt) 174 | { 175 | size_t length = 0; 176 | 177 | while (iov_cnt) { 178 | length += iovec->iov_len; 179 | iovec++; 180 | iov_cnt--; 181 | } 182 | 183 | return length; 184 | } 185 | 186 | void __tcmu_sense_set_data(uint8_t *sense_buf, uint8_t key, uint16_t asc_ascq) 187 | { 188 | sense_buf[0] |= 0x70; /* fixed, current */ 189 | sense_buf[2] = key; 190 | sense_buf[7] = 0xa; 191 | sense_buf[12] = (asc_ascq >> 8) & 0xff; 192 | sense_buf[13] = asc_ascq & 0xff; 193 | } 194 | 195 | int tcmu_sense_set_data(uint8_t *sense_buf, uint8_t key, uint16_t asc_ascq) 196 | { 197 | memset(sense_buf, 0, SENSE_BUFFERSIZE); 198 | __tcmu_sense_set_data(sense_buf, key, asc_ascq); 199 | return TCMU_STS_PASSTHROUGH_ERR; 200 | } 201 | 202 | void tcmu_sense_set_key_specific_info(uint8_t *sense_buf, uint16_t info) 203 | { 204 | memset(sense_buf, 0, 18); 205 | 206 | put_unaligned_be16(info, &sense_buf[16]); 207 | /* Set SKSV bit */ 208 | sense_buf[15] |= 0x80; 209 | } 210 | 211 | void tcmu_sense_set_info(uint8_t *sense_buf, uint32_t info) 212 | { 213 | memset(sense_buf, 0, 18); 214 | 215 | put_unaligned_be32(info, &sense_buf[3]); 216 | /* Set VALID bit */ 217 | sense_buf[0] |= 0x80; 218 | } 219 | 220 | /* 221 | * Zero iovec. 222 | */ 223 | void tcmu_iovec_zero(struct iovec *iovec, size_t iov_cnt) 224 | { 225 | while (iov_cnt) { 226 | bzero(iovec->iov_base, iovec->iov_len); 227 | 228 | iovec++; 229 | iov_cnt--; 230 | } 231 | } 232 | 233 | static inline bool tcmu_zeroed_mem(const char *buf, size_t size) 234 | { 235 | int i; 236 | 237 | for (i = 0; i < size; i++) { 238 | if (buf[i]) 239 | return false; 240 | } 241 | 242 | return true; 243 | } 244 | 245 | bool tcmu_iovec_zeroed(struct iovec *iovec, size_t iov_cnt) 246 | { 247 | int i; 248 | 249 | for (i = 0; i < iov_cnt; i++) { 250 | if (!tcmu_zeroed_mem(iovec[i].iov_base, iovec[i].iov_len)) 251 | return false; 252 | } 253 | 254 | return true; 255 | } 256 | 257 | /* 258 | * Copy data into an iovec, and consume the space in the iovec. 259 | * 260 | * Will truncate instead of overrunning the iovec. 261 | */ 262 | size_t tcmu_memcpy_into_iovec( 263 | struct iovec *iovec, 264 | size_t iov_cnt, 265 | void *src, 266 | size_t len) 267 | { 268 | size_t copied = 0; 269 | 270 | while (len && iov_cnt) { 271 | size_t to_copy = min(iovec->iov_len, len); 272 | 273 | if (to_copy) { 274 | memcpy(iovec->iov_base, src + copied, to_copy); 275 | 276 | len -= to_copy; 277 | copied += to_copy; 278 | iovec->iov_base += to_copy; 279 | iovec->iov_len -= to_copy; 280 | } 281 | 282 | iovec++; 283 | iov_cnt--; 284 | } 285 | 286 | return copied; 287 | } 288 | 289 | /* 290 | * Copy data from an iovec, and consume the space in the iovec. 291 | */ 292 | size_t tcmu_memcpy_from_iovec( 293 | void *dest, 294 | size_t len, 295 | struct iovec *iovec, 296 | size_t iov_cnt) 297 | { 298 | size_t copied = 0; 299 | 300 | while (len && iov_cnt) { 301 | size_t to_copy = min(iovec->iov_len, len); 302 | 303 | if (to_copy) { 304 | memcpy(dest + copied, iovec->iov_base, to_copy); 305 | 306 | len -= to_copy; 307 | copied += to_copy; 308 | iovec->iov_base += to_copy; 309 | iovec->iov_len -= to_copy; 310 | } 311 | 312 | iovec++; 313 | iov_cnt--; 314 | } 315 | 316 | return copied; 317 | } 318 | 319 | #define CDB_TO_BUF_SIZE(bytes) ((bytes) * 3 + 2) 320 | #define CDB_FIX_BYTES 64 /* 64 bytes for default */ 321 | #define CDB_FIX_SIZE CDB_TO_BUF_SIZE(CDB_FIX_BYTES) 322 | void tcmu_cdb_print_info(struct tcmu_device *dev, 323 | const struct tcmulib_cmd *cmd, 324 | const char *info) 325 | { 326 | int i, n, bytes, info_len = 0; 327 | char fix[CDB_FIX_SIZE], *buf; 328 | 329 | buf = fix; 330 | 331 | bytes = tcmu_cdb_get_length(cmd->cdb); 332 | if (bytes < 0) 333 | return; 334 | 335 | if (info) 336 | info_len = strlen(info); 337 | 338 | if (CDB_TO_BUF_SIZE(bytes) + info_len > CDB_FIX_SIZE) { 339 | buf = malloc(CDB_TO_BUF_SIZE(bytes) + info_len); 340 | if (!buf) { 341 | tcmu_dev_err(dev, "out of memory\n"); 342 | return; 343 | } 344 | } 345 | 346 | for (i = 0, n = 0; i < bytes; i++) { 347 | n += sprintf(buf + n, "%x ", cmd->cdb[i]); 348 | } 349 | 350 | if (info) 351 | n += sprintf(buf + n, "%s", info); 352 | 353 | sprintf(buf + n, "\n"); 354 | 355 | if (info) { 356 | tcmu_dev_dbg(dev, "%s", buf); 357 | } else { 358 | tcmu_dev_dbg_scsi_cmd(dev, "%s", buf); 359 | } 360 | 361 | if (buf != fix) 362 | free(buf); 363 | } 364 | 365 | void tcmu_thread_cancel(pthread_t thread) 366 | { 367 | void *join_retval; 368 | int ret; 369 | 370 | ret = pthread_cancel(thread); 371 | if (ret) { 372 | tcmu_err("pthread_cancel failed with value %d\n", ret); 373 | return; 374 | } 375 | 376 | ret = pthread_join(thread, &join_retval); 377 | if (ret) { 378 | tcmu_err("pthread_join failed with value %d\n", ret); 379 | return; 380 | } 381 | 382 | if (join_retval != PTHREAD_CANCELED) 383 | tcmu_err("unexpected join retval: %p\n", join_retval); 384 | } 385 | -------------------------------------------------------------------------------- /be_byteshift.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #ifndef _TCMU_BE_BYTESHIFT_H 10 | #define _TCMU_BE_BYTESHIFT_H 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | static inline void __put_unaligned_be32(uint32_t val, uint8_t *p) 18 | { 19 | *p++ = val >> 24; 20 | *p++ = val >> 16; 21 | *p++ = val >> 8; 22 | *p++ = val; 23 | } 24 | 25 | static inline void put_unaligned_be32(uint32_t val, void *p) 26 | { 27 | __put_unaligned_be32(val, p); 28 | } 29 | 30 | static inline void __put_unaligned_be16(uint16_t val, uint8_t *p) 31 | { 32 | *p++ = val >> 8; 33 | *p++ = val; 34 | } 35 | 36 | static inline void put_unaligned_be16(uint16_t val, void *p) 37 | { 38 | __put_unaligned_be16(val, p); 39 | } 40 | 41 | static inline uint16_t __get_unaligned_be16(const uint8_t *p) 42 | { 43 | return p[0] << 8 | p[1]; 44 | } 45 | 46 | static inline uint16_t get_unaligned_be16(const void *p) 47 | { 48 | return __get_unaligned_be16(p); 49 | } 50 | 51 | static inline uint32_t __get_unaligned_be32(const uint8_t *p) 52 | { 53 | return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; 54 | } 55 | 56 | static inline uint32_t get_unaligned_be32(const void *p) 57 | { 58 | return __get_unaligned_be32(p); 59 | } 60 | 61 | static inline uint64_t get_unaligned_be64(const void *p) 62 | { 63 | uint64_t val; 64 | memcpy(&val, p, sizeof(val)); 65 | return be64toh(val); 66 | } 67 | 68 | static inline void put_unaligned_be64(uint64_t val, void *p) 69 | { 70 | val = htobe64(val); 71 | memcpy(p, &val, sizeof(val)); 72 | } 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /ccan/ccan/build_assert/build_assert.h: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #ifndef CCAN_BUILD_ASSERT_H 3 | #define CCAN_BUILD_ASSERT_H 4 | 5 | /** 6 | * BUILD_ASSERT - assert a build-time dependency. 7 | * @cond: the compile-time condition which must be true. 8 | * 9 | * Your compile will fail if the condition isn't true, or can't be evaluated 10 | * by the compiler. This can only be used within a function. 11 | * 12 | * Example: 13 | * #include 14 | * ... 15 | * static char *foo_to_char(struct foo *foo) 16 | * { 17 | * // This code needs string to be at start of foo. 18 | * BUILD_ASSERT(offsetof(struct foo, string) == 0); 19 | * return (char *)foo; 20 | * } 21 | */ 22 | #define BUILD_ASSERT(cond) \ 23 | do { (void) sizeof(char [1 - 2*!(cond)]); } while(0) 24 | 25 | /** 26 | * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression. 27 | * @cond: the compile-time condition which must be true. 28 | * 29 | * Your compile will fail if the condition isn't true, or can't be evaluated 30 | * by the compiler. This can be used in an expression: its value is "0". 31 | * 32 | * Example: 33 | * #define foo_to_char(foo) \ 34 | * ((char *)(foo) \ 35 | * + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0)) 36 | */ 37 | #define BUILD_ASSERT_OR_ZERO(cond) \ 38 | (sizeof(char [1 - 2*!(cond)]) - 1) 39 | 40 | #endif /* CCAN_BUILD_ASSERT_H */ 41 | -------------------------------------------------------------------------------- /ccan/ccan/check_type/check_type.h: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #ifndef CCAN_CHECK_TYPE_H 3 | #define CCAN_CHECK_TYPE_H 4 | // #include "config.h" 5 | 6 | /** 7 | * check_type - issue a warning or build failure if type is not correct. 8 | * @expr: the expression whose type we should check (not evaluated). 9 | * @type: the exact type we expect the expression to be. 10 | * 11 | * This macro is usually used within other macros to try to ensure that a macro 12 | * argument is of the expected type. No type promotion of the expression is 13 | * done: an unsigned int is not the same as an int! 14 | * 15 | * check_type() always evaluates to 0. 16 | * 17 | * If your compiler does not support typeof, then the best we can do is fail 18 | * to compile if the sizes of the types are unequal (a less complete check). 19 | * 20 | * Example: 21 | * // They should always pass a 64-bit value to _set_some_value! 22 | * #define set_some_value(expr) \ 23 | * _set_some_value((check_type((expr), uint64_t), (expr))) 24 | */ 25 | 26 | /** 27 | * check_types_match - issue a warning or build failure if types are not same. 28 | * @expr1: the first expression (not evaluated). 29 | * @expr2: the second expression (not evaluated). 30 | * 31 | * This macro is usually used within other macros to try to ensure that 32 | * arguments are of identical types. No type promotion of the expressions is 33 | * done: an unsigned int is not the same as an int! 34 | * 35 | * check_types_match() always evaluates to 0. 36 | * 37 | * If your compiler does not support typeof, then the best we can do is fail 38 | * to compile if the sizes of the types are unequal (a less complete check). 39 | * 40 | * Example: 41 | * // Do subtraction to get to enclosing type, but make sure that 42 | * // pointer is of correct type for that member. 43 | * #define container_of(mbr_ptr, encl_type, mbr) \ 44 | * (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \ 45 | * ((encl_type *) \ 46 | * ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr)))) 47 | */ 48 | #if HAVE_TYPEOF 49 | #define check_type(expr, type) \ 50 | ((__typeof__(expr) *)0 != (type *)0) 51 | 52 | #define check_types_match(expr1, expr2) \ 53 | ((__typeof__(expr1) *)0 != (__typeof__(expr2) *)0) 54 | #else 55 | #include "ccan/build_assert/build_assert.h" 56 | /* Without typeof, we can only test the sizes. */ 57 | #define check_type(expr, type) \ 58 | BUILD_ASSERT_OR_ZERO(sizeof(expr) == sizeof(type)) 59 | 60 | #define check_types_match(expr1, expr2) \ 61 | BUILD_ASSERT_OR_ZERO(sizeof(expr1) == sizeof(expr2)) 62 | #endif /* HAVE_TYPEOF */ 63 | 64 | #endif /* CCAN_CHECK_TYPE_H */ 65 | -------------------------------------------------------------------------------- /ccan/ccan/container_of/container_of.h: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #ifndef CCAN_CONTAINER_OF_H 3 | #define CCAN_CONTAINER_OF_H 4 | #include 5 | 6 | // #include "config.h" 7 | #include "ccan/check_type/check_type.h" 8 | 9 | /** 10 | * container_of - get pointer to enclosing structure 11 | * @member_ptr: pointer to the structure member 12 | * @containing_type: the type this member is within 13 | * @member: the name of this member within the structure. 14 | * 15 | * Given a pointer to a member of a structure, this macro does pointer 16 | * subtraction to return the pointer to the enclosing type. 17 | * 18 | * Example: 19 | * struct foo { 20 | * int fielda, fieldb; 21 | * // ... 22 | * }; 23 | * struct info { 24 | * int some_other_field; 25 | * struct foo my_foo; 26 | * }; 27 | * 28 | * static struct info *foo_to_info(struct foo *foo) 29 | * { 30 | * return container_of(foo, struct info, my_foo); 31 | * } 32 | */ 33 | #define container_of(member_ptr, containing_type, member) \ 34 | ((containing_type *) \ 35 | ((char *)(member_ptr) \ 36 | - container_off(containing_type, member)) \ 37 | + check_types_match(*(member_ptr), ((containing_type *)0)->member)) 38 | 39 | 40 | /** 41 | * container_of_or_null - get pointer to enclosing structure, or NULL 42 | * @member_ptr: pointer to the structure member 43 | * @containing_type: the type this member is within 44 | * @member: the name of this member within the structure. 45 | * 46 | * Given a pointer to a member of a structure, this macro does pointer 47 | * subtraction to return the pointer to the enclosing type, unless it 48 | * is given NULL, in which case it also returns NULL. 49 | * 50 | * Example: 51 | * struct foo { 52 | * int fielda, fieldb; 53 | * // ... 54 | * }; 55 | * struct info { 56 | * int some_other_field; 57 | * struct foo my_foo; 58 | * }; 59 | * 60 | * static struct info *foo_to_info_allowing_null(struct foo *foo) 61 | * { 62 | * return container_of_or_null(foo, struct info, my_foo); 63 | * } 64 | */ 65 | static inline char *container_of_or_null_(void *member_ptr, size_t offset) 66 | { 67 | return member_ptr ? (char *)member_ptr - offset : NULL; 68 | } 69 | #define container_of_or_null(member_ptr, containing_type, member) \ 70 | ((containing_type *) \ 71 | container_of_or_null_(member_ptr, \ 72 | container_off(containing_type, member)) \ 73 | + check_types_match(*(member_ptr), ((containing_type *)0)->member)) 74 | 75 | /** 76 | * container_off - get offset to enclosing structure 77 | * @containing_type: the type this member is within 78 | * @member: the name of this member within the structure. 79 | * 80 | * Given a pointer to a member of a structure, this macro does 81 | * typechecking and figures out the offset to the enclosing type. 82 | * 83 | * Example: 84 | * struct foo { 85 | * int fielda, fieldb; 86 | * // ... 87 | * }; 88 | * struct info { 89 | * int some_other_field; 90 | * struct foo my_foo; 91 | * }; 92 | * 93 | * static struct info *foo_to_info(struct foo *foo) 94 | * { 95 | * size_t off = container_off(struct info, my_foo); 96 | * return (void *)((char *)foo - off); 97 | * } 98 | */ 99 | #define container_off(containing_type, member) \ 100 | offsetof(containing_type, member) 101 | 102 | /** 103 | * container_of_var - get pointer to enclosing structure using a variable 104 | * @member_ptr: pointer to the structure member 105 | * @container_var: a pointer of same type as this member's container 106 | * @member: the name of this member within the structure. 107 | * 108 | * Given a pointer to a member of a structure, this macro does pointer 109 | * subtraction to return the pointer to the enclosing type. 110 | * 111 | * Example: 112 | * static struct info *foo_to_i(struct foo *foo) 113 | * { 114 | * struct info *i = container_of_var(foo, i, my_foo); 115 | * return i; 116 | * } 117 | */ 118 | #if HAVE_TYPEOF 119 | #define container_of_var(member_ptr, container_var, member) \ 120 | container_of(member_ptr, __typeof__(*container_var), member) 121 | #else 122 | #define container_of_var(member_ptr, container_var, member) \ 123 | ((void *)((char *)(member_ptr) - \ 124 | container_off_var(container_var, member))) 125 | #endif 126 | 127 | /** 128 | * container_off_var - get offset of a field in enclosing structure 129 | * @container_var: a pointer to a container structure 130 | * @member: the name of a member within the structure. 131 | * 132 | * Given (any) pointer to a structure and a its member name, this 133 | * macro does pointer subtraction to return offset of member in a 134 | * structure memory layout. 135 | * 136 | */ 137 | #if HAVE_TYPEOF 138 | #define container_off_var(var, member) \ 139 | container_off(__typeof__(*var), member) 140 | #else 141 | #define container_off_var(var, member) \ 142 | ((const char *)&(var)->member - (const char *)(var)) 143 | #endif 144 | 145 | #endif /* CCAN_CONTAINER_OF_H */ 146 | -------------------------------------------------------------------------------- /ccan/ccan/str/str.h: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #ifndef CCAN_STR_H 3 | #define CCAN_STR_H 4 | // #include "config.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /** 11 | * streq - Are two strings equal? 12 | * @a: first string 13 | * @b: first string 14 | * 15 | * This macro is arguably more readable than "!strcmp(a, b)". 16 | * 17 | * Example: 18 | * if (streq(somestring, "")) 19 | * printf("String is empty!\n"); 20 | */ 21 | #define streq(a,b) (strcmp((a),(b)) == 0) 22 | 23 | /** 24 | * strstarts - Does this string start with this prefix? 25 | * @str: string to test 26 | * @prefix: prefix to look for at start of str 27 | * 28 | * Example: 29 | * if (strstarts(somestring, "foo")) 30 | * printf("String %s begins with 'foo'!\n", somestring); 31 | */ 32 | #define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0) 33 | 34 | /** 35 | * strends - Does this string end with this postfix? 36 | * @str: string to test 37 | * @postfix: postfix to look for at end of str 38 | * 39 | * Example: 40 | * if (strends(somestring, "foo")) 41 | * printf("String %s end with 'foo'!\n", somestring); 42 | */ 43 | static inline bool strends(const char *str, const char *postfix) 44 | { 45 | if (strlen(str) < strlen(postfix)) 46 | return false; 47 | 48 | return streq(str + strlen(str) - strlen(postfix), postfix); 49 | } 50 | 51 | /** 52 | * stringify - Turn expression into a string literal 53 | * @expr: any C expression 54 | * 55 | * Example: 56 | * #define PRINT_COND_IF_FALSE(cond) \ 57 | * ((cond) || printf("%s is false!", stringify(cond))) 58 | */ 59 | #define stringify(expr) stringify_1(expr) 60 | /* Double-indirection required to stringify expansions */ 61 | #define stringify_1(expr) #expr 62 | 63 | /** 64 | * strcount - Count number of (non-overlapping) occurrences of a substring. 65 | * @haystack: a C string 66 | * @needle: a substring 67 | * 68 | * Example: 69 | * assert(strcount("aaa aaa", "a") == 6); 70 | * assert(strcount("aaa aaa", "ab") == 0); 71 | * assert(strcount("aaa aaa", "aa") == 2); 72 | */ 73 | size_t strcount(const char *haystack, const char *needle); 74 | 75 | /** 76 | * STR_MAX_CHARS - Maximum possible size of numeric string for this type. 77 | * @type_or_expr: a pointer or integer type or expression. 78 | * 79 | * This provides enough space for a nul-terminated string which represents the 80 | * largest possible value for the type or expression. 81 | * 82 | * Note: The implementation adds extra space so hex values or negative 83 | * values will fit (eg. sprintf(... "%p"). ) 84 | * 85 | * Example: 86 | * char str[STR_MAX_CHARS(int)]; 87 | * 88 | * sprintf(str, "%i", 7); 89 | */ 90 | #define STR_MAX_CHARS(type_or_expr) \ 91 | ((sizeof(type_or_expr) * CHAR_BIT + 8) / 9 * 3 + 2 \ 92 | + STR_MAX_CHARS_TCHECK_(type_or_expr)) 93 | 94 | #if HAVE_TYPEOF 95 | /* Only a simple type can have 0 assigned, so test that. */ 96 | #define STR_MAX_CHARS_TCHECK_(type_or_expr) \ 97 | (sizeof(({ __typeof__(type_or_expr) x = 0; x; }))*0) 98 | #else 99 | #define STR_MAX_CHARS_TCHECK_(type_or_expr) 0 100 | #endif 101 | 102 | /** 103 | * cisalnum - isalnum() which takes a char (and doesn't accept EOF) 104 | * @c: a character 105 | * 106 | * Surprisingly, the standard ctype.h isalnum() takes an int, which 107 | * must have the value of EOF (-1) or an unsigned char. This variant 108 | * takes a real char, and doesn't accept EOF. 109 | */ 110 | static inline bool cisalnum(char c) 111 | { 112 | return isalnum((unsigned char)c); 113 | } 114 | static inline bool cisalpha(char c) 115 | { 116 | return isalpha((unsigned char)c); 117 | } 118 | static inline bool cisascii(char c) 119 | { 120 | return isascii((unsigned char)c); 121 | } 122 | #if HAVE_ISBLANK 123 | static inline bool cisblank(char c) 124 | { 125 | return isblank((unsigned char)c); 126 | } 127 | #endif 128 | static inline bool ciscntrl(char c) 129 | { 130 | return iscntrl((unsigned char)c); 131 | } 132 | static inline bool cisdigit(char c) 133 | { 134 | return isdigit((unsigned char)c); 135 | } 136 | static inline bool cisgraph(char c) 137 | { 138 | return isgraph((unsigned char)c); 139 | } 140 | static inline bool cislower(char c) 141 | { 142 | return islower((unsigned char)c); 143 | } 144 | static inline bool cisprint(char c) 145 | { 146 | return isprint((unsigned char)c); 147 | } 148 | static inline bool cispunct(char c) 149 | { 150 | return ispunct((unsigned char)c); 151 | } 152 | static inline bool cisspace(char c) 153 | { 154 | return isspace((unsigned char)c); 155 | } 156 | static inline bool cisupper(char c) 157 | { 158 | return isupper((unsigned char)c); 159 | } 160 | static inline bool cisxdigit(char c) 161 | { 162 | return isxdigit((unsigned char)c); 163 | } 164 | 165 | #include "ccan/str/str_debug.h" 166 | 167 | /* These checks force things out of line, hence they are under DEBUG. */ 168 | #ifdef CCAN_STR_DEBUG 169 | #include "ccan/build_assert/build_assert.h" 170 | 171 | /* These are commonly misused: they take -1 or an *unsigned* char value. */ 172 | #undef isalnum 173 | #undef isalpha 174 | #undef isascii 175 | #undef isblank 176 | #undef iscntrl 177 | #undef isdigit 178 | #undef isgraph 179 | #undef islower 180 | #undef isprint 181 | #undef ispunct 182 | #undef isspace 183 | #undef isupper 184 | #undef isxdigit 185 | 186 | /* You can use a char if char is unsigned. */ 187 | #if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF 188 | #define str_check_arg_(i) \ 189 | ((i) + BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(__typeof__(i), \ 190 | char) \ 191 | || (char)255 > 0)) 192 | #else 193 | #define str_check_arg_(i) (i) 194 | #endif 195 | 196 | #define isalnum(i) str_isalnum(str_check_arg_(i)) 197 | #define isalpha(i) str_isalpha(str_check_arg_(i)) 198 | #define isascii(i) str_isascii(str_check_arg_(i)) 199 | #if HAVE_ISBLANK 200 | #define isblank(i) str_isblank(str_check_arg_(i)) 201 | #endif 202 | #define iscntrl(i) str_iscntrl(str_check_arg_(i)) 203 | #define isdigit(i) str_isdigit(str_check_arg_(i)) 204 | #define isgraph(i) str_isgraph(str_check_arg_(i)) 205 | #define islower(i) str_islower(str_check_arg_(i)) 206 | #define isprint(i) str_isprint(str_check_arg_(i)) 207 | #define ispunct(i) str_ispunct(str_check_arg_(i)) 208 | #define isspace(i) str_isspace(str_check_arg_(i)) 209 | #define isupper(i) str_isupper(str_check_arg_(i)) 210 | #define isxdigit(i) str_isxdigit(str_check_arg_(i)) 211 | 212 | #if HAVE_TYPEOF 213 | /* With GNU magic, we can make const-respecting standard string functions. */ 214 | #undef strstr 215 | #undef strchr 216 | #undef strrchr 217 | 218 | /* + 0 is needed to decay array into pointer. */ 219 | #define strstr(haystack, needle) \ 220 | ((__typeof__((haystack) + 0))str_strstr((haystack), (needle))) 221 | #define strchr(haystack, c) \ 222 | ((__typeof__((haystack) + 0))str_strchr((haystack), (c))) 223 | #define strrchr(haystack, c) \ 224 | ((__typeof__((haystack) + 0))str_strrchr((haystack), (c))) 225 | #endif 226 | #endif /* CCAN_STR_DEBUG */ 227 | 228 | #endif /* CCAN_STR_H */ 229 | -------------------------------------------------------------------------------- /ccan/ccan/str/str_debug.h: -------------------------------------------------------------------------------- 1 | /* CC0 (Public domain) - see LICENSE file for details */ 2 | #ifndef CCAN_STR_DEBUG_H 3 | #define CCAN_STR_DEBUG_H 4 | 5 | /* #define CCAN_STR_DEBUG 1 */ 6 | 7 | #ifdef CCAN_STR_DEBUG 8 | /* Because we mug the real ones with macros, we need our own wrappers. */ 9 | int str_isalnum(int i); 10 | int str_isalpha(int i); 11 | int str_isascii(int i); 12 | #if HAVE_ISBLANK 13 | int str_isblank(int i); 14 | #endif 15 | int str_iscntrl(int i); 16 | int str_isdigit(int i); 17 | int str_isgraph(int i); 18 | int str_islower(int i); 19 | int str_isprint(int i); 20 | int str_ispunct(int i); 21 | int str_isspace(int i); 22 | int str_isupper(int i); 23 | int str_isxdigit(int i); 24 | 25 | char *str_strstr(const char *haystack, const char *needle); 26 | char *str_strchr(const char *s, int c); 27 | char *str_strrchr(const char *s, int c); 28 | #endif /* CCAN_STR_DEBUG */ 29 | 30 | #endif /* CCAN_STR_DEBUG_H */ 31 | -------------------------------------------------------------------------------- /configfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #define _GNU_SOURCE 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "libtcmu_log.h" 19 | #include "libtcmu_common.h" 20 | #include "libtcmu_priv.h" 21 | 22 | #define CFGFS_BUF_SIZE 4096 23 | 24 | int tcmu_cfgfs_get_int(const char *path) 25 | { 26 | int fd; 27 | char buf[16]; 28 | ssize_t ret; 29 | unsigned long val; 30 | 31 | fd = open(path, O_RDONLY); 32 | if (fd == -1) { 33 | if (errno == ENOENT) { 34 | tcmu_err("Kernel does not support configfs file %s.\n", 35 | path); 36 | } else { 37 | tcmu_err("Could not open configfs file %s: %s\n", 38 | path, strerror(errno)); 39 | } 40 | return -errno; 41 | } 42 | 43 | ret = read(fd, buf, sizeof(buf)); 44 | close(fd); 45 | if (ret == -1) { 46 | tcmu_err("Could not read configfs to read attribute %s: %s\n", 47 | path, strerror(errno)); 48 | return -errno; 49 | } 50 | 51 | val = strtoul(buf, NULL, 0); 52 | if (val > INT_MAX ) { 53 | tcmu_err("could not convert string %s to value\n", buf); 54 | return -EINVAL; 55 | } 56 | 57 | return val; 58 | } 59 | 60 | int tcmu_cfgfs_dev_get_attr_int(struct tcmu_device *dev, const char *name) 61 | { 62 | char path[PATH_MAX]; 63 | 64 | snprintf(path, sizeof(path), CFGFS_CORE"/%s/%s/attrib/%s", 65 | dev->tcm_hba_name, dev->tcm_dev_name, name); 66 | return tcmu_cfgfs_get_int(path); 67 | } 68 | 69 | uint64_t tcmu_cfgfs_dev_get_info_u64(struct tcmu_device *dev, const char *name, 70 | int *fn_ret) 71 | { 72 | int fd; 73 | char path[PATH_MAX]; 74 | char buf[CFGFS_BUF_SIZE]; 75 | ssize_t ret; 76 | char *rover; 77 | char *search_pattern; 78 | uint64_t val; 79 | 80 | *fn_ret = 0; 81 | snprintf(path, sizeof(path), CFGFS_CORE"/%s/%s/info", 82 | dev->tcm_hba_name, dev->tcm_dev_name); 83 | 84 | fd = open(path, O_RDONLY); 85 | if (fd == -1) { 86 | if (errno == ENOENT) { 87 | tcmu_err("Kernel does not support device info file %s.\n", 88 | path); 89 | } else { 90 | tcmu_err("Could not open device info file %s: %s\n", 91 | path, strerror(errno)); 92 | } 93 | *fn_ret = -errno; 94 | return 0; 95 | } 96 | 97 | ret = read(fd, buf, sizeof(buf)); 98 | close(fd); 99 | if (ret == -1) { 100 | tcmu_err("Could not read configfs to read dev info: %s\n", 101 | strerror(errno)); 102 | *fn_ret = -EINVAL; 103 | return 0; 104 | } else if (ret == 0) { 105 | tcmu_err("Invalid device info.\n"); 106 | *fn_ret = -EINVAL; 107 | return 0; 108 | } 109 | buf[ret-1] = '\0'; /* paranoid? Ensure null terminated */ 110 | 111 | if (asprintf(&search_pattern, " %s: ", name) < 0) { 112 | tcmu_err("Could not create search string.\n"); 113 | *fn_ret = -ENOMEM; 114 | return 0; 115 | } 116 | 117 | rover = strstr(buf, search_pattern); 118 | free(search_pattern); 119 | if (!rover) { 120 | tcmu_err("Could not find \" %s: \" in %s: %s\n", name, path, 121 | strerror(errno)); 122 | *fn_ret = -EINVAL; 123 | return 0; 124 | } 125 | rover += strlen(name) + 3; /* name plus ':' and spaces before/after */ 126 | 127 | val = strtoull(rover, NULL, 0); 128 | if (val == ULLONG_MAX) { 129 | tcmu_err("Could not get %s: %s\n", name, strerror(errno)); 130 | *fn_ret = -EINVAL; 131 | return 0; 132 | } 133 | 134 | return val; 135 | } 136 | 137 | int tcmu_cfgfs_dev_set_ctrl_u64(struct tcmu_device *dev, const char *key, 138 | uint64_t val) 139 | { 140 | char path[PATH_MAX]; 141 | char buf[CFGFS_BUF_SIZE]; 142 | 143 | snprintf(path, sizeof(path), CFGFS_CORE"/%s/%s/control", 144 | dev->tcm_hba_name, dev->tcm_dev_name); 145 | snprintf(buf, sizeof(buf), "%s=%"PRIu64"", key, val); 146 | 147 | return tcmu_cfgfs_set_str(path, buf, strlen(buf) + 1); 148 | } 149 | 150 | int tcmu_cfgfs_mod_param_set_u32(const char *name, uint32_t val) 151 | { 152 | char path[PATH_MAX]; 153 | 154 | snprintf(path, sizeof(path), CFGFS_MOD_PARAM"/%s", name); 155 | return tcmu_cfgfs_set_u32(path, val); 156 | } 157 | 158 | /* 159 | * Return a string that contains the device's WWN, or NULL. 160 | * 161 | * Callers must free the result with free(). 162 | */ 163 | char *tcmu_cfgfs_dev_get_wwn(struct tcmu_device *dev) 164 | { 165 | int fd; 166 | char path[PATH_MAX]; 167 | char buf[CFGFS_BUF_SIZE]; 168 | char *ret_buf; 169 | int ret; 170 | 171 | snprintf(path, sizeof(path), 172 | CFGFS_CORE"/%s/%s/wwn/vpd_unit_serial", 173 | dev->tcm_hba_name, dev->tcm_dev_name); 174 | 175 | fd = open(path, O_RDONLY); 176 | if (fd == -1) { 177 | if (errno == ENOENT) { 178 | tcmu_err("Kernel does not support unit serial file %s.\n", 179 | path); 180 | } else { 181 | tcmu_err("Could not open unit serial file %s: %s\n", 182 | path, strerror(errno)); 183 | } 184 | return NULL; 185 | } 186 | 187 | ret = read(fd, buf, sizeof(buf)); 188 | close(fd); 189 | if (ret == -1) { 190 | tcmu_err("Could not read configfs to read unit serial: %s\n", 191 | strerror(errno)); 192 | return NULL; 193 | } else if (ret == 0) { 194 | tcmu_err("Invalid VPD serial number.\n"); 195 | return NULL; 196 | } 197 | 198 | /* Kill the trailing '\n' */ 199 | buf[ret-1] = '\0'; 200 | 201 | /* Skip to the good stuff */ 202 | ret = asprintf(&ret_buf, "%s", &buf[28]); 203 | if (ret == -1) { 204 | tcmu_err("could not convert string to value: %s\n", 205 | strerror(errno)); 206 | return NULL; 207 | } 208 | 209 | return ret_buf; 210 | } 211 | 212 | char *tcmu_cfgfs_get_str(const char *path) 213 | { 214 | int fd, n; 215 | char buf[CFGFS_BUF_SIZE]; 216 | ssize_t ret; 217 | char *val; 218 | 219 | memset(buf, 0, sizeof(buf)); 220 | fd = open(path, O_RDONLY); 221 | if (fd == -1) { 222 | if (errno == ENOENT) { 223 | tcmu_err("Kernel does not support configfs file %s.\n", 224 | path); 225 | } else { 226 | tcmu_err("Could not open configfs file %s: %s\n", 227 | path, strerror(errno)); 228 | } 229 | return NULL; 230 | } 231 | 232 | ret = read(fd, buf, sizeof(buf)); 233 | close(fd); 234 | if (ret == -1) { 235 | tcmu_err("Could not read configfs to read attribute %s: %s\n", 236 | path, strerror(errno)); 237 | return NULL; 238 | } 239 | 240 | if (ret == 0) 241 | return NULL; 242 | 243 | /* 244 | * Some files like members will terminate each member/line with a null 245 | * char. Except for the last one, replace it with '\n' so parsers will 246 | * just see an empty member. 247 | */ 248 | if (ret != strlen(buf)) { 249 | do { 250 | n = strlen(buf); 251 | buf[n] = '\n'; 252 | } while (n < ret); 253 | } 254 | 255 | /* 256 | * Some files like members ends with a null char, but other files like 257 | * the alua ones end with a newline. 258 | */ 259 | if (buf[ret - 1] == '\n') 260 | buf[ret - 1] = '\0'; 261 | 262 | if (buf[ret - 1] != '\0') { 263 | if (ret >= CFGFS_BUF_SIZE) { 264 | tcmu_err("Invalid cfgfs file %s: not enough space for ending null char.\n", 265 | path); 266 | return NULL; 267 | } 268 | /* 269 | * In case the file does "return sprintf()" with no ending 270 | * newline add the ending null so we will not crash below. 271 | */ 272 | buf[ret] = '\0'; 273 | } 274 | 275 | val = strdup(buf); 276 | if (!val) { 277 | tcmu_err("could not copy buffer %s : %s\n", 278 | buf, strerror(errno)); 279 | return NULL; 280 | } 281 | 282 | return val; 283 | } 284 | 285 | int tcmu_cfgfs_set_str(const char *path, const char *val, int val_len) 286 | { 287 | int fd; 288 | ssize_t ret; 289 | 290 | fd = open(path, O_WRONLY); 291 | if (fd == -1) { 292 | if (errno == ENOENT) { 293 | tcmu_err("Kernel does not support configfs file %s.\n", 294 | path); 295 | } else { 296 | tcmu_err("Could not open configfs file %s: %s\n", 297 | path, strerror(errno)); 298 | } 299 | return -errno; 300 | } 301 | 302 | ret = write(fd, val, val_len); 303 | close(fd); 304 | if (ret == -1) { 305 | tcmu_err("Could not write configfs to write attribute %s: %s\n", 306 | path, strerror(errno)); 307 | return -errno; 308 | } 309 | 310 | return 0; 311 | } 312 | 313 | int tcmu_cfgfs_set_u32(const char *path, uint32_t val) 314 | { 315 | char buf[20]; 316 | 317 | sprintf(buf, "%"PRIu32"", val); 318 | return tcmu_cfgfs_set_str(path, buf, strlen(buf) + 1); 319 | } 320 | 321 | int tcmu_cfgfs_dev_exec_action(struct tcmu_device *dev, const char *name, 322 | uint32_t val) 323 | { 324 | char path[PATH_MAX]; 325 | int ret; 326 | 327 | snprintf(path, sizeof(path), CFGFS_CORE"/%s/%s/action/%s", 328 | dev->tcm_hba_name, dev->tcm_dev_name, name); 329 | tcmu_dev_dbg(dev, "executing action %s\n", name); 330 | ret = tcmu_cfgfs_set_u32(path, val); 331 | tcmu_dev_dbg(dev, "action %s done\n", name); 332 | return ret; 333 | } 334 | -------------------------------------------------------------------------------- /consumer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is licensed to you under your choice of the GNU Lesser 3 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 4 | * later), or the Apache License 2.0. 5 | */ 6 | 7 | /* 8 | * An example of using libtcmu to back one or more types of LIO 9 | * userspace passthrough devices. 10 | */ 11 | 12 | #define _GNU_SOURCE 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include "target_core_user_local.h" 31 | #include "libtcmu.h" 32 | #include "scsi_defs.h" 33 | #include "libtcmu_log.h" 34 | #include "scsi.h" 35 | 36 | #define LOG_DIR "/var/log" 37 | 38 | struct tcmu_device *tcmu_dev_array[128]; 39 | size_t dev_array_len = 0; 40 | 41 | struct foo_state { 42 | int fd; 43 | uint64_t num_lbas; 44 | uint32_t block_size; 45 | }; 46 | 47 | static int foo_open(struct tcmu_device *dev) 48 | { 49 | /* open the backing file */ 50 | /* alloc private struct 'foo_state' */ 51 | /* Save a ptr to it in dev->hm_private */ 52 | 53 | /* Add new device to our horrible fixed-length array */ 54 | tcmu_dev_array[dev_array_len] = dev; 55 | dev_array_len++; 56 | 57 | return 0; 58 | } 59 | 60 | static void foo_close(struct tcmu_device *dev) 61 | { 62 | /* not supported in this example */ 63 | } 64 | 65 | static int foo_handle_cmd( 66 | struct tcmu_device *dev, 67 | uint8_t *cdb, 68 | struct iovec *iovec, 69 | size_t iov_cnt, 70 | uint8_t *sense) 71 | { 72 | struct foo_state *state = tcmu_dev_get_private(dev); 73 | uint8_t cmd; 74 | 75 | cmd = cdb[0]; 76 | 77 | switch (cmd) { 78 | case INQUIRY: 79 | return tcmu_emulate_inquiry(dev, NULL, cdb, iovec, iov_cnt); 80 | case TEST_UNIT_READY: 81 | return tcmu_emulate_test_unit_ready(cdb, iovec, iov_cnt); 82 | case SERVICE_ACTION_IN_16: 83 | if (cdb[1] == READ_CAPACITY_16) 84 | return tcmu_emulate_read_capacity_16(state->num_lbas, 85 | state->block_size, 86 | cdb, iovec, iov_cnt); 87 | else 88 | return TCMU_STS_NOT_HANDLED; 89 | break; 90 | case MODE_SENSE: 91 | case MODE_SENSE_10: 92 | return tcmu_emulate_mode_sense(dev, cdb, iovec, iov_cnt); 93 | break; 94 | case MODE_SELECT: 95 | case MODE_SELECT_10: 96 | return tcmu_emulate_mode_select(dev, cdb, iovec, iov_cnt); 97 | break; 98 | case READ_6: 99 | case READ_10: 100 | case READ_12: 101 | case READ_16: 102 | // A real "read" implementation goes here! 103 | return TCMU_STS_RD_ERR; 104 | 105 | case WRITE_6: 106 | case WRITE_10: 107 | case WRITE_12: 108 | case WRITE_16: 109 | // A real "write" implemention goes here! 110 | return TCMU_STS_OK; 111 | 112 | default: 113 | tcmu_err("unknown command %x\n", cdb[0]); 114 | return TCMU_STS_NOT_HANDLED; 115 | } 116 | } 117 | 118 | static struct tcmulib_handler foo_handler = { 119 | .name = "Handler for foo devices (example code)", 120 | .subtype = "foo", 121 | .cfg_desc = "a description goes here", 122 | 123 | .added = foo_open, 124 | .removed = foo_close, 125 | }; 126 | 127 | int main(int argc, char **argv) 128 | { 129 | struct tcmulib_context *tcmulib_ctx; 130 | struct pollfd pollfds[16]; 131 | int i; 132 | int ret; 133 | 134 | if (tcmu_setup_log(LOG_DIR)) { 135 | fprintf(stderr, "Could not setup tcmu logger.\n"); 136 | exit(1); 137 | } 138 | 139 | /* If any TCMU devices that exist that match subtype, 140 | handler->added() will now be called from within 141 | tcmulib_initialize(). */ 142 | tcmulib_ctx = tcmulib_initialize(&foo_handler, 1); 143 | if (!tcmulib_ctx) { 144 | tcmu_err("tcmulib_initialize failed with %p\n", tcmulib_ctx); 145 | exit(1); 146 | } 147 | 148 | while (1) { 149 | pollfds[0].fd = tcmulib_get_master_fd(tcmulib_ctx); 150 | pollfds[0].events = POLLIN; 151 | pollfds[0].revents = 0; 152 | 153 | for (i = 0; i < dev_array_len; i++) { 154 | pollfds[i+1].fd = tcmu_dev_get_fd(tcmu_dev_array[i]); 155 | pollfds[i+1].events = POLLIN; 156 | pollfds[i+1].revents = 0; 157 | } 158 | 159 | /* Use ppoll instead poll to avoid poll call reschedules during signal 160 | * handling. If we were removing a device, then the uio device's memory 161 | * could be freed, but the poll would be rescheduled and end up accessing 162 | * the released device. */ 163 | ret = ppoll(pollfds, dev_array_len+1, NULL, NULL); 164 | if (ret == -1) { 165 | tcmu_err("ppoll() returned %d, exiting\n", ret); 166 | exit(EXIT_FAILURE); 167 | } 168 | 169 | if (pollfds[0].revents) { 170 | /* If any tcmu devices have been added or removed, the 171 | added() and removed() handler callbacks will be called 172 | from within this. */ 173 | tcmulib_master_fd_ready(tcmulib_ctx); 174 | 175 | /* Since devices (may) have changed, re-poll() instead of 176 | processing per-device fds. */ 177 | continue; 178 | } 179 | 180 | for (i = 0; i < dev_array_len; i++) { 181 | if (pollfds[i+1].revents) { 182 | struct tcmulib_cmd *cmd; 183 | struct tcmu_device *dev = tcmu_dev_array[i]; 184 | 185 | tcmulib_processing_start(dev); 186 | 187 | while ((cmd = tcmulib_get_next_command(dev, 0)) != NULL) { 188 | ret = foo_handle_cmd(dev, 189 | cmd->cdb, 190 | cmd->iovec, 191 | cmd->iov_cnt, 192 | cmd->sense_buf); 193 | tcmulib_command_complete(dev, cmd, ret); 194 | } 195 | 196 | tcmulib_processing_complete(dev); 197 | } 198 | } 199 | } 200 | 201 | return 0; 202 | } 203 | -------------------------------------------------------------------------------- /darray.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Joseph Adams 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef CCAN_DARRAY_H 24 | #define CCAN_DARRAY_H 25 | 26 | #include 27 | #include 28 | 29 | #define HAVE_STATEMENT_EXPR 1 30 | #define HAVE_TYPEOF 1 31 | 32 | /* 33 | * SYNOPSIS 34 | * 35 | * Life cycle of a darray (dynamically-allocated array): 36 | * 37 | * darray(int) a = darray_new(); 38 | * darray_free(a); 39 | * 40 | * struct {darray(int) a;} foo; 41 | * darray_init(foo.a); 42 | * darray_free(foo.a); 43 | * 44 | * Typedefs for darrays of common types: 45 | * 46 | * darray_char, darray_schar, darray_uchar 47 | * darray_short, darray_int, darray_long 48 | * darray_ushort, darray_uint, darray_ulong 49 | * 50 | * Access: 51 | * 52 | * T darray_item(darray(T) arr, size_t index); 53 | * size_t darray_size(darray(T) arr); 54 | * size_t darray_alloc(darray(T) arr); 55 | * bool darray_empty(darray(T) arr); 56 | * 57 | * Insertion (single item): 58 | * 59 | * void darray_append(darray(T) arr, T item); 60 | * void darray_prepend(darray(T) arr, T item); 61 | * void darray_push(darray(T) arr, T item); // same as darray_append 62 | * 63 | * Insertion (multiple items): 64 | * 65 | * void darray_append_items(darray(T) arr, T *items, size_t count); 66 | * void darray_prepend_items(darray(T) arr, T *items, size_t count); 67 | * 68 | * void darray_appends(darray(T) arr, [T item, [...]]); 69 | * void darray_prepends(darray(T) arr, [T item, [...]]); 70 | * 71 | * // Same functionality as above, but does not require typeof. 72 | * void darray_appends_t(darray(T) arr, #T, [T item, [...]]); 73 | * void darray_prepends_t(darray(T) arr, #T, [T item, [...]]); 74 | * 75 | * Removal: 76 | * 77 | * T darray_pop(darray(T) arr | darray_size(arr) != 0); 78 | * T* darray_pop_check(darray(T*) arr); 79 | * 80 | * Replacement: 81 | * 82 | * void darray_from_items(darray(T) arr, T *items, size_t count); 83 | * void darray_from_c(darray(T) arr, T c_array[N]); 84 | * 85 | * String buffer: 86 | * 87 | * void darray_append_string(darray(char) arr, const char *str); 88 | * void darray_append_lit(darray(char) arr, char stringLiteral[N+1]); 89 | * 90 | * void darray_prepend_string(darray(char) arr, const char *str); 91 | * void darray_prepend_lit(darray(char) arr, char stringLiteral[N+1]); 92 | * 93 | * void darray_from_string(darray(T) arr, const char *str); 94 | * void darray_from_lit(darray(char) arr, char stringLiteral[N+1]); 95 | * 96 | * Size management: 97 | * 98 | * void darray_resize(darray(T) arr, size_t newSize); 99 | * void darray_resize0(darray(T) arr, size_t newSize); 100 | * 101 | * void darray_realloc(darray(T) arr, size_t newAlloc); 102 | * void darray_growalloc(darray(T) arr, size_t newAlloc); 103 | * 104 | * void darray_make_room(darray(T) arr, size_t room); 105 | * 106 | * Traversal: 107 | * 108 | * darray_foreach(T *&i, darray(T) arr) {...} 109 | * darray_foreach_reverse(T *&i, darray(T) arr) {...} 110 | * 111 | * Except for darray_foreach and darray_foreach_reverse, 112 | * all macros evaluate their non-darray arguments only once. 113 | */ 114 | 115 | /*** Life cycle ***/ 116 | 117 | #define darray(type) struct {type *item; size_t size; size_t alloc;} 118 | 119 | #define darray_new() {0,0,0} 120 | #define darray_init(arr) do {(arr).item=0; (arr).size=0; (arr).alloc=0;} while(0) 121 | #define darray_free(arr) do {free((arr).item);} while(0) 122 | 123 | 124 | /* 125 | * Typedefs for darrays of common types. These are useful 126 | * when you want to pass a pointer to an darray(T) around. 127 | * 128 | * The following will produce an incompatible pointer warning: 129 | * 130 | * void foo(darray(int) *arr); 131 | * darray(int) arr = darray_new(); 132 | * foo(&arr); 133 | * 134 | * The workaround: 135 | * 136 | * void foo(darray_int *arr); 137 | * darray_int arr = darray_new(); 138 | * foo(&arr); 139 | */ 140 | 141 | typedef darray(char) darray_char; 142 | typedef darray(signed char) darray_schar; 143 | typedef darray(unsigned char) darray_uchar; 144 | 145 | typedef darray(short) darray_short; 146 | typedef darray(int) darray_int; 147 | typedef darray(long) darray_long; 148 | 149 | typedef darray(unsigned short) darray_ushort; 150 | typedef darray(unsigned int) darray_uint; 151 | typedef darray(unsigned long) darray_ulong; 152 | 153 | 154 | /*** Access ***/ 155 | 156 | #define darray_item(arr, i) ((arr).item[i]) 157 | #define darray_size(arr) ((arr).size) 158 | #define darray_alloc(arr) ((arr).alloc) 159 | #define darray_empty(arr) ((arr).size == 0) 160 | 161 | 162 | /*** Insertion (single item) ***/ 163 | 164 | #define darray_append(arr, ...) do { \ 165 | darray_resize(arr, (arr).size+1); \ 166 | (arr).item[(arr).size-1] = (__VA_ARGS__); \ 167 | } while(0) 168 | #define darray_prepend(arr, ...) do { \ 169 | darray_resize(arr, (arr).size+1); \ 170 | memmove((arr).item+1, (arr).item, ((arr).size-1)*sizeof(*(arr).item)); \ 171 | (arr).item[0] = (__VA_ARGS__); \ 172 | } while(0) 173 | #define darray_push(arr, ...) darray_append(arr, __VA_ARGS__) 174 | 175 | 176 | /*** Insertion (multiple items) ***/ 177 | 178 | #define darray_append_items(arr, items, count) do { \ 179 | size_t __count = (count), __oldSize = (arr).size; \ 180 | darray_resize(arr, __oldSize + __count); \ 181 | memcpy((arr).item + __oldSize, items, __count * sizeof(*(arr).item)); \ 182 | } while(0) 183 | 184 | #define darray_prepend_items(arr, items, count) do { \ 185 | size_t __count = (count), __oldSize = (arr).size; \ 186 | darray_resize(arr, __count + __oldSize); \ 187 | memmove((arr).item + __count, (arr).item, __oldSize * sizeof(*(arr).item)); \ 188 | memcpy((arr).item, items, __count * sizeof(*(arr).item)); \ 189 | } while(0) 190 | 191 | #define darray_append_items_nullterminate(arr, items, count) do { \ 192 | size_t __count = (count), __oldSize = (arr).size; \ 193 | darray_resize(arr, __oldSize + __count + 1); \ 194 | memcpy((arr).item + __oldSize, items, __count * sizeof(*(arr).item)); \ 195 | (arr).item[--(arr).size] = 0; \ 196 | } while(0) 197 | 198 | #define darray_prepend_items_nullterminate(arr, items, count) do { \ 199 | size_t __count = (count), __oldSize = (arr).size; \ 200 | darray_resize(arr, __count + __oldSize + 1); \ 201 | memmove((arr).item + __count, (arr).item, __oldSize * sizeof(*(arr).item)); \ 202 | memcpy((arr).item, items, __count * sizeof(*(arr).item)); \ 203 | (arr).item[--(arr).size] = 0; \ 204 | } while(0) 205 | 206 | #if HAVE_TYPEOF 207 | #define darray_appends(arr, ...) darray_appends_t(arr, typeof((*(arr).item)), __VA_ARGS__) 208 | #define darray_prepends(arr, ...) darray_prepends_t(arr, typeof((*(arr).item)), __VA_ARGS__) 209 | #endif 210 | 211 | #define darray_appends_t(arr, type, ...) do { \ 212 | type __src[] = {__VA_ARGS__}; \ 213 | darray_append_items(arr, __src, sizeof(__src)/sizeof(*__src)); \ 214 | } while(0) 215 | #define darray_prepends_t(arr, type, ...) do { \ 216 | type __src[] = {__VA_ARGS__}; \ 217 | darray_prepend_items(arr, __src, sizeof(__src)/sizeof(*__src)); \ 218 | } while(0) 219 | 220 | 221 | /*** Removal ***/ 222 | 223 | /* Warning: Do not call darray_pop on an empty darray. */ 224 | #define darray_pop(arr) ((arr).item[--(arr).size]) 225 | #define darray_pop_check(arr) ((arr).size ? darray_pop(arr) : NULL) 226 | 227 | 228 | /*** Replacement ***/ 229 | 230 | #define darray_from_items(arr, items, count) do {size_t __count = (count); darray_resize(arr, __count); memcpy((arr).item, items, __count*sizeof(*(arr).item));} while(0) 231 | #define darray_from_c(arr, c_array) darray_from_items(arr, c_array, sizeof(c_array)/sizeof(*(c_array))) 232 | 233 | 234 | /*** String buffer ***/ 235 | 236 | #define darray_append_string(arr, str) do {const char *__str = (str); darray_append_items(arr, __str, strlen(__str)+1); (arr).size--;} while(0) 237 | #define darray_append_lit(arr, stringLiteral) do {darray_append_items(arr, stringLiteral, sizeof(stringLiteral)); (arr).size--;} while(0) 238 | 239 | #define darray_prepend_string(arr, str) do { \ 240 | const char *__str = (str); \ 241 | darray_prepend_items_nullterminate(arr, __str, strlen(__str)); \ 242 | } while(0) 243 | #define darray_prepend_lit(arr, stringLiteral) \ 244 | darray_prepend_items_nullterminate(arr, stringLiteral, sizeof(stringLiteral) - 1) 245 | 246 | #define darray_from_string(arr, str) do {const char *__str = (str); darray_from_items(arr, __str, strlen(__str)+1); (arr).size--;} while(0) 247 | #define darray_from_lit(arr, stringLiteral) do {darray_from_items(arr, stringLiteral, sizeof(stringLiteral)); (arr).size--;} while(0) 248 | 249 | 250 | /*** Size management ***/ 251 | 252 | #define darray_resize(arr, newSize) darray_growalloc(arr, (arr).size = (newSize)) 253 | #define darray_resize0(arr, newSize) do { \ 254 | size_t __oldSize = (arr).size, __newSize = (newSize); \ 255 | (arr).size = __newSize; \ 256 | if (__newSize > __oldSize) { \ 257 | darray_growalloc(arr, __newSize); \ 258 | memset(&(arr).item[__oldSize], 0, (__newSize - __oldSize) * sizeof(*(arr).item)); \ 259 | } \ 260 | } while(0) 261 | 262 | #define darray_realloc(arr, newAlloc) do { \ 263 | (arr).item = realloc((arr).item, ((arr).alloc = (newAlloc)) * sizeof(*(arr).item)); \ 264 | } while(0) 265 | #define darray_growalloc(arr, need) do { \ 266 | size_t __need = (need); \ 267 | if (__need > (arr).alloc) \ 268 | darray_realloc(arr, darray_next_alloc((arr).alloc, __need)); \ 269 | } while(0) 270 | 271 | #if HAVE_STATEMENT_EXPR==1 272 | #define darray_make_room(arr, room) ({size_t newAlloc = (arr).size+(room); if ((arr).alloc &(arr).item[0]; ) 303 | 304 | 305 | #endif /* CCAN_DARRAY_H */ 306 | 307 | /* 308 | 309 | darray_growalloc(arr, newAlloc) sees if the darray can currently hold newAlloc items; 310 | if not, it increases the alloc to satisfy this requirement, allocating slack 311 | space to avoid having to reallocate for every size increment. 312 | 313 | darray_from_string(arr, str) copies a string to an darray_char. 314 | 315 | darray_push(arr, item) pushes an item to the end of the darray. 316 | darray_pop(arr) pops it back out. Be sure there is at least one item in the darray before calling. 317 | darray_pop_check(arr) does the same as darray_pop, but returns NULL if there are no more items left in the darray. 318 | 319 | darray_make_room(arr, room) ensures there's 'room' elements of space after the end of the darray, and it returns a pointer to this space. 320 | Currently requires HAVE_STATEMENT_EXPR, but I plan to remove this dependency by creating an inline function. 321 | 322 | The following require HAVE_TYPEOF==1 : 323 | 324 | darray_appends(arr, item0, item1...) appends a collection of comma-delimited items to the darray. 325 | darray_prepends(arr, item0, item1...) prepends a collection of comma-delimited items to the darray.\ 326 | 327 | 328 | Examples: 329 | 330 | darray(int) arr; 331 | int *i; 332 | 333 | darray_appends(arr, 0,1,2,3,4); 334 | darray_appends(arr, -5,-4,-3,-2,-1); 335 | darray_foreach(i, arr) 336 | printf("%d ", *i); 337 | printf("\n"); 338 | 339 | darray_free(arr); 340 | 341 | typedef struct {int n,d;} Fraction; 342 | darray(Fraction) fractions; 343 | Fraction *i; 344 | 345 | darray_appends(fractions, {3,4}, {3,5}, {2,1}); 346 | darray_foreach(i, fractions) 347 | printf("%d/%d\n", i->n, i->d); 348 | 349 | darray_free(fractions); 350 | */ 351 | 352 | /* added by me */ 353 | #define darray_remove(arr, i) do { \ 354 | if (i < arr.size-1) \ 355 | memmove(&(arr).item[i], &(arr).item[i+1], ((arr).size-1-i)*sizeof(*(arr).item)); \ 356 | (arr).size--; \ 357 | } while (0) 358 | -------------------------------------------------------------------------------- /extra/install_dep.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Userspace side of the LIO TCM-User backstore 4 | # 5 | # For now only fedora, rhel and centos systems are supported 6 | 7 | if test $(id -u) != 0 ; then 8 | SUDO=sudo 9 | fi 10 | 11 | if [ y`uname`y = yLinuxy ]; then 12 | source /etc/os-release 13 | case $ID in 14 | fedora|rhel|centos) 15 | # for generic 16 | $SUDO yum install -y cmake make gcc 17 | $SUDO yum install -y libnl3 18 | $SUDO yum install -y libnl3-devel 19 | $SUDO yum install -y glib2 20 | $SUDO yum install -y glib2-devel 21 | $SUDO yum install -y zlib 22 | $SUDO yum install -y zlib-devel 23 | $SUDO yum install -y kmod 24 | $SUDO yum install -y kmod-devel 25 | $SUDO yum install -y gperftools-devel 26 | 27 | # for glusterfs 28 | $SUDO yum install -y glusterfs-api 29 | $SUDO yum install -y glusterfs-api-devel 30 | # for ceph 31 | $SUDO yum install -y librados2 32 | $SUDO yum install -y librados2-devel 33 | $SUDO yum install -y librbd1 34 | yum search librbd-devel | grep -q "N/S matched" && LIBRBD=librbd || LIBRBD=librbd1 35 | $SUDO yum install -y $LIBRBD-devel 36 | ;; 37 | debian) 38 | # Update APT cache 39 | $SUDO apt update 40 | 41 | # for generic 42 | $SUDO apt install -y cmake make gcc 43 | $SUDO apt install -y zlib1g kmod 44 | $SUDO apt install -y libnl-3-dev 45 | $SUDO apt install -y libnl-genl-3-dev 46 | $SUDO apt install -y libglib2.0-0 47 | $SUDO apt install -y libkmod-dev 48 | $SUDO apt install -y libgoogle-perftools-dev 49 | 50 | # for glusterfs 51 | $SUDO apt install -y libglusterfs-dev 52 | 53 | # for ceph 54 | $SUDO apt install -y librados2 55 | $SUDO apt install -y librbd-dev 56 | ;; 57 | sles|opensuse-tumbleweed) 58 | # for generic 59 | $SUDO zypper install -y cmake make gcc 60 | $SUDO zypper install -y libnl3-200 61 | $SUDO zypper install -y glib2 62 | $SUDO zypper install -y zlib 63 | $SUDO zypper install -y kmod 64 | $SUDO zypper install -y libnl3-devel 65 | $SUDO zypper install -y glib2-devel 66 | $SUDO zypper install -y zlib-devel 67 | $SUDO zypper install -y libkmod-devel 68 | $SUDO zypper install -y gperftools-devel 69 | 70 | #for glusterfs 71 | $SUDO zypper install -y glusterfs 72 | $SUDO zypper install -y glusterfs-devel 73 | #for ceph 74 | $SUDO zypper install -y librbd-devel 75 | $SUDO zypper install -y librados-devel 76 | $SUDO zypper install -y librados2 77 | ;; 78 | *) 79 | echo "TODO: distro not supported for now!" 80 | ;; 81 | esac 82 | else 83 | echo "TODO: only Linux is supported for now!" 84 | exit 1 85 | fi 86 | -------------------------------------------------------------------------------- /extra/make_runnerrpms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "$1" == "--help" -o "$1" == "help" ]; then 4 | echo "" 5 | echo " USAGE:" 6 | echo "" 7 | echo " # cd tcmu-runner/extra/" 8 | echo " # ./make_runnerrpms.sh [--without (rbd|glfs|qcow|zbc|fbo|tcmalloc)]" 9 | echo "" 10 | echo " Will build the RPMs in current dir by using the HEAD commit ID as default." 11 | echo "" 12 | exit 13 | fi 14 | 15 | TOPDIR=`pwd`/../ 16 | 17 | if [ ! -e $TOPDIR/.git ]; then 18 | echo "" 19 | echo "For now this will only support the git repo code." 20 | echo "" 21 | exit 22 | fi 23 | 24 | VERSION=`git describe --tags --match "v[0-9]*"` 25 | VERSION=`echo $VERSION | sed "s/-/./g"` 26 | VERSION=`echo $VERSION | sed "s/v//"` 27 | TCMURUNNER_TAR=tcmu-runner-$VERSION.tar.gz 28 | rpmbuild_path=`pwd`/rpmbuild 29 | 30 | # Try to clear the old rpmbuild data. 31 | if [ -e $rpmbuild_path ]; then 32 | rm -rf $rpmbuild_path/* 33 | fi 34 | 35 | mkdir -p $rpmbuild_path/BUILD 36 | mkdir -p $rpmbuild_path/SPECS 37 | mkdir -p $rpmbuild_path/RPMS 38 | mkdir -p $rpmbuild_path/SRPMS 39 | mkdir -p $rpmbuild_path/SOURCES 40 | 41 | cp ../tcmu-runner.spec $rpmbuild_path/SPECS/ 42 | SPEC=$rpmbuild_path/SPECS/tcmu-runner.spec 43 | 44 | # Replace the Version 45 | sed -i "s/Version:.*$/Version: ${VERSION}/" $SPEC 46 | 47 | # Delete all the _RC code if exists 48 | LN=`grep -n "define" $SPEC |grep _RC | awk -F: '{print $1}'` 49 | sed -i "${LN}d" $SPEC 50 | sed -i "s/%{?_RC:%{_RC}}/0/g" $SPEC 51 | sed -i "s/%{?_RC:-%{_RC}}//g" $SPEC 52 | 53 | # Generate the source package 54 | TMPDIR=/tmp/tcmu-runner-build 55 | PKG_NAME=tcmu-runner-$VERSION 56 | mkdir -p $TMPDIR/$PKG_NAME 57 | git clone $TOPDIR/.git $TMPDIR/$PKG_NAME 58 | rm -rf $TMPDIR/$PKG_NAME/.git* 59 | cd $TMPDIR 60 | tar -czvf $rpmbuild_path/SOURCES/$TCMURUNNER_TAR $PKG_NAME 2&> /dev/null 61 | cd $TOPDIR/extra 62 | rm -rf $TMPDIR 63 | 64 | # Build the RPMs 65 | rpmbuild --define="_topdir $rpmbuild_path" -ba $rpmbuild_path/SPECS/tcmu-runner.spec "$@" 66 | -------------------------------------------------------------------------------- /file_example.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | /* 10 | * Example code to demonstrate how a TCMU handler might work. 11 | * 12 | * Using the example of backing a device by a file to demonstrate: 13 | * 14 | * 1) Registering with tcmu-runner 15 | * 2) Parsing the handler-specific config string as needed for setup 16 | * 3) Opening resources as needed 17 | * 4) Handling SCSI commands and using the handler API 18 | */ 19 | 20 | #define _GNU_SOURCE 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "scsi_defs.h" 36 | #include "libtcmu.h" 37 | #include "tcmu-runner.h" 38 | #include "tcmur_device.h" 39 | 40 | struct file_state { 41 | int fd; 42 | }; 43 | 44 | static int file_open(struct tcmu_device *dev, bool reopen) 45 | { 46 | struct file_state *state; 47 | char *config; 48 | 49 | state = calloc(1, sizeof(*state)); 50 | if (!state) 51 | return -ENOMEM; 52 | 53 | tcmur_dev_set_private(dev, state); 54 | 55 | config = strchr(tcmu_dev_get_cfgstring(dev), '/'); 56 | if (!config) { 57 | tcmu_err("no configuration found in cfgstring\n"); 58 | goto err; 59 | } 60 | config += 1; /* get past '/' */ 61 | 62 | tcmu_dev_set_write_cache_enabled(dev, 1); 63 | 64 | state->fd = open(config, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); 65 | if (state->fd == -1) { 66 | tcmu_err("could not open %s: %m\n", config); 67 | goto err; 68 | } 69 | 70 | tcmu_dbg("config %s\n", tcmu_dev_get_cfgstring(dev)); 71 | 72 | return 0; 73 | 74 | err: 75 | free(state); 76 | return -EINVAL; 77 | } 78 | 79 | static void file_close(struct tcmu_device *dev) 80 | { 81 | struct file_state *state = tcmur_dev_get_private(dev); 82 | 83 | close(state->fd); 84 | free(state); 85 | } 86 | 87 | static int file_read(struct tcmu_device *dev, struct tcmur_cmd *cmd, 88 | struct iovec *iov, size_t iov_cnt, size_t length, 89 | off_t offset) 90 | { 91 | struct file_state *state = tcmur_dev_get_private(dev); 92 | size_t remaining = length; 93 | ssize_t ret; 94 | 95 | while (remaining) { 96 | ret = preadv(state->fd, iov, iov_cnt, offset); 97 | if (ret < 0) { 98 | tcmu_err("read failed: %m\n"); 99 | ret = TCMU_STS_RD_ERR; 100 | goto done; 101 | } 102 | 103 | if (ret == 0) { 104 | /* EOF, then zeros the iovecs left */ 105 | tcmu_iovec_zero(iov, iov_cnt); 106 | break; 107 | } 108 | 109 | tcmu_iovec_seek(iov, ret); 110 | offset += ret; 111 | remaining -= ret; 112 | } 113 | ret = TCMU_STS_OK; 114 | done: 115 | return ret; 116 | } 117 | 118 | static int file_write(struct tcmu_device *dev, struct tcmur_cmd *cmd, 119 | struct iovec *iov, size_t iov_cnt, size_t length, 120 | off_t offset) 121 | { 122 | struct file_state *state = tcmur_dev_get_private(dev); 123 | size_t remaining = length; 124 | ssize_t ret; 125 | 126 | while (remaining) { 127 | ret = pwritev(state->fd, iov, iov_cnt, offset); 128 | if (ret < 0) { 129 | tcmu_err("write failed: %m\n"); 130 | ret = TCMU_STS_WR_ERR; 131 | goto done; 132 | } 133 | tcmu_iovec_seek(iov, ret); 134 | offset += ret; 135 | remaining -= ret; 136 | } 137 | ret = TCMU_STS_OK; 138 | done: 139 | return ret; 140 | } 141 | 142 | static int file_flush(struct tcmu_device *dev, struct tcmur_cmd *cmd) 143 | { 144 | struct file_state *state = tcmur_dev_get_private(dev); 145 | int ret; 146 | 147 | if (fsync(state->fd)) { 148 | tcmu_err("sync failed\n"); 149 | ret = TCMU_STS_WR_ERR; 150 | goto done; 151 | } 152 | ret = TCMU_STS_OK; 153 | done: 154 | return ret; 155 | } 156 | 157 | static int file_reconfig(struct tcmu_device *dev, struct tcmulib_cfg_info *cfg) 158 | { 159 | switch (cfg->type) { 160 | case TCMULIB_CFG_DEV_SIZE: 161 | /* 162 | * TODO - For open/reconfig we should make sure the FS the 163 | * file is on is large enough for the requested size. For 164 | * now assume we can grow the file and return 0. 165 | */ 166 | return 0; 167 | case TCMULIB_CFG_DEV_CFGSTR: 168 | case TCMULIB_CFG_WRITE_CACHE: 169 | default: 170 | return -EOPNOTSUPP; 171 | } 172 | } 173 | 174 | static const char file_cfg_desc[] = 175 | "The path to the file to use as a backstore."; 176 | 177 | static struct tcmur_handler file_handler = { 178 | .cfg_desc = file_cfg_desc, 179 | 180 | .reconfig = file_reconfig, 181 | 182 | .open = file_open, 183 | .close = file_close, 184 | .read = file_read, 185 | .write = file_write, 186 | .flush = file_flush, 187 | .name = "File-backed Handler (example code)", 188 | .subtype = "file", 189 | .nr_threads = 2, 190 | }; 191 | 192 | /* Entry point must be named "handler_init". */ 193 | int handler_init(void) 194 | { 195 | return tcmur_register_handler(&file_handler); 196 | } 197 | -------------------------------------------------------------------------------- /libtcmu-register.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is licensed to you under your choice of the GNU Lesser 3 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 4 | * later), or the Apache License 2.0. 5 | */ 6 | 7 | #define _GNU_SOURCE 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "libtcmu.h" 15 | #include "libtcmu_log.h" 16 | #include "libtcmu_priv.h" 17 | #include "tcmuhandler-generated.h" 18 | 19 | static bool 20 | tcmulib_register_handler(struct tcmulib_context *ctx, 21 | struct tcmulib_handler *handler) 22 | { 23 | GError *error = NULL; 24 | gboolean succeeded; 25 | const gchar *reason; 26 | bool ret = true; 27 | 28 | GVariant *result = g_dbus_connection_call_sync(ctx->connection, 29 | "org.kernel.TCMUService1", 30 | "/org/kernel/TCMUService1/HandlerManager1", 31 | "org.kernel.TCMUService1.HandlerManager1", 32 | "RegisterHandler", 33 | g_variant_new("(ss)", 34 | handler->subtype, 35 | handler->cfg_desc), 36 | g_variant_type_new("(bs)"), 37 | G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); 38 | if (!result) { 39 | tcmu_err("Failed to call register method for '%s(%s)': %s", 40 | handler->name, 41 | handler->subtype, 42 | error->message); 43 | return false; 44 | } 45 | g_variant_get(result, "(b&s)", &succeeded, &reason); 46 | if (!succeeded) { 47 | tcmu_err("Failed to register method for '%s(%s)': %s", 48 | handler->name, 49 | handler->subtype, 50 | reason); 51 | ret = false; 52 | } 53 | g_variant_unref(result); 54 | return ret; 55 | } 56 | 57 | static void tcmulib_reg_fail(struct tcmulib_context *ctx) 58 | { 59 | // TODO: Report failures back to process performing registration 60 | } 61 | 62 | static gboolean 63 | tcmulib_check_config(TCMUService1 *interface, 64 | GDBusMethodInvocation *invocation, 65 | gchar *cfgstring, 66 | gpointer user_data) 67 | { 68 | struct tcmulib_handler *handler = user_data; 69 | char *reason = NULL; 70 | bool ok; 71 | 72 | ok = handler->check_config ? 73 | handler->check_config(cfgstring, &reason) : 74 | TRUE; 75 | g_dbus_method_invocation_return_value(invocation, 76 | g_variant_new("(bs)", ok, reason ? : (ok ? "OK" : "unknown"))); 77 | free(reason); 78 | return TRUE; 79 | } 80 | 81 | static void 82 | tcmulib_reg_bus_acquired(GDBusConnection *connection, 83 | const gchar *name, 84 | gpointer user_data) 85 | { 86 | struct tcmulib_handler *handler = user_data; 87 | struct tcmulib_context *ctx = handler->ctx; 88 | char *obj_path; 89 | TCMUService1 *interface; 90 | GError *error = NULL; 91 | gboolean r; 92 | 93 | interface = tcmuservice1_skeleton_new(); 94 | 95 | obj_path = g_strdup_printf("/org/kernel/TCMUService1/HandlerManager1/%s", 96 | handler->subtype); 97 | 98 | g_signal_connect(interface, 99 | "handle-check-config", 100 | G_CALLBACK(tcmulib_check_config), 101 | handler); /* user_data */ 102 | 103 | /* Export our object with org.kernel.TCMUService1 interface. */ 104 | r = g_dbus_interface_skeleton_export( 105 | G_DBUS_INTERFACE_SKELETON(interface), 106 | connection, 107 | obj_path, 108 | &error); 109 | g_free(obj_path); 110 | if (!r) 111 | tcmulib_reg_fail(ctx); 112 | } 113 | 114 | static void 115 | tcmulib_reg_name_acquired(GDBusConnection *connection, 116 | const gchar *name, 117 | gpointer user_data) 118 | { 119 | struct tcmulib_handler *handler = user_data; 120 | struct tcmulib_context *ctx = handler->ctx; 121 | 122 | handler->connection = connection; 123 | 124 | if (ctx->connection) { 125 | /* The primary service is already available. Register immediately. */ 126 | tcmulib_register_handler(ctx, handler); 127 | } 128 | } 129 | 130 | static void 131 | tcmulib_reg_name_lost(GDBusConnection *connection, 132 | const gchar *name, 133 | gpointer user_data) 134 | { 135 | struct tcmulib_handler *handler = user_data; 136 | handler->connection = NULL; 137 | } 138 | 139 | static void tcmulib_handler_own_bus(struct tcmulib_handler *handler) 140 | { 141 | char *bus_name; 142 | bus_name = g_strdup_printf("org.kernel.TCMUService1.HandlerManager1.%s", 143 | handler->subtype); 144 | g_bus_own_name(G_BUS_TYPE_SYSTEM, 145 | bus_name, 146 | G_BUS_NAME_OWNER_FLAGS_NONE, 147 | tcmulib_reg_bus_acquired, 148 | tcmulib_reg_name_acquired, 149 | tcmulib_reg_name_lost, 150 | handler, NULL); 151 | g_free(bus_name); 152 | } 153 | 154 | static void 155 | tcmulib_reg_name_appeared(GDBusConnection *connection, 156 | const gchar *name, 157 | const gchar *name_owner, 158 | gpointer user_data) 159 | { 160 | struct tcmulib_context *ctx = user_data; 161 | struct tcmulib_handler *handler; 162 | 163 | ctx->connection = connection; 164 | /* Primary TCMU service is now available. None of the handlers are 165 | registered, so register all handlers that have acquired their bus name */ 166 | darray_foreach(handler, ctx->handlers) { 167 | if (handler->connection) { 168 | tcmulib_register_handler(ctx, handler); 169 | } 170 | } 171 | } 172 | 173 | static void 174 | tcmulib_reg_name_vanished(GDBusConnection *connection, 175 | const gchar *name, 176 | gpointer user_data) 177 | { 178 | struct tcmulib_context *ctx = user_data; 179 | 180 | ctx->connection = NULL; 181 | } 182 | 183 | void tcmulib_register(struct tcmulib_context *ctx) 184 | { 185 | struct tcmulib_handler *handler; 186 | 187 | /* Start acquiring buses for each subtype owned by this context. */ 188 | darray_foreach(handler, ctx->handlers) { 189 | tcmulib_handler_own_bus(handler); 190 | } 191 | 192 | /* Start waiting for the primary service to become available */ 193 | g_bus_watch_name(G_BUS_TYPE_SYSTEM, 194 | "org.kernel.TCMUService1", 195 | G_BUS_NAME_WATCHER_FLAGS_NONE, 196 | tcmulib_reg_name_appeared, 197 | tcmulib_reg_name_vanished, 198 | ctx, 199 | NULL); 200 | } 201 | -------------------------------------------------------------------------------- /libtcmu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | /* 10 | * This header defines the libtcmu API. 11 | */ 12 | 13 | #ifndef __LIBTCMU_H 14 | #define __LIBTCMU_H 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "libtcmu_common.h" 25 | 26 | enum tcmulib_cfg_type { 27 | TCMULIB_CFG_DEV_CFGSTR, 28 | TCMULIB_CFG_DEV_SIZE, 29 | TCMULIB_CFG_WRITE_CACHE, 30 | }; 31 | 32 | struct tcmulib_cfg_info { 33 | enum tcmulib_cfg_type type; 34 | 35 | union { 36 | uint64_t dev_size; 37 | char *dev_cfgstring; 38 | bool write_cache; 39 | } data; 40 | }; 41 | 42 | struct tcmulib_handler { 43 | const char *name; /* Human-friendly name */ 44 | const char *subtype; /* Name for cfgstring matching */ 45 | const char *cfg_desc; /* Description of this backstore's config string */ 46 | 47 | struct tcmulib_context *ctx; /* The context this handler is added to, 48 | used internally by libtcmu. */ 49 | 50 | /* 51 | * As much as possible, check that the cfgstring will result 52 | * in a working device when given to us as dev->cfgstring in 53 | * the ->open() call. 54 | * 55 | * This function is optional but gives configuration tools a 56 | * chance to warn users in advance if the device they're 57 | * trying to create is invalid. 58 | * 59 | * Returns true if string is valid. Only if false, set *reason 60 | * to a string that says why. The string will be free()ed. 61 | * Suggest using asprintf(). 62 | */ 63 | bool (*check_config)(const char *cfgstring, char **reason); 64 | 65 | int (*reconfig)(struct tcmu_device *dev, struct tcmulib_cfg_info *cfg); 66 | 67 | bool (*update_logdir)(void); 68 | 69 | /* Per-device added/removed callbacks */ 70 | int (*added)(struct tcmu_device *dev); 71 | void (*removed)(struct tcmu_device *dev); 72 | 73 | void *hm_private; /* private ptr for handler module */ 74 | void *connection; /* private, dbus connection for this subtype */ 75 | }; 76 | 77 | /* 78 | * APIs for libtcmu only 79 | * 80 | * Use these functions to handle TCMU devices and events within an 81 | * existing program's event loop. 82 | */ 83 | 84 | /* Opaque (private) type */ 85 | struct tcmulib_context; 86 | 87 | /* Claim subtypes you wish to handle. Returns libtcmu's master fd or -error.*/ 88 | struct tcmulib_context *tcmulib_initialize( 89 | struct tcmulib_handler *handlers, 90 | size_t handler_count); 91 | 92 | /* Register to TCMU DBus service, for the claimed subtypes to be configurable 93 | * in targetcli. */ 94 | void tcmulib_register(struct tcmulib_context *ctx); 95 | 96 | /* Gets the master file descriptor used by tcmulib. */ 97 | int tcmulib_get_master_fd(struct tcmulib_context *ctx); 98 | 99 | /* 100 | * Call this when the master fd becomes ready, from your main thread. 101 | * Handlers' callbacks may be called before it returns. 102 | */ 103 | int tcmulib_master_fd_ready(struct tcmulib_context *ctx); 104 | 105 | /* 106 | * When a device fd becomes ready, call this to get SCSI cmd info in 107 | * 'cmd' struct. libtcmu will allocate hm_cmd_size bytes for each cmd 108 | * that can be accessed via cmd->hm_private pointer. The memory at 109 | * hm_private will be freed in tcmulib_command_complete. 110 | * 111 | * Repeat until it returns false. 112 | */ 113 | struct tcmulib_cmd *tcmulib_get_next_command(struct tcmu_device *dev, 114 | int hm_cmd_size); 115 | 116 | /* 117 | * Mark the command as complete. 118 | * Must be called before get_next_command() is called again. 119 | * 120 | * result is TCMU_STS value from libtcmu_common.h. If TCMU_STS_PASSTHROUGH_ERR 121 | * is returned then the caller must setup the tcmulib_cmd->sense_buf. 122 | */ 123 | void tcmulib_command_complete(struct tcmu_device *dev, struct tcmulib_cmd *cmd, int result); 124 | 125 | /* Call when start processing commands (before calling tcmulib_get_next_command()) */ 126 | void tcmulib_processing_start(struct tcmu_device *dev); 127 | 128 | /* Call when complete processing commands (tcmulib_get_next_command() returned NULL) */ 129 | void tcmulib_processing_complete(struct tcmu_device *dev); 130 | 131 | /* Clean up loose ends when exiting */ 132 | void tcmulib_close(struct tcmulib_context *ctx); 133 | 134 | #ifdef __cplusplus 135 | } 136 | #endif 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /libtcmu_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | /* 10 | * APIs for both libtcmu users and tcmu-runner plugins to use. 11 | */ 12 | 13 | #ifndef __LIBTCMU_COMMON_H 14 | #define __LIBTCMU_COMMON_H 15 | 16 | #include 17 | #include 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | struct tcmu_device; 24 | struct tgt_port; 25 | struct tcmulib_cmd; 26 | 27 | /* 28 | * TCMU return status codes 29 | */ 30 | enum { 31 | TCMU_STS_ASYNC_HANDLED = -2, 32 | TCMU_STS_NOT_HANDLED = -1, 33 | TCMU_STS_OK = 0, 34 | TCMU_STS_NO_RESOURCE, 35 | /* handler has setup sense. */ 36 | TCMU_STS_PASSTHROUGH_ERR, 37 | TCMU_STS_BUSY, 38 | TCMU_STS_WR_ERR, 39 | TCMU_STS_RD_ERR, 40 | TCMU_STS_MISCOMPARE, 41 | TCMU_STS_INVALID_CMD, 42 | TCMU_STS_INVALID_CDB, 43 | TCMU_STS_INVALID_PARAM_LIST, 44 | TCMU_STS_INVALID_PARAM_LIST_LEN, 45 | TCMU_STS_TIMEOUT, 46 | TCMU_STS_FENCED, 47 | TCMU_STS_HW_ERR, 48 | TCMU_STS_RANGE, 49 | TCMU_STS_FRMT_IN_PROGRESS, 50 | TCMU_STS_CAPACITY_CHANGED, 51 | TCMU_STS_NOTSUPP_SAVE_PARAMS, 52 | TCMU_STS_WR_ERR_INCOMPAT_FRMT, 53 | TCMU_STS_TRANSITION, 54 | TCMU_STS_IMPL_TRANSITION_ERR, 55 | TCMU_STS_EXPL_TRANSITION_ERR, 56 | TCMU_STS_NO_LOCK_HOLDERS, 57 | /* xcopy specific errors */ 58 | TCMU_STS_NOTSUPP_SEG_DESC_TYPE, 59 | TCMU_STS_NOTSUPP_TGT_DESC_TYPE, 60 | TCMU_STS_CP_TGT_DEV_NOTCONN, 61 | TCMU_STS_INVALID_CP_TGT_DEV_TYPE, 62 | TCMU_STS_TOO_MANY_SEG_DESC, 63 | TCMU_STS_TOO_MANY_TGT_DESC, 64 | }; 65 | 66 | #define TCMU_THREAD_NAME_LEN 16 67 | 68 | #define SENSE_BUFFERSIZE 96 69 | 70 | #define CFGFS_ROOT "/sys/kernel/config/target" 71 | #define CFGFS_CORE CFGFS_ROOT"/core" 72 | 73 | #define CFGFS_TARGET_MOD "/sys/module/target_core_user" 74 | #define CFGFS_MOD_PARAM CFGFS_TARGET_MOD"/parameters" 75 | 76 | #define max(a, b) ({ \ 77 | __typeof__ (a) _a = (a); \ 78 | __typeof__ (b) _b = (b); \ 79 | (void) (&_a == &_b); \ 80 | _a < _b ? _b : _a; }) 81 | 82 | #define min(a, b) ({ \ 83 | __typeof__ (a) _a = (a); \ 84 | __typeof__ (b) _b = (b); \ 85 | (void) (&_a == &_b); \ 86 | _a < _b ? _a : _b; }) 87 | 88 | #define round_up(a, b) ({ \ 89 | __typeof__ (a) _a = (a); \ 90 | __typeof__ (b) _b = (b); \ 91 | ((_a + (_b - 1)) / _b) * _b; }) 92 | 93 | #define round_down(a, b) ({ \ 94 | __typeof__ (a) _a = (a); \ 95 | __typeof__ (b) _b = (b); \ 96 | (_a - (_a % _b)); }) 97 | 98 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 99 | 100 | struct tcmulib_cmd { 101 | uint16_t cmd_id; 102 | uint8_t *cdb; 103 | struct iovec *iovec; 104 | size_t iov_cnt; 105 | uint8_t sense_buf[SENSE_BUFFERSIZE]; 106 | void *hm_private; 107 | }; 108 | 109 | /* Set/Get methods for the opaque tcmu_device */ 110 | void *tcmu_dev_get_private(struct tcmu_device *dev); 111 | void tcmu_dev_set_private(struct tcmu_device *dev, void *priv); 112 | const char *tcmu_dev_get_uio_name(struct tcmu_device *dev); 113 | void tcmu_set_thread_name(const char *prefix, struct tcmu_device *dev); 114 | int tcmu_dev_get_fd(struct tcmu_device *dev); 115 | char *tcmu_dev_get_memory_info(struct tcmu_device *dev, void **base, 116 | size_t *len, off_t *offset); 117 | char *tcmu_dev_get_cfgstring(struct tcmu_device *dev); 118 | void tcmu_dev_set_num_lbas(struct tcmu_device *dev, uint64_t num_lbas); 119 | uint64_t tcmu_dev_get_num_lbas(struct tcmu_device *dev); 120 | void tcmu_dev_set_block_size(struct tcmu_device *dev, uint32_t block_size); 121 | uint32_t tcmu_dev_get_block_size(struct tcmu_device *dev); 122 | uint64_t tcmu_lba_to_byte(struct tcmu_device *dev, uint64_t lba); 123 | uint64_t tcmu_byte_to_lba(struct tcmu_device *dev, uint64_t byte); 124 | void tcmu_dev_set_max_xfer_len(struct tcmu_device *dev, uint32_t len); 125 | uint32_t tcmu_dev_get_max_xfer_len(struct tcmu_device *dev); 126 | void tcmu_dev_set_opt_xcopy_rw_len(struct tcmu_device *dev, uint32_t len); 127 | uint32_t tcmu_dev_get_opt_xcopy_rw_len(struct tcmu_device *dev); 128 | void tcmu_dev_set_max_unmap_len(struct tcmu_device *dev, uint32_t len); 129 | uint32_t tcmu_dev_get_max_unmap_len(struct tcmu_device *dev); 130 | void tcmu_dev_set_opt_unmap_gran(struct tcmu_device *dev, uint32_t len, 131 | bool split); 132 | uint32_t tcmu_dev_get_opt_unmap_gran(struct tcmu_device *dev); 133 | void tcmu_dev_set_unmap_gran_align(struct tcmu_device *dev, uint32_t len); 134 | uint32_t tcmu_dev_get_unmap_gran_align(struct tcmu_device *dev); 135 | void tcmu_dev_set_write_cache_enabled(struct tcmu_device *dev, bool enabled); 136 | bool tcmu_dev_get_write_cache_enabled(struct tcmu_device *dev); 137 | void tcmu_dev_set_solid_state_media(struct tcmu_device *dev, bool solid_state); 138 | bool tcmu_dev_get_solid_state_media(struct tcmu_device *dev); 139 | void tcmu_dev_set_unmap_enabled(struct tcmu_device *dev, bool enabled); 140 | bool tcmu_dev_get_unmap_enabled(struct tcmu_device *dev); 141 | void tcmu_dev_set_write_protect_enabled(struct tcmu_device *dev, bool enabled); 142 | bool tcmu_dev_get_write_protect_enabled(struct tcmu_device *dev); 143 | struct tcmulib_handler *tcmu_dev_get_handler(struct tcmu_device *dev); 144 | void tcmu_dev_flush_ring(struct tcmu_device *dev); 145 | bool tcmu_dev_oooc_supported(struct tcmu_device* dev); 146 | 147 | /* Set/Get methods for interacting with configfs */ 148 | char *tcmu_cfgfs_get_str(const char *path); 149 | int tcmu_cfgfs_set_str(const char *path, const char *val, int val_len); 150 | int tcmu_cfgfs_get_int(const char *path); 151 | int tcmu_cfgfs_set_u32(const char *path, uint32_t val); 152 | int tcmu_cfgfs_dev_get_attr_int(struct tcmu_device *dev, const char *name); 153 | int tcmu_cfgfs_dev_exec_action(struct tcmu_device *dev, const char *name, 154 | uint32_t val); 155 | int tcmu_cfgfs_dev_set_ctrl_u64(struct tcmu_device *dev, const char *key, 156 | uint64_t val); 157 | uint64_t tcmu_cfgfs_dev_get_info_u64(struct tcmu_device *dev, const char *name, 158 | int *fn_ret); 159 | char *tcmu_cfgfs_dev_get_wwn(struct tcmu_device *dev); 160 | int tcmu_cfgfs_mod_param_set_u32(const char *name, uint32_t val); 161 | 162 | /* Helper routines for processing commands */ 163 | 164 | /* SCSI CDB processing */ 165 | int tcmu_cdb_get_length(uint8_t *cdb); 166 | uint64_t tcmu_cdb_get_lba(uint8_t *cdb); 167 | uint32_t tcmu_cdb_get_xfer_length(uint8_t *cdb); 168 | void tcmu_cdb_print_info(struct tcmu_device *dev, const struct tcmulib_cmd *cmd, 169 | const char *info); 170 | uint64_t tcmu_cdb_to_byte(struct tcmu_device *dev, uint8_t *cdb); 171 | 172 | /* iovec processing */ 173 | off_t tcmu_iovec_compare(void *mem, struct iovec *iovec, size_t size); 174 | size_t tcmu_iovec_seek(struct iovec *iovec, size_t count); 175 | void tcmu_iovec_zero(struct iovec *iovec, size_t iov_cnt); 176 | bool tcmu_iovec_zeroed(struct iovec *iovec, size_t iov_cnt); 177 | size_t tcmu_iovec_length(struct iovec *iovec, size_t iov_cnt); 178 | 179 | /* memory mangement */ 180 | size_t tcmu_memcpy_into_iovec(struct iovec *iovec, size_t iov_cnt, void *src, 181 | size_t len); 182 | size_t tcmu_memcpy_from_iovec(void *dest, size_t len, struct iovec *iovec, 183 | size_t iov_cnt); 184 | 185 | /* tcmulib_cmd processing */ 186 | void tcmu_cmd_seek(struct tcmulib_cmd *cmd, size_t count); 187 | 188 | /* SCSI Sense */ 189 | int tcmu_sense_set_data(uint8_t *sense_buf, uint8_t key, uint16_t asc_ascq); 190 | void tcmu_sense_set_info(uint8_t *sense_buf, uint32_t info); 191 | void tcmu_sense_set_key_specific_info(uint8_t *sense_buf, uint16_t info); 192 | void __tcmu_sense_set_data(uint8_t *sense_buf, uint8_t key, uint16_t asc_ascq); 193 | 194 | /* 195 | * Misc 196 | */ 197 | void tcmu_thread_cancel(pthread_t thread); 198 | 199 | extern __thread int __tcmu_is_ework_thread; 200 | 201 | #ifdef __cplusplus 202 | } 203 | #endif 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /libtcmu_config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 China Mobile, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #define _GNU_SOURCE 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "darray.h" 23 | #include "libtcmu_config.h" 24 | #include "libtcmu_log.h" 25 | #include "libtcmu_common.h" 26 | 27 | #include "ccan/list/list.h" 28 | 29 | #define TCMU_CONFIG_DIR_DEFAULT "/etc/tcmu" 30 | #define TCMU_CONFIG_FILE_DEFAULT TCMU_CONFIG_DIR_DEFAULT"/tcmu.conf" 31 | 32 | typedef enum { 33 | TCMU_OPT_NONE = 0, 34 | TCMU_OPT_INT, /* type int */ 35 | TCMU_OPT_STR, /* type string */ 36 | TCMU_OPT_BOOL, /* type boolean */ 37 | TCMU_OPT_MAX, 38 | } tcmu_option_type; 39 | 40 | struct tcmu_conf_option { 41 | struct list_node list; 42 | 43 | char *key; 44 | tcmu_option_type type; 45 | union { 46 | int opt_int; 47 | bool opt_bool; 48 | char *opt_str; 49 | }; 50 | }; 51 | 52 | /* 53 | * System config for TCMU, for now there are only 3 option types supported: 54 | * 1, The "int type" option, for example: 55 | * log_level = 2 56 | * 57 | * 2, The "string type" option, for example: 58 | * tcmu_str = "Tom" --> Tom 59 | * or 60 | * tcmu_str = 'Tom' --> Tom 61 | * or 62 | * tcmu_str = 'Tom is a "boy"' ---> Tom is a "boy" 63 | * or 64 | * tcmu_str = "'T' is short for Tom" --> 'T' is short for Tom 65 | * 66 | * 3, The "boolean type" option, for example: 67 | * tcmu_bool 68 | * 69 | * ======================== 70 | * How to add new options ? 71 | * 72 | * Using "log_level" as an example: 73 | * 74 | * 1, Add log_level member in: 75 | * struct tcmu_config { 76 | * int log_level; 77 | * }; 78 | * in file libtcmu_config.h. 79 | * 80 | * 2, Add the following option in "tcmu.conf" file as default: 81 | * log_level = 2 82 | * or 83 | * # log_level = 2 84 | * 85 | * Note: the option name in config file must be the same as in 86 | * tcmu_config. 87 | * 88 | * 3, You should add your own set method in: 89 | * static void tcmu_conf_set_options(struct tcmu_config *cfg) 90 | * { 91 | * TCMU_PARSE_CFG_INT(cfg, log_level); 92 | * TCMU_CONF_CHECK_LOG_LEVEL(log_level); 93 | * } 94 | * 95 | * Note: For now, if the options have been changed in config file, the 96 | * system config reload thread daemon will try to update them for all the 97 | * tcmu-runner, consumer and tcmu-synthesizer daemons. 98 | */ 99 | 100 | static LIST_HEAD(tcmu_options); 101 | 102 | static struct tcmu_conf_option * tcmu_get_option(const char *key) 103 | { 104 | struct tcmu_conf_option *option; 105 | 106 | list_for_each(&tcmu_options, option, list) { 107 | if (!strcmp(option->key, key)) 108 | return option; 109 | } 110 | 111 | return NULL; 112 | } 113 | 114 | static struct tcmu_conf_option * 115 | tcmu_register_option(char *key, tcmu_option_type type) 116 | { 117 | struct tcmu_conf_option *option; 118 | 119 | option = calloc(1, sizeof(*option)); 120 | if (!option) 121 | return NULL; 122 | 123 | option->key = strdup(key); 124 | if (!option->key) 125 | goto free_option; 126 | option->type = type; 127 | list_node_init(&option->list); 128 | 129 | list_add_tail(&tcmu_options, &option->list); 130 | return option; 131 | 132 | free_option: 133 | free(option); 134 | return NULL; 135 | } 136 | 137 | /* The default value should be specified here, 138 | * so the next time when users comment out an 139 | * option in config file, here it will set the 140 | * default value back. 141 | */ 142 | #define TCMU_PARSE_CFG_INT(cfg, key) \ 143 | do { \ 144 | struct tcmu_conf_option *option; \ 145 | option = tcmu_get_option(#key); \ 146 | if (!option) { \ 147 | option = tcmu_register_option(#key, TCMU_OPT_INT); \ 148 | cfg->key = cfg->def_##key; \ 149 | } else { \ 150 | cfg->key = option->opt_int; \ 151 | } \ 152 | option->opt_int = cfg->def_##key;\ 153 | } while (0) 154 | 155 | #define TCMU_PARSE_CFG_BOOL(cfg, key) \ 156 | do { \ 157 | struct tcmu_conf_option *option; \ 158 | option = tcmu_get_option(#key); \ 159 | if (!option) { \ 160 | option = tcmu_register_option(#key, TCMU_OPT_BOOL); \ 161 | cfg->key = cfg->def_##key; \ 162 | } else { \ 163 | cfg->key = option->opt_bool; \ 164 | } \ 165 | option->opt_bool = cfg->def_##key; \ 166 | } while (0) 167 | 168 | #define TCMU_PARSE_CFG_STR(cfg, key) \ 169 | do { \ 170 | struct tcmu_conf_option *option; \ 171 | option = tcmu_get_option(#key); \ 172 | memset(cfg->key, 0, sizeof(cfg->key)); \ 173 | if (!option) { \ 174 | option = tcmu_register_option(#key, TCMU_OPT_STR); \ 175 | snprintf(cfg->key, sizeof(cfg->key), "%s", cfg->def_##key); \ 176 | } else { \ 177 | snprintf(cfg->key, sizeof(cfg->key), "%s", option->opt_str); \ 178 | if (option->opt_str) \ 179 | free(option->opt_str); \ 180 | } \ 181 | option->opt_str = strdup(cfg->def_##key); \ 182 | } while (0); 183 | 184 | static void tcmu_conf_set_options(struct tcmu_config *cfg) 185 | { 186 | /* set log_level option */ 187 | TCMU_PARSE_CFG_INT(cfg, log_level); 188 | tcmu_set_log_level(cfg->log_level); 189 | 190 | /* set log_dir path option */ 191 | TCMU_PARSE_CFG_STR(cfg, log_dir); 192 | tcmu_resetup_log_file(cfg, cfg->log_dir); 193 | 194 | /* add your new config options */ 195 | } 196 | 197 | #define TCMU_MAX_CFG_FILE_SIZE (2 * 1024 * 1024) 198 | static int tcmu_read_config(int fd, char *buf, int count) 199 | { 200 | ssize_t len; 201 | int save = errno; 202 | 203 | do { 204 | len = read(fd, buf, count); 205 | } while (len < 0 && errno == EAGAIN); 206 | 207 | errno = save; 208 | return len; 209 | } 210 | 211 | /* end of line */ 212 | #define __EOL(c) (((c) == '\n') || ((c) == '\r')) 213 | 214 | #define TCMU_TO_LINE_END(x, y) \ 215 | do { while ((x) < (y) && !__EOL(*(x))) { \ 216 | (x)++; } \ 217 | } while (0); 218 | 219 | /* skip blank lines */ 220 | #define TCMU_SKIP_BLANK_LINES(x, y) \ 221 | do { while ((x) < (y) && (isblank(*(x)) || __EOL(*(x)))) { \ 222 | (x)++; } \ 223 | } while (0); 224 | 225 | /* skip comment line with '#' */ 226 | #define TCMU_SKIP_COMMENT_LINE(x, y) \ 227 | do { while ((x) < (y) && !__EOL(*x)) { \ 228 | (x)++; } \ 229 | (x)++; \ 230 | } while (0); 231 | 232 | /* skip comment lines with '#' */ 233 | #define TCMU_SKIP_COMMENT_LINES(x, y) \ 234 | do { while ((x) < (y) && *(x) == '#') { \ 235 | TCMU_SKIP_COMMENT_LINE((x), (y)); } \ 236 | } while (0); 237 | 238 | static void tcmu_parse_option(char **cur, const char *end) 239 | { 240 | struct tcmu_conf_option *option; 241 | tcmu_option_type type; 242 | char *p = *cur, *q = *cur, *r, *s; 243 | 244 | while (isblank(*p)) 245 | p++; 246 | 247 | TCMU_TO_LINE_END(q, end); 248 | *q = '\0'; 249 | *cur = q + 1; 250 | 251 | /* parse the boolean type option */ 252 | s = r = strchr(p, '='); 253 | if (!r) { 254 | /* boolean type option at file end or line end */ 255 | r = p; 256 | while (!isblank(*r) && r < q) 257 | r++; 258 | *r = '\0'; 259 | option = tcmu_get_option(p); 260 | if (!option) 261 | option = tcmu_register_option(p, TCMU_OPT_BOOL); 262 | 263 | if (option) 264 | option->opt_bool = true; 265 | 266 | return; 267 | } 268 | /* skip character '=' */ 269 | s++; 270 | r--; 271 | while (isblank(*r)) 272 | r--; 273 | r++; 274 | *r = '\0'; 275 | 276 | option = tcmu_get_option(p); 277 | if (!option) { 278 | r = s; 279 | while (isblank(*r)) 280 | r++; 281 | 282 | if (isdigit(*r)) 283 | type = TCMU_OPT_INT; 284 | else 285 | type = TCMU_OPT_STR; 286 | 287 | option = tcmu_register_option(p, type); 288 | if (!option) 289 | return; 290 | } 291 | 292 | /* parse the int/string type options */ 293 | switch (option->type) { 294 | case TCMU_OPT_INT: 295 | while (!isdigit(*s)) 296 | s++; 297 | r = s; 298 | while (isdigit(*r)) 299 | r++; 300 | *r= '\0'; 301 | 302 | option->opt_int = atoi(s); 303 | break; 304 | case TCMU_OPT_STR: 305 | while (isblank(*s)) 306 | s++; 307 | /* skip first " or ' if exist */ 308 | if (*s == '"' || *s == '\'') 309 | s++; 310 | r = q - 1; 311 | while (isblank(*r)) 312 | r--; 313 | /* skip last " or ' if exist */ 314 | if (*r == '"' || *r == '\'') 315 | *r = '\0'; 316 | 317 | if (option->opt_str) 318 | /* free if this is reconfig */ 319 | free(option->opt_str); 320 | option->opt_str = strdup(s); 321 | break; 322 | default: 323 | tcmu_err("option type %d not supported!\n", option->type); 324 | break; 325 | } 326 | } 327 | 328 | static void tcmu_parse_options(struct tcmu_config *cfg, char *buf, int len) 329 | { 330 | char *cur = buf, *end = buf + len; 331 | 332 | while (cur < end) { 333 | /* skip blanks lines */ 334 | TCMU_SKIP_BLANK_LINES(cur, end); 335 | 336 | /* skip comments with '#' */ 337 | TCMU_SKIP_COMMENT_LINES(cur, end); 338 | 339 | if (cur >= end) 340 | break; 341 | 342 | if (!isalpha(*cur)) 343 | continue; 344 | 345 | /* parse the options from config file to tcmu_options[] */ 346 | tcmu_parse_option(&cur, end); 347 | } 348 | 349 | /* parse the options from tcmu_options[] to struct tcmu_config */ 350 | tcmu_conf_set_options(cfg); 351 | } 352 | 353 | int tcmu_load_config(struct tcmu_config *cfg) 354 | { 355 | int ret = -1; 356 | int fd, len; 357 | char *buf; 358 | int i; 359 | 360 | buf = malloc(TCMU_MAX_CFG_FILE_SIZE); 361 | if (!buf) 362 | return -ENOMEM; 363 | 364 | for (i = 0; i < 5; i++) { 365 | if ((fd = open(TCMU_CONFIG_FILE_DEFAULT, O_RDONLY)) == -1) { 366 | /* give a moment for editor to restore 367 | * the conf-file after edit and save */ 368 | sleep(1); 369 | continue; 370 | } 371 | break; 372 | } 373 | if (fd == -1) { 374 | tcmu_err("Failed to open file '%s', %m\n", 375 | TCMU_CONFIG_FILE_DEFAULT); 376 | goto free_buf; 377 | } 378 | 379 | len = tcmu_read_config(fd, buf, TCMU_MAX_CFG_FILE_SIZE); 380 | close(fd); 381 | if (len < 0) { 382 | tcmu_err("Failed to read file '%s'\n", TCMU_CONFIG_FILE_DEFAULT); 383 | goto free_buf; 384 | } 385 | 386 | buf[len] = '\0'; 387 | 388 | tcmu_parse_options(cfg, buf, len); 389 | 390 | ret = 0; 391 | free_buf: 392 | free(buf); 393 | return ret; 394 | } 395 | 396 | #define BUF_LEN 1024 397 | static void *dyn_config_start(void *arg) 398 | { 399 | struct tcmu_config *cfg = arg; 400 | int monitor, wd, len; 401 | char buf[BUF_LEN]; 402 | char *p; 403 | 404 | tcmu_set_thread_name("dyn-config", NULL); 405 | 406 | monitor = inotify_init(); 407 | if (monitor == -1) { 408 | tcmu_err("Failed to init inotify %m\n"); 409 | return NULL; 410 | } 411 | 412 | /* Editors (vim, nano ..) follow different approaches to save conf file. 413 | * The two commonly followed techniques are to overwrite the existing 414 | * file, or to write to a new file (.swp, .tmp ..) and move it to actual 415 | * file name later. In the later case, the inotify fails, because the 416 | * file it's been intended to watch no longer exists, as the new file 417 | * is a different file with just a same name. 418 | * To handle both the file save approaches mentioned above, it is better 419 | * we watch the directory and filter for MODIFY events. 420 | */ 421 | wd = inotify_add_watch(monitor, TCMU_CONFIG_DIR_DEFAULT, IN_MODIFY); 422 | if (wd == -1) { 423 | tcmu_err("Failed to add \"%s\" to inotify %m\n", 424 | TCMU_CONFIG_DIR_DEFAULT); 425 | return NULL; 426 | } 427 | 428 | tcmu_dbg("Inotify is watching \"%s\", wd: %d, mask: IN_MODIFY\n", 429 | TCMU_CONFIG_DIR_DEFAULT, wd); 430 | 431 | while (1) { 432 | struct inotify_event *event; 433 | 434 | len = read(monitor, buf, BUF_LEN); 435 | if (len == -1) { 436 | tcmu_warn("Failed to read inotify: %m\n"); 437 | continue; 438 | } 439 | 440 | for (p = buf; p < buf + len; 441 | p += sizeof(struct inotify_event) + event->len) { 442 | event = (struct inotify_event *)p; 443 | 444 | tcmu_dbg("event->mask: 0x%x\n", event->mask); 445 | 446 | if (event->wd != wd) 447 | continue; 448 | 449 | /* Try to reload the config file */ 450 | if (event->mask & IN_MODIFY) 451 | tcmu_load_config(cfg); 452 | } 453 | } 454 | 455 | return NULL; 456 | } 457 | 458 | int tcmu_watch_config(struct tcmu_config *cfg) 459 | { 460 | int ret; 461 | 462 | ret = pthread_create(&cfg->thread_id, NULL, dyn_config_start, cfg); 463 | if (ret) 464 | return -ret; 465 | return 0; 466 | } 467 | 468 | void tcmu_unwatch_config(struct tcmu_config *cfg) 469 | { 470 | tcmu_thread_cancel(cfg->thread_id); 471 | } 472 | 473 | struct tcmu_config* tcmu_initialize_config(void) 474 | { 475 | struct tcmu_config *cfg; 476 | char *log_dir; 477 | 478 | cfg = calloc(1, sizeof(*cfg)); 479 | if (cfg == NULL) { 480 | tcmu_err("allocating TCMU config failed: %m\n"); 481 | errno = ENOMEM; 482 | return NULL; 483 | } 484 | 485 | log_dir = getenv("TCMU_LOGDIR"); 486 | snprintf(cfg->def_log_dir, PATH_MAX, "%s", 487 | log_dir ? log_dir : TCMU_LOG_DIR_DEFAULT); 488 | cfg->def_log_level = TCMU_CONF_LOG_INFO; 489 | 490 | return cfg; 491 | } 492 | 493 | void tcmu_free_config(struct tcmu_config *cfg) 494 | { 495 | struct tcmu_conf_option *option, *next; 496 | 497 | if (!cfg) 498 | return; 499 | 500 | list_for_each_safe(&tcmu_options, option, next, list) { 501 | list_del(&option->list); 502 | 503 | if (option->type == TCMU_OPT_STR) 504 | free(option->opt_str); 505 | free(option->key); 506 | free(option); 507 | } 508 | 509 | free(cfg); 510 | } 511 | -------------------------------------------------------------------------------- /libtcmu_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 China Mobile, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #ifndef __TCMU_CONFIG_H 10 | # define __TCMU_CONFIG_H 11 | 12 | #include 13 | #include 14 | 15 | #include "ccan/list/list.h" 16 | 17 | struct tcmu_config { 18 | pthread_t thread_id; 19 | 20 | int log_level; 21 | int def_log_level; 22 | 23 | char log_dir[PATH_MAX]; 24 | char def_log_dir[PATH_MAX]; 25 | 26 | struct tcmulib_context *ctx; 27 | }; 28 | 29 | /* 30 | * There are 6 logging levels supported in tcmu.conf: 31 | * 0: CRIT 32 | * 1: ERROR 33 | * 2: WARNING 34 | * 3: INFO 35 | * 4: DEBUG 36 | * 5: DEBUG SCSI CMD 37 | */ 38 | enum { 39 | TCMU_CONF_LOG_LEVEL_MIN = 0, 40 | TCMU_CONF_LOG_CRIT = 0, 41 | TCMU_CONF_LOG_ERROR = 1, 42 | TCMU_CONF_LOG_WARN, 43 | TCMU_CONF_LOG_INFO, 44 | TCMU_CONF_LOG_DEBUG, 45 | TCMU_CONF_LOG_DEBUG_SCSI_CMD, 46 | TCMU_CONF_LOG_LEVEL_MAX = TCMU_CONF_LOG_DEBUG_SCSI_CMD, 47 | }; 48 | 49 | static const char *const log_level_lookup[] = { 50 | [TCMU_CONF_LOG_CRIT] = "CRIT", 51 | [TCMU_CONF_LOG_ERROR] = "ERROR", 52 | [TCMU_CONF_LOG_WARN] = "WARNING", 53 | [TCMU_CONF_LOG_INFO] = "INFO", 54 | [TCMU_CONF_LOG_DEBUG] = "DEBUG", 55 | [TCMU_CONF_LOG_DEBUG_SCSI_CMD] = "DEBUG SCSI CMD", 56 | }; 57 | 58 | struct tcmu_config* tcmu_initialize_config(void); 59 | void tcmu_free_config(struct tcmu_config *cfg); 60 | int tcmu_load_config(struct tcmu_config *cfg); 61 | int tcmu_watch_config(struct tcmu_config *cfg); 62 | void tcmu_unwatch_config(struct tcmu_config *cfg); 63 | 64 | #endif /* __TCMU_CONFIG_H */ 65 | -------------------------------------------------------------------------------- /libtcmu_log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 China Mobile, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #ifndef __TCMU_LOG_H 10 | #define __TCMU_LOG_H 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define TCMU_LOG_CRIT LOG_CRIT /* critical conditions */ 18 | #define TCMU_LOG_ERROR LOG_ERR /* error conditions */ 19 | #define TCMU_LOG_WARN LOG_WARNING /* warning conditions */ 20 | #define TCMU_LOG_INFO LOG_INFO /* informational */ 21 | #define TCMU_LOG_DEBUG LOG_DEBUG /* debug-level messages */ 22 | #define TCMU_LOG_DEBUG_SCSI_CMD (LOG_DEBUG + 1) /* scsi cmd debug-level messages */ 23 | 24 | /* default tcmu log dir path */ 25 | #define TCMU_LOG_DIR_DEFAULT "/var/log" 26 | 27 | struct tcmu_device; 28 | struct tcmu_config; 29 | 30 | void tcmu_set_log_level(int level); 31 | unsigned int tcmu_get_log_level(void); 32 | int tcmu_setup_log(char *log_dir); 33 | void tcmu_destroy_log(void); 34 | int tcmu_make_absolute_logfile(char *path, const char *filename); 35 | int tcmu_resetup_log_file(struct tcmu_config *cfg, char *log_dir); 36 | 37 | __attribute__ ((format (printf, 4, 5))) 38 | void tcmu_crit_message(struct tcmu_device *dev, const char *funcname, int linenr, const char *fmt, ...); 39 | __attribute__ ((format (printf, 4, 5))) 40 | void tcmu_err_message(struct tcmu_device *dev, const char *funcname, int linenr, const char *fmt, ...); 41 | __attribute__ ((format (printf, 4, 5))) 42 | void tcmu_warn_message(struct tcmu_device *dev, const char *funcname, int linenr, const char *fmt, ...); 43 | __attribute__ ((format (printf, 4, 5))) 44 | void tcmu_info_message(struct tcmu_device *dev, const char *funcname, int linenr, const char *fmt, ...); 45 | __attribute__ ((format (printf, 4, 5))) 46 | void tcmu_dbg_message(struct tcmu_device *dev, const char *funcname, int linenr, const char *fmt, ...); 47 | __attribute__ ((format (printf, 4, 5))) 48 | void tcmu_dbg_scsi_cmd_message(struct tcmu_device *dev, const char *funcname, int linenr, const char *fmt, ...); 49 | 50 | #define tcmu_dev_crit(dev, ...) do { tcmu_crit_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) 51 | #define tcmu_dev_err(dev, ...) do { tcmu_err_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) 52 | #define tcmu_dev_warn(dev, ...) do { tcmu_warn_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) 53 | #define tcmu_dev_info(dev, ...) do { tcmu_info_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) 54 | #define tcmu_dev_dbg(dev, ...) do { tcmu_dbg_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) 55 | #define tcmu_dev_dbg_scsi_cmd(dev, ...) do { tcmu_dbg_scsi_cmd_message(dev, __func__, __LINE__, __VA_ARGS__);} while (0) 56 | 57 | 58 | #define tcmu_crit(...) do { tcmu_crit_message(NULL, __func__, __LINE__, __VA_ARGS__);} while (0) 59 | #define tcmu_err(...) do { tcmu_err_message(NULL, __func__, __LINE__, __VA_ARGS__);} while (0) 60 | #define tcmu_warn(...) do { tcmu_warn_message(NULL, __func__, __LINE__, __VA_ARGS__);} while (0) 61 | #define tcmu_info(...) do { tcmu_info_message(NULL, __func__, __LINE__, __VA_ARGS__);} while (0) 62 | #define tcmu_dbg(...) do { tcmu_dbg_message(NULL, __func__, __LINE__, __VA_ARGS__);} while (0) 63 | #define tcmu_dbg_scsi_cmd(...) do { tcmu_dbg_scsi_cmd_message(NULL, __func__, __LINE__, __VA_ARGS__);} while (0) 64 | #endif /* __TCMU_LOG_H */ 65 | -------------------------------------------------------------------------------- /libtcmu_priv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | /* 10 | * This header defines structures private to libtcmu, and should not 11 | * be used by anyone else. 12 | */ 13 | 14 | #ifndef __LIBTCMU_PRIV_H 15 | #define __LIBTCMU_PRIV_H 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "darray.h" 23 | 24 | #define KERN_IFACE_VER 2 25 | 26 | // The full (private) declaration 27 | struct tcmulib_context { 28 | darray(struct tcmulib_handler) handlers; 29 | 30 | /* Just keep ptrs b/c we hand these to clients */ 31 | darray(struct tcmu_device*) devices; 32 | 33 | struct nl_sock *nl_sock; 34 | 35 | GDBusConnection *connection; 36 | }; 37 | 38 | struct tcmu_device { 39 | int fd; 40 | 41 | struct tcmu_mailbox *map; 42 | size_t map_len; 43 | 44 | uint32_t cmd_tail; 45 | 46 | uint64_t num_lbas; 47 | uint32_t block_size; 48 | uint32_t block_size_shift; 49 | uint32_t max_xfer_len; 50 | uint32_t opt_xcopy_rw_len; 51 | bool split_unmaps; 52 | uint32_t max_unmap_len; 53 | uint32_t opt_unmap_gran; 54 | uint32_t unmap_gran_align; 55 | unsigned int write_cache_enabled:1; 56 | unsigned int solid_state_media:1; 57 | unsigned int unmap_enabled:1; 58 | unsigned int write_protect_enabled:1; 59 | 60 | char dev_name[16]; /* e.g. "uio14" */ 61 | char tcm_hba_name[16]; /* e.g. "user_8" */ 62 | char tcm_dev_name[128]; /* e.g. "backup2" */ 63 | char cfgstring[PATH_MAX]; 64 | 65 | struct tcmulib_handler *handler; 66 | struct tcmulib_context *ctx; 67 | 68 | void *hm_private; /* private ptr for handler module */ 69 | }; 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /libtcmu_time.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 China Mobile, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "libtcmu_time.h" 16 | 17 | int time_string_now(char* buf) 18 | { 19 | struct tm *tm; 20 | struct timeval tv; 21 | 22 | if (gettimeofday (&tv, NULL) < 0) 23 | return -1; 24 | 25 | /* The value maybe changed in multi-thread*/ 26 | tm = localtime(&tv.tv_sec); 27 | if (tm == NULL) 28 | return -1; 29 | 30 | tm->tm_year += 1900; 31 | tm->tm_mon += 1; 32 | 33 | if (snprintf(buf, TCMU_TIME_STRING_BUFLEN, 34 | "%4d-%02d-%02d %02d:%02d:%02d.%03d", 35 | tm->tm_year, tm->tm_mon, tm->tm_mday, 36 | tm->tm_hour, tm->tm_min, tm->tm_sec, 37 | (int) (tv.tv_usec / 1000ull % 1000)) >= TCMU_TIME_STRING_BUFLEN) 38 | return ERANGE; 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /libtcmu_time.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 China Mobile, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | /* The time format string 10 | * 11 | * Yr Mon Day Hour Min Sec Ms 12 | * %4d-%02d-%02d %02d:%02d:%02d.%03d 13 | * 14 | */ 15 | 16 | #ifndef __TCMU_TIME_H 17 | #define __TCMU_TIME_H 18 | 19 | # define TCMU_TIME_STRING_BUFLEN \ 20 | (4 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 3 + 1) 21 | /* Yr Mon Day Hour Min Sec Ms NULL */ 22 | 23 | /* generate localtime string into buf */ 24 | int time_string_now(char* buf); 25 | 26 | #endif /* __TCMU_TIME_H */ 27 | -------------------------------------------------------------------------------- /logrotate.conf: -------------------------------------------------------------------------------- 1 | /var/log/tcmu-runner*.log { 2 | rotate 7 3 | size 10M 4 | compress 5 | missingok 6 | notifempty 7 | nodateext 8 | postrotate 9 | killall -q -s 1 tcmu-runner 10 | endscript 11 | } 12 | -------------------------------------------------------------------------------- /logrotate.conf_install.cmake.in: -------------------------------------------------------------------------------- 1 | if (EXISTS "/etc/logrotate.d/tcmu-runner") 2 | file(INSTALL "/etc/logrotate.d/tcmu-runner" DESTINATION "/etc/logrotate.d/tcmu-runner.bak" RENAME "tcmu-runner") 3 | endif() 4 | file(INSTALL "${PROJECT_SOURCE_DIR}/logrotate.conf" DESTINATION "/etc/logrotate.d" RENAME "tcmu-runner") 5 | -------------------------------------------------------------------------------- /main-syms.txt: -------------------------------------------------------------------------------- 1 | { 2 | tcmur_register_handler; 3 | tcmur_handle_caw; 4 | tcmur_handle_writesame; 5 | tcmu_notify_lock_lost; 6 | tcmu_notify_conn_lost; 7 | tcmu_notify_cmd_timed_out; 8 | tcmu_event_name; 9 | tcmur_dev_update_size; 10 | tcmur_dev_set_private; 11 | tcmur_dev_get_private; 12 | tcmur_cmd_complete; 13 | }; 14 | -------------------------------------------------------------------------------- /org.kernel.TCMUService1.service: -------------------------------------------------------------------------------- 1 | # When placed in /usr/share/dbus-1/system-services/, this should cause tcmu-runner 2 | # to be loaded and run by dbus-daemon when a client accesses the service name. 3 | 4 | [D-BUS Service] 5 | Name=org.kernel.TCMUService1 6 | Exec=/usr/bin/tcmu-runner 7 | User=root 8 | SystemdService=tcmu-runner.service 9 | -------------------------------------------------------------------------------- /qcow.h: -------------------------------------------------------------------------------- 1 | #ifndef _QCOW_H_ 2 | #define _QCOW_H_ 3 | 4 | #include 5 | #include "libtcmu_log.h" 6 | 7 | #define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) 8 | #define QCOW_VERSION 1 9 | 10 | #define QCOW_CRYPT_NONE 0 11 | #define QCOW_CRYPT_AES 1 12 | 13 | #define QCOW_OFLAG_COMPRESSED (1LL << 63) 14 | 15 | struct qcow_header 16 | { 17 | uint32_t magic; 18 | uint32_t version; 19 | uint64_t backing_file_offset; 20 | uint32_t backing_file_size; 21 | uint32_t mtime; 22 | uint64_t size; /* in bytes */ 23 | uint8_t cluster_bits; 24 | uint8_t l2_bits; 25 | uint16_t padding; 26 | uint32_t crypt_method; 27 | uint64_t l1_table_offset; 28 | } __attribute__((__packed__)); 29 | 30 | #define L2_CACHE_SIZE 16 31 | 32 | #endif /* _QCOW_H_ */ 33 | -------------------------------------------------------------------------------- /scsi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | /* 10 | * API used by tcmu-runner and example daemons 11 | */ 12 | 13 | #ifndef __TCMU_SCSI_H 14 | #define __TCMU_SCSI_H 15 | 16 | #include 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | 23 | /* Temporarily limit this to 32M */ 24 | #define VPD_MAX_UNMAP_LBA_COUNT (32 * 1024 * 1024) 25 | #define VPD_MAX_UNMAP_BLOCK_DESC_COUNT 0x04 26 | /* Temporarily limit this is 0x1 */ 27 | #define MAX_CAW_LENGTH 0x01 28 | 29 | #define VPD_MAX_WRITE_SAME_LENGTH 0xFFFFFFFF 30 | 31 | /* Basic implementations of mandatory SCSI commands */ 32 | bool char_to_hex(unsigned char *val, char c); 33 | struct tcmur_handler *tcmu_get_runner_handler(struct tcmu_device *dev); 34 | int tcmu_emulate_inquiry(struct tcmu_device *dev, struct tgt_port *port, uint8_t *cdb, struct iovec *iovec, size_t iov_cnt); 35 | int tcmu_emulate_start_stop(struct tcmu_device *dev, uint8_t *cdb); 36 | int tcmu_emulate_test_unit_ready(uint8_t *cdb, struct iovec *iovec, size_t iov_cnt); 37 | int tcmu_emulate_read_capacity_10(uint64_t num_lbas, uint32_t block_size, uint8_t *cdb, 38 | struct iovec *iovec, size_t iov_cnt); 39 | int tcmu_emulate_read_capacity_16(uint64_t num_lbas, uint32_t block_size, uint8_t *cdb, 40 | struct iovec *iovec, size_t iov_cnt); 41 | int tcmu_emulate_mode_sense(struct tcmu_device *dev, uint8_t *cdb, 42 | struct iovec *iovec, size_t iov_cnt); 43 | int tcmu_emulate_mode_select(struct tcmu_device *dev, uint8_t *cdb, 44 | struct iovec *iovec, size_t iov_cnt); 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /scsi_defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Additional values not defined by other headers, they 3 | * seem a little incomplete. 4 | * 5 | * Find codes in the various SCSI specs. 6 | * BTW sense codes are at www.t10.org/lists/asc-num.txt 7 | * 8 | */ 9 | #ifndef __TCMU_SCSI_DEFS 10 | #define __TCMU_SCSI_DEFS 11 | 12 | /* 13 | * SCSI Opcodes 14 | */ 15 | #define READ_FORMAT_CAPACITIES 0x23 16 | #define UNMAP 0x42 17 | #define GET_CONFIGURATION 0x46 18 | #define READ_DISC_INFORMATION 0x51 19 | #define MODE_SELECT_10 0x55 20 | #define MODE_SENSE_10 0x5a 21 | #define EXTENDED_COPY 0x83 22 | #define RECEIVE_COPY_RESULTS 0x84 /* RECEIVE COPY STATUS */ 23 | #define READ_16 0x88 24 | #define COMPARE_AND_WRITE 0x89 25 | #define WRITE_16 0x8a 26 | #define WRITE_VERIFY_16 0x8e 27 | #define SYNCHRONIZE_CACHE_16 0x91 28 | #define WRITE_SAME_16 0x93 29 | #define SERVICE_ACTION_IN_16 0x9e 30 | #define READ_DVD_STRUCTURE 0xad 31 | #define MECHANISM_STATUS 0xbd 32 | #define MAINTENANCE_IN 0xa3 33 | #define MAINTENANCE_OUT 0xa4 34 | #define MI_REPORT_TARGET_PGS 0x0a 35 | #define MO_SET_TARGET_PGS 0x0a 36 | 37 | /* 38 | * Receive Copy Results Sevice Actions 39 | */ 40 | #define RCR_SA_COPY_STATUS 0x00 41 | #define RCR_SA_RECEIVE_DATA 0x01 42 | #define RCR_SA_OPERATING_PARAMETERS 0x03 43 | #define RCR_SA_FAILED_SEGMENT_DETAILS 0x04 44 | 45 | /* 46 | * Receive Copy Results Operating Parameters 47 | */ 48 | #define RCR_OP_MAX_TARGET_DESC_COUNT 0x02 49 | #define RCR_OP_MAX_SEGMENT_DESC_COUNT 0x01 50 | #define RCR_OP_MAX_DESC_LIST_LEN 1024 51 | #define RCR_OP_MAX_SEGMENT_LEN 16777216 52 | #define RCR_OP_TOTAL_CONCURR_COPIES 0x01 53 | #define RCR_OP_MAX_CONCURR_COPIES 0x01 54 | #define RCR_OP_DATA_SEG_GRAN_LOG2 0x09 55 | #define RCR_OP_INLINE_DATA_GRAN_LOG2 0x09 56 | #define RCR_OP_HELD_DATA_GRAN_LOG2 0x09 57 | 58 | /* 59 | * Receive Copy Results descriptor type codes supports 60 | */ 61 | #define RCR_OP_IMPLE_DES_LIST_LENGTH 0x02 62 | #define XCOPY_SEG_DESC_TYPE_CODE_B2B 0x02 /* block --> block */ 63 | #define XCOPY_TARGET_DESC_TYPE_CODE_ID 0xe4 /* Identification descriptor */ 64 | 65 | /* 66 | * Service action opcodes 67 | */ 68 | #define READ_CAPACITY_16 0x10 69 | 70 | /* SCSI protocols; these are taken from SPC-3 section 7.5 */ 71 | enum scsi_protocol { 72 | SCSI_PROTOCOL_FCP = 0, /* Fibre Channel */ 73 | SCSI_PROTOCOL_SPI = 1, /* parallel SCSI */ 74 | SCSI_PROTOCOL_SSA = 2, /* Serial Storage Architecture - Obsolete */ 75 | SCSI_PROTOCOL_SBP = 3, /* firewire */ 76 | SCSI_PROTOCOL_SRP = 4, /* Infiniband RDMA */ 77 | SCSI_PROTOCOL_ISCSI = 5, 78 | SCSI_PROTOCOL_SAS = 6, 79 | SCSI_PROTOCOL_ADT = 7, /* Media Changers */ 80 | SCSI_PROTOCOL_ATA = 8, 81 | SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */ 82 | }; 83 | 84 | /* 85 | * SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft 86 | * T10/1561-D Revision 4 Draft dated 7th November 2002. 87 | */ 88 | #define SAM_STAT_GOOD 0x00 89 | #define SAM_STAT_CHECK_CONDITION 0x02 90 | #define SAM_STAT_CONDITION_MET 0x04 91 | #define SAM_STAT_BUSY 0x08 92 | #define SAM_STAT_INTERMEDIATE 0x10 93 | #define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 94 | #define SAM_STAT_RESERVATION_CONFLICT 0x18 95 | #define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */ 96 | #define SAM_STAT_TASK_SET_FULL 0x28 97 | #define SAM_STAT_ACA_ACTIVE 0x30 98 | #define SAM_STAT_TASK_ABORTED 0x40 99 | 100 | #define ALUA_ACCESS_STATE_OPTIMIZED 0x0 101 | #define ALUA_ACCESS_STATE_NON_OPTIMIZED 0x1 102 | #define ALUA_ACCESS_STATE_STANDBY 0x2 103 | #define ALUA_ACCESS_STATE_UNAVAILABLE 0x3 104 | #define ALUA_ACCESS_STATE_LBA_DEPENDENT 0x4 105 | #define ALUA_ACCESS_STATE_OFFLINE 0xe 106 | #define ALUA_ACCESS_STATE_TRANSITIONING 0xf 107 | 108 | #define ALUA_SUP_OPTIMIZED 0x01 109 | #define ALUA_SUP_NON_OPTIMIZED 0x02 110 | #define ALUA_SUP_STANDBY 0x04 111 | #define ALUA_SUP_UNAVAILABLE 0x08 112 | #define ALUA_SUP_LBA_DEPENDENT 0x10 113 | #define ALUA_SUP_OFFLINE 0x40 114 | #define ALUA_SUP_TRANSITIONING 0x80 115 | 116 | #define TPGS_ALUA_NONE 0x00 117 | #define TPGS_ALUA_IMPLICIT 0x10 118 | #define TPGS_ALUA_EXPLICIT 0x20 119 | 120 | #define ALUA_STAT_NONE 0x00 121 | #define ALUA_STAT_ALTERED_BY_EXPLICIT_STPG 0x01 122 | #define ALUA_STAT_ALTERED_BY_IMPLICIT_ALUA 0x02 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /string_priv.h: -------------------------------------------------------------------------------- 1 | #ifndef TCMU_STRING_PRIV_H 2 | #define TCMU_STRING_PRIV_H 3 | 4 | #include 5 | 6 | size_t strlcpy(char *dst, const char *src, size_t size); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /strlcpy.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | /* 23 | * Copy string src to buffer dst of size dsize. At most dsize-1 24 | * chars will be copied. Always NUL terminates (unless dsize == 0). 25 | * Returns strlen(src); if retval >= dsize, truncation occurred. 26 | */ 27 | size_t 28 | strlcpy(char *dst, const char *src, size_t dsize) 29 | { 30 | const char *osrc = src; 31 | size_t nleft = dsize; 32 | 33 | /* Copy as many bytes as will fit. */ 34 | if (nleft != 0) { 35 | while (--nleft != 0) { 36 | if ((*dst++ = *src++) == '\0') 37 | break; 38 | } 39 | } 40 | 41 | /* Not enough room in dst, add NUL and traverse rest of src. */ 42 | if (nleft == 0) { 43 | if (dsize != 0) 44 | *dst = '\0'; /* NUL-terminate dst */ 45 | while (*src++) 46 | ; 47 | } 48 | 49 | return(src - osrc - 1); /* count does not include NUL */ 50 | } 51 | -------------------------------------------------------------------------------- /target.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #define _GNU_SOURCE 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "ccan/list/list.h" 21 | 22 | #include "libtcmu_log.h" 23 | #include "libtcmu_common.h" 24 | #include "tcmur_device.h" 25 | #include "tcmur_work.h" 26 | #include "target.h" 27 | #include "alua.h" 28 | 29 | static struct list_head tpg_recovery_list = LIST_HEAD_INIT(tpg_recovery_list); 30 | /* 31 | * Locking ordering: 32 | * rdev->rdev_lock 33 | * tpg_recovery_lock 34 | */ 35 | static pthread_mutex_t tpg_recovery_lock = PTHREAD_MUTEX_INITIALIZER; 36 | 37 | struct tgt_port_grp { 38 | char *wwn; 39 | char *fabric; 40 | uint16_t tpgt; 41 | 42 | /* entry on tpg_recovery_list */ 43 | struct list_node recovery_entry; 44 | /* list of devs to recover */ 45 | struct list_head devs; 46 | pthread_t recovery_thread; 47 | }; 48 | 49 | static int tcmu_set_tpg_int(struct tgt_port_grp *tpg, const char *name, 50 | int val) 51 | { 52 | char path[PATH_MAX]; 53 | 54 | snprintf(path, sizeof(path), CFGFS_ROOT"/%s/%s/tpgt_%hu/%s", 55 | tpg->fabric, tpg->wwn, tpg->tpgt, name); 56 | return tcmu_cfgfs_set_u32(path, val); 57 | } 58 | 59 | static int tcmu_get_tpg_int(struct tgt_port *port, const char *name) 60 | { 61 | char path[PATH_MAX]; 62 | 63 | snprintf(path, sizeof(path), 64 | CFGFS_ROOT"/%s/%s/tpgt_%hu/%s", 65 | port->fabric, port->wwn, port->tpgt, name); 66 | return tcmu_cfgfs_get_int(path); 67 | } 68 | 69 | static int tcmu_get_lun_int_stat(struct tgt_port *port, uint64_t lun, 70 | const char *stat_name) 71 | { 72 | char path[PATH_MAX]; 73 | 74 | snprintf(path, sizeof(path), 75 | CFGFS_ROOT"/%s/%s/tpgt_%hu/lun/lun_%"PRIu64"/statistics/%s", 76 | port->fabric, port->wwn, port->tpgt, lun, stat_name); 77 | return tcmu_cfgfs_get_int(path); 78 | } 79 | 80 | void tcmu_free_tgt_port(struct tgt_port *port) 81 | { 82 | if (port->wwn) 83 | free(port->wwn); 84 | if (port->fabric) 85 | free(port->fabric); 86 | free(port); 87 | } 88 | 89 | struct tgt_port *tcmu_get_tgt_port(char *member_str) 90 | { 91 | struct tgt_port *port; 92 | char fabric[17], wwn[224]; 93 | uint64_t lun; 94 | uint16_t tpgt; 95 | int ret; 96 | 97 | if (!strlen(member_str)) 98 | return NULL; 99 | 100 | ret = sscanf(member_str, "%16[^/]/%223[^/]/tpgt_%hu/lun_%"PRIu64, 101 | fabric, wwn, &tpgt, &lun); 102 | if (ret != 4) { 103 | tcmu_err("Invalid ALUA member %s:%s\n", member_str, 104 | strerror(errno)); 105 | return NULL; 106 | } 107 | 108 | port = calloc(1, sizeof(*port)); 109 | if (!port) 110 | return NULL; 111 | list_node_init(&port->entry); 112 | 113 | if (!strcmp(fabric, "iSCSI")) 114 | /* 115 | * iSCSI's fabric name and target_core_fabric_ops name do 116 | * not match. 117 | */ 118 | port->fabric = strdup("iscsi"); 119 | else 120 | port->fabric = strdup(fabric); 121 | if (!port->fabric) 122 | goto free_port; 123 | 124 | port->wwn = strdup(wwn); 125 | if (!port->wwn) 126 | goto free_port; 127 | 128 | port->tpgt = tpgt; 129 | 130 | ret = tcmu_get_lun_int_stat(port, lun, "scsi_port/indx"); 131 | if (ret < 0) 132 | goto free_port; 133 | 134 | port->rel_port_id = ret; 135 | 136 | ret = tcmu_get_lun_int_stat(port, lun, "scsi_transport/proto_id"); 137 | if (ret < 0) 138 | goto free_port; 139 | port->proto_id = ret; 140 | 141 | ret = tcmu_get_tpg_int(port, "enable"); 142 | if (ret < 0) 143 | goto free_port; 144 | port->enabled = ret; 145 | 146 | return port; 147 | 148 | free_port: 149 | tcmu_free_tgt_port(port); 150 | return NULL; 151 | } 152 | 153 | static bool port_is_on_tgt_port_grp(struct tgt_port *port, 154 | struct tgt_port_grp *tpg) 155 | { 156 | if (!strcmp(port->fabric, tpg->fabric) && 157 | !strcmp(port->wwn, tpg->wwn) && port->tpgt == tpg->tpgt) 158 | return true; 159 | return false; 160 | } 161 | 162 | static struct tgt_port_grp *port_is_on_recovery_list(struct tgt_port *port) 163 | { 164 | struct tgt_port_grp *tpg; 165 | 166 | list_for_each(&tpg_recovery_list, tpg, recovery_entry) { 167 | if (port_is_on_tgt_port_grp(port, tpg)) 168 | return tpg; 169 | } 170 | return NULL; 171 | } 172 | 173 | static void free_tgt_port_grp(struct tgt_port_grp *tpg) 174 | { 175 | free(tpg->fabric); 176 | free(tpg->wwn); 177 | free(tpg); 178 | } 179 | 180 | static struct tgt_port_grp *setup_tgt_port_grp(struct tgt_port *port) 181 | { 182 | struct tgt_port_grp *tpg; 183 | 184 | tpg = calloc(1, sizeof(*tpg)); 185 | if (!tpg) 186 | goto fail; 187 | 188 | list_head_init(&tpg->devs); 189 | list_node_init(&tpg->recovery_entry); 190 | tpg->tpgt = port->tpgt; 191 | 192 | tpg->wwn = strdup(port->wwn); 193 | if (!tpg->wwn) 194 | goto free_tpg; 195 | 196 | tpg->fabric = strdup(port->fabric); 197 | if (!tpg->fabric) 198 | goto free_wwn; 199 | 200 | return tpg; 201 | 202 | free_wwn: 203 | free(tpg->wwn); 204 | free_tpg: 205 | free(tpg); 206 | fail: 207 | return NULL; 208 | } 209 | 210 | /* 211 | * Disable the target tpg to avoid flip flopping between paths 212 | * (transport path is ok so multipath layer switches to it, but 213 | * then sends IO only for it to fail due to the handler not 214 | * being able to reach its backend). 215 | */ 216 | static void tgt_port_grp_recovery_work_fn(void *arg) 217 | { 218 | struct tgt_port_grp *tpg = arg; 219 | struct tcmur_device *rdev, *tmp_rdev; 220 | bool enable_tpg = false; 221 | int ret; 222 | 223 | tcmu_dbg("Disabling %s/%s/tpgt_%hu.\n", tpg->fabric, tpg->wwn, 224 | tpg->tpgt); 225 | /* 226 | * This will return when all running commands have completed at 227 | * the target layer. Handlers must call tcmu_notify_lock_lost 228 | * before completing the failed command, so the device will be on 229 | * the list reopen list when setting enable=0 returns.. 230 | */ 231 | ret = tcmu_set_tpg_int(tpg, "enable", 0); 232 | 233 | pthread_mutex_lock(&tpg_recovery_lock); 234 | list_del(&tpg->recovery_entry); 235 | pthread_mutex_unlock(&tpg_recovery_lock); 236 | 237 | if (ret < 0) { 238 | tcmu_err("Could not disable %s/%s/tpgt_%hu (err %d).\n", 239 | tpg->fabric, tpg->wwn, tpg->tpgt, ret); 240 | /* just recover the devs and leave the tpg in curr state */ 241 | goto done; 242 | } 243 | 244 | enable_tpg = true; 245 | tcmu_info("Disabled %s/%s/tpgt_%hu.\n", tpg->fabric, tpg->wwn, 246 | tpg->tpgt); 247 | 248 | done: 249 | /* 250 | * TODO - the transport is stopped, so we should use the 251 | * cmdproc thread to reopen all these in parallel. 252 | */ 253 | list_for_each_safe(&tpg->devs, rdev, tmp_rdev, recovery_entry) { 254 | list_del(&rdev->recovery_entry); 255 | ret = __tcmu_reopen_dev(rdev->dev, -1); 256 | if (ret) { 257 | tcmu_dev_err(rdev->dev, "Could not reinitialize device. (err %d).\n", 258 | ret); 259 | if (!(rdev->flags & TCMUR_DEV_FLAG_STOPPING)) 260 | /* assume fatal error so do not enable tpg */ 261 | enable_tpg = false; 262 | } 263 | } 264 | 265 | if (enable_tpg) { 266 | ret = tcmu_set_tpg_int(tpg, "enable", 1); 267 | if (ret) { 268 | tcmu_err("Could not enable %s/%s/tpgt_%hu (err %d).\n", 269 | tpg->fabric, tpg->wwn, tpg->tpgt, ret); 270 | } else { 271 | tcmu_info("Enabled %s/%s/tpgt_%hu.\n", tpg->fabric, tpg->wwn, 272 | tpg->tpgt); 273 | } 274 | } 275 | 276 | free_tgt_port_grp(tpg); 277 | } 278 | 279 | int tcmu_add_dev_to_recovery_list(struct tcmu_device *dev) 280 | { 281 | struct tcmur_device *rdev = tcmu_dev_get_private(dev); 282 | struct list_head alua_list; 283 | struct alua_grp *group; 284 | struct tgt_port_grp *tpg; 285 | struct tgt_port *port, *enabled_port = NULL; 286 | int ret; 287 | 288 | pthread_mutex_lock(&tpg_recovery_lock); 289 | 290 | list_head_init(&alua_list); 291 | ret = tcmu_get_alua_grps(dev, &alua_list); 292 | if (ret) { 293 | /* User is deleting device so fast fail */ 294 | tcmu_dev_warn(dev, "Could not find any tpgs.\n"); 295 | goto done; 296 | } 297 | 298 | /* 299 | * This assumes a tcmu_dev is only exported though one local 300 | * enabled tpg. The kernel members file only returns 301 | * the one and runner is not passed info about which 302 | * tpg/port IO was received on. 303 | */ 304 | list_for_each(&alua_list, group, entry) { 305 | list_for_each(&group->tgt_ports, port, entry) { 306 | if (port->enabled) 307 | enabled_port = port; 308 | /* 309 | * If another device already kicked off recovery 310 | * the enabled bit might not be set. 311 | */ 312 | tpg = port_is_on_recovery_list(port); 313 | if (tpg) 314 | goto add_to_list; 315 | } 316 | } 317 | 318 | if (!enabled_port) { 319 | ret = -EIO; 320 | /* User disabled port from under us? */ 321 | tcmu_dev_err(dev, "Could not find enabled port.\n"); 322 | goto done; 323 | } 324 | 325 | tpg = setup_tgt_port_grp(enabled_port); 326 | if (!tpg) { 327 | ret = -ENOMEM; 328 | goto done; 329 | } 330 | 331 | ret = tcmur_run_work(rdev->event_work, tpg, tgt_port_grp_recovery_work_fn); 332 | if (ret) { 333 | tcmu_dev_err(dev, "Could not start recovery thread. Err %d\n", 334 | ret); 335 | free_tgt_port_grp(tpg); 336 | goto done; 337 | } 338 | list_add(&tpg_recovery_list, &tpg->recovery_entry); 339 | 340 | add_to_list: 341 | list_add(&tpg->devs, &rdev->recovery_entry); 342 | rdev->flags |= TCMUR_DEV_FLAG_IN_RECOVERY; 343 | done: 344 | tcmu_release_alua_grps(&alua_list); 345 | pthread_mutex_unlock(&tpg_recovery_lock); 346 | return ret; 347 | } 348 | -------------------------------------------------------------------------------- /target.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #ifndef __TCMU_TARGET_H 10 | #define __TCMU_TARGET_H 11 | 12 | #include "ccan/list/list.h" 13 | 14 | struct tgt_port_grp; 15 | 16 | struct tgt_port { 17 | uint16_t rel_port_id; 18 | uint8_t proto_id; 19 | char *wwn; 20 | 21 | /* LIO settings */ 22 | char *fabric; 23 | bool enabled; 24 | /* configfs tpgt */ 25 | uint16_t tpgt; 26 | 27 | struct alua_grp *grp; 28 | /* entry on group's tgt_ports list */ 29 | struct list_node entry; 30 | }; 31 | 32 | void tcmu_free_tgt_port(struct tgt_port *port); 33 | struct tgt_port *tcmu_get_tgt_port(char *member_str); 34 | int tcmu_add_dev_to_recovery_list(struct tcmu_device *dev); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /target_core_user_local.h: -------------------------------------------------------------------------------- 1 | #ifndef __TARGET_CORE_USER_H 2 | #define __TARGET_CORE_USER_H 3 | 4 | /* This header will be used by application too */ 5 | 6 | #include 7 | #include 8 | 9 | #define TCMU_VERSION "2.0" 10 | 11 | /* 12 | * Ring Design 13 | * ----------- 14 | * 15 | * The mmaped area is divided into three parts: 16 | * 1) The mailbox (struct tcmu_mailbox, below) 17 | * 2) The command ring 18 | * 3) Everything beyond the command ring (data) 19 | * 20 | * The mailbox tells userspace the offset of the command ring from the 21 | * start of the shared memory region, and how big the command ring is. 22 | * 23 | * The kernel passes SCSI commands to userspace by putting a struct 24 | * tcmu_cmd_entry in the ring, updating mailbox->cmd_head, and poking 25 | * userspace via uio's interrupt mechanism. 26 | * 27 | * tcmu_cmd_entry contains a header. If the header type is PAD, 28 | * userspace should skip hdr->length bytes (mod cmdr_size) to find the 29 | * next cmd_entry. 30 | * 31 | * Otherwise, the entry will contain offsets into the mmaped area that 32 | * contain the cdb and data buffers -- the latter accessible via the 33 | * iov array. iov addresses are also offsets into the shared area. 34 | * 35 | * When userspace is completed handling the command, set 36 | * entry->rsp.scsi_status, fill in rsp.sense_buffer if appropriate, 37 | * and also set mailbox->cmd_tail equal to the old cmd_tail plus 38 | * hdr->length, mod cmdr_size. If cmd_tail doesn't equal cmd_head, it 39 | * should process the next packet the same way, and so on. 40 | */ 41 | 42 | #define TCMU_MAILBOX_VERSION 2 43 | #define ALIGN_SIZE 64 /* Should be enough for most CPUs */ 44 | #define TCMU_MAILBOX_FLAG_CAP_OOOC (1 << 0) /* Out-of-order completions */ 45 | 46 | struct tcmu_mailbox { 47 | __u16 version; 48 | __u16 flags; 49 | __u32 cmdr_off; 50 | __u32 cmdr_size; 51 | 52 | __u32 cmd_head; 53 | 54 | /* Updated by user. On its own cacheline */ 55 | __u32 cmd_tail __attribute__((__aligned__(ALIGN_SIZE))); 56 | 57 | } __attribute__((packed)); 58 | 59 | enum tcmu_opcode { 60 | TCMU_OP_PAD = 0, 61 | TCMU_OP_CMD, 62 | }; 63 | 64 | /* 65 | * Only a few opcodes, and length is 8-byte aligned, so use low bits for opcode. 66 | */ 67 | struct tcmu_cmd_entry_hdr { 68 | __u32 len_op; 69 | __u16 cmd_id; 70 | __u8 kflags; 71 | #define TCMU_UFLAG_UNKNOWN_OP 0x1 72 | __u8 uflags; 73 | 74 | } __attribute__((packed)); 75 | 76 | #define TCMU_OP_MASK 0x7 77 | 78 | static __inline__ enum tcmu_opcode tcmu_hdr_get_op(__u32 len_op) 79 | { 80 | return len_op & TCMU_OP_MASK; 81 | } 82 | 83 | static __inline__ void tcmu_hdr_set_op(__u32 *len_op, enum tcmu_opcode op) 84 | { 85 | *len_op &= ~TCMU_OP_MASK; 86 | *len_op |= (op & TCMU_OP_MASK); 87 | } 88 | 89 | static __inline__ __u32 tcmu_hdr_get_len(__u32 len_op) 90 | { 91 | return len_op & ~TCMU_OP_MASK; 92 | } 93 | 94 | static __inline__ void tcmu_hdr_set_len(__u32 *len_op, __u32 len) 95 | { 96 | *len_op &= TCMU_OP_MASK; 97 | *len_op |= len; 98 | } 99 | 100 | /* Currently the same as SCSI_SENSE_BUFFERSIZE */ 101 | #define TCMU_SENSE_BUFFERSIZE 96 102 | 103 | struct tcmu_cmd_entry { 104 | struct tcmu_cmd_entry_hdr hdr; 105 | 106 | union { 107 | struct { 108 | uint32_t iov_cnt; 109 | uint32_t iov_bidi_cnt; 110 | uint32_t iov_dif_cnt; 111 | uint64_t cdb_off; 112 | uint64_t __pad1; 113 | uint64_t __pad2; 114 | struct iovec iov[0]; 115 | } req; 116 | struct { 117 | uint8_t scsi_status; 118 | uint8_t __pad1; 119 | uint16_t __pad2; 120 | uint32_t __pad3; 121 | char sense_buffer[TCMU_SENSE_BUFFERSIZE]; 122 | } rsp; 123 | }; 124 | 125 | } __attribute__((packed)); 126 | 127 | #define TCMU_OP_ALIGN_SIZE sizeof(uint64_t) 128 | 129 | enum tcmu_genl_cmd { 130 | TCMU_CMD_UNSPEC, 131 | TCMU_CMD_ADDED_DEVICE, 132 | TCMU_CMD_REMOVED_DEVICE, 133 | TCMU_CMD_RECONFIG_DEVICE, 134 | TCMU_CMD_ADDED_DEVICE_DONE, 135 | TCMU_CMD_REMOVED_DEVICE_DONE, 136 | TCMU_CMD_RECONFIG_DEVICE_DONE, 137 | TCMU_CMD_SET_FEATURES, 138 | __TCMU_CMD_MAX, 139 | }; 140 | #define TCMU_CMD_MAX (__TCMU_CMD_MAX - 1) 141 | 142 | enum tcmu_genl_attr { 143 | TCMU_ATTR_UNSPEC, 144 | TCMU_ATTR_DEVICE, 145 | TCMU_ATTR_MINOR, 146 | TCMU_ATTR_PAD, 147 | TCMU_ATTR_DEV_CFG, 148 | TCMU_ATTR_DEV_SIZE, 149 | TCMU_ATTR_WRITECACHE, 150 | TCMU_ATTR_CMD_STATUS, 151 | TCMU_ATTR_DEVICE_ID, 152 | TCMU_ATTR_SUPP_KERN_CMD_REPLY, 153 | __TCMU_ATTR_MAX, 154 | }; 155 | #define TCMU_ATTR_MAX (__TCMU_ATTR_MAX - 1) 156 | 157 | #endif 158 | -------------------------------------------------------------------------------- /tcmu-handler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tcmu-runner.8: -------------------------------------------------------------------------------- 1 | .TH tcmu-runner 8 2 | .SH NAME 3 | tcmu-runner \- A daemon that handles the userspace side of the LIO TCM-User backstore 4 | .SH DESCRIPTION 5 | .B tcmu-runner 6 | is a service that provides an operating environment for LIO TCM-User 7 | handlers. These currently include handlers for Gluster glfs and the 8 | qcow and qcow2 image formats. 9 | 10 | .SH USAGE 11 | While normally started automatically via D-Bus system activation, 12 | .B tcmu-runner 13 | can also be run as root directly, usually for debugging and 14 | development purposes. 15 | 16 | .SH OPTIONS 17 | .TP 18 | .B \-h, \-\-help 19 | Print usage information 20 | .TP 21 | .B \-d, \-\-debug 22 | Enable debug messages 23 | .TP 24 | .B \-V, \-\-version 25 | Print tcmu-runner's version 26 | .P 27 | .SH CONFIGURING HANDLERS 28 | TCMU-backed handlers are typically configured using normal LIO 29 | configuration tools, such as 30 | .BR targetcli . 31 | Part of doing this will be entering a handler-specific configuration 32 | string. 33 | .SS CONFIGURING GLUSTER HANDLER (glfs) 34 | This handler uses the Gluster glfs API to use Gluster as a backstore 35 | for the image file. 36 | .P 37 | Its configuration string is: 38 | .IP "" 4 39 | \fIvolume\fB@\fIhostname\fB/\fIfilename\fR 40 | .br 41 | volume: The volume on the Gluster server 42 | .br 43 | hostname: The server's hostname 44 | .br 45 | filename: The backing file 46 | .SS CONFIGURING QCOW HANDLER (qcow) 47 | This handler supports qcow, qcow2, or raw image file 48 | formats. 49 | .P 50 | Its configuration string is: 51 | .IP "" 4 52 | \fIpath\fR 53 | .br 54 | path: The full path to a file of a supported file format. The file 55 | must already have been created using 56 | .BR qemu-img . 57 | 58 | .SH SEE ALSO 59 | .BR qemu-img (1), 60 | .BR targetcli (8), 61 | .BR targetctl (8) 62 | 63 | .SH AUTHOR 64 | Andy Grover 65 | .SH REPORTING BUGS 66 | Report bugs via 67 | .br 68 | or 69 | -------------------------------------------------------------------------------- /tcmu-runner.conf: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tcmu-runner.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | /* 10 | * This header defines the interface between tcmu-runner and its loadable 11 | * subtype handlers. 12 | */ 13 | 14 | #ifndef __TCMU_RUNNER_H 15 | #define __TCMU_RUNNER_H 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "ccan/list/list.h" 27 | 28 | #include "scsi_defs.h" 29 | #include "libtcmu_log.h" 30 | #include "libtcmu_common.h" 31 | #include "alua.h" 32 | #include "scsi.h" 33 | 34 | struct tcmur_cmd; 35 | 36 | struct tcmur_cmd { 37 | /* Pointer to tcmulib_get_next_command's cmd. */ 38 | struct tcmulib_cmd *lib_cmd; 39 | 40 | /* Used by compound commands like CAW, format unit, etc. */ 41 | struct iovec *iovec; 42 | size_t iov_cnt; 43 | /* 44 | * Some handlers will manipulcate the iov_base pointer while copying 45 | * to/from it. This is a pointer to the original pointer. 46 | */ 47 | void *iov_base_copy; 48 | void *cmd_state; 49 | 50 | /* Bytes to read/write from iovec */ 51 | size_t requested; 52 | 53 | struct list_node cmds_list_entry; 54 | struct timespec start_time; 55 | bool timed_out; 56 | 57 | /* callback to finish/continue command processing */ 58 | void (*done)(struct tcmu_device *dev, struct tcmur_cmd *cmd, int ret); 59 | }; 60 | 61 | enum tcmur_event { 62 | TCMUR_EVT_LOCK_LOST, 63 | TCMUR_EVT_CONN_LOST, 64 | TCMUR_EVT_CMD_TIMED_OUT, 65 | }; 66 | 67 | struct tcmulib_cfg_info; 68 | 69 | struct tcmur_handler { 70 | const char *name; /* Human-friendly name */ 71 | const char *subtype; /* Name for cfgstring matching */ 72 | const char *cfg_desc; /* Description of this backstore's config string */ 73 | 74 | void *opaque; /* Handler private data. */ 75 | 76 | /* 77 | * As much as possible, check that the cfgstring will result 78 | * in a working device when given to us as dev->cfgstring in 79 | * the ->open() call. 80 | * 81 | * This function is optional but gives configuration tools a 82 | * chance to warn users in advance if the device they're 83 | * trying to create is invalid. 84 | * 85 | * Returns true if string is valid. Only if false, set *reason 86 | * to a string that says why. The string will be free()ed. 87 | * Suggest using asprintf(). 88 | */ 89 | bool (*check_config)(const char *cfgstring, char **reason); 90 | 91 | int (*reconfig)(struct tcmu_device *dev, struct tcmulib_cfg_info *cfg); 92 | 93 | /* Per-device added/removed callbacks */ 94 | int (*open)(struct tcmu_device *dev, bool reopen); 95 | void (*close)(struct tcmu_device *dev); 96 | 97 | /* 98 | * If > 0, runner will execute up to nr_threads IO callouts from 99 | * threads. 100 | * if 0, runner will call IO callouts from the cmd proc thread or 101 | * completion context for compound commands. 102 | */ 103 | int nr_threads; 104 | 105 | /* 106 | * handle_cmd only handlers return: 107 | * 108 | * - TCMU_STS_OK if the command has been executed successfully 109 | * - TCMU_STS_NOT_HANDLED if opcode is not handled 110 | * - TCMU_STS_ASYNC_HANDLED if opcode is handled asynchronously 111 | * - Non TCMU_STS_OK code indicating failure 112 | * - TCMU_STS_PASSTHROUGH_ERR For handlers that require low level 113 | * SCSI processing and want to setup their own sense buffers. 114 | * 115 | * Handlers that completely execute cmds from the handle_cmd's calling 116 | * context must return a TCMU_STS code from handle_cmd. 117 | * 118 | * Async handlers that queue a command from handle_cmd and complete 119 | * from their own async context return: 120 | * 121 | * - TCMU_STS_OK if the handler has queued the command. 122 | * - TCMU_STS_NOT_HANDLED if the command is not supported. 123 | * - TCMU_STS_NO_RESOURCE if the handler was not able to allocate 124 | * resources to queue the command. 125 | * 126 | * If TCMU_STS_OK is returned from the callout the handler must call 127 | * tcmur_cmd_complete with TCMU_STS return code to complete the command. 128 | */ 129 | int (*handle_cmd)(struct tcmu_device *dev, struct tcmur_cmd *cmd); 130 | 131 | /* 132 | * Below callouts are only executed by generic_handle_cmd. 133 | * 134 | * Handlers that completely execute cmds from the callout's calling 135 | * context must return a TCMU_STS code from the callout. 136 | * 137 | * Async handlers that queue a command from the callout and complete 138 | * it from their own async context return: 139 | * - TCMU_STS_OK if the handler has queued the command. 140 | * - TCMU_STS_NO_RESOURCE if the handler was not able to allocate 141 | * resources to queue the command. 142 | * 143 | * If TCMU_STS_OK is returned from the callout the handler must call 144 | * tcmur_cmd_complete with a TCMU_STS return code to complete the 145 | * command. 146 | */ 147 | int (*read)(struct tcmu_device *dev, struct tcmur_cmd *cmd, 148 | struct iovec *iovec, size_t iov_cnt, size_t len, off_t off); 149 | int (*write)(struct tcmu_device *dev, struct tcmur_cmd *cmd, 150 | struct iovec *iovec, size_t iov_cnt, size_t len, off_t off); 151 | int (*flush)(struct tcmu_device *dev, struct tcmur_cmd *cmd); 152 | int (*unmap)(struct tcmu_device *dev, struct tcmur_cmd *cmd, 153 | uint64_t off, uint64_t len); 154 | int (*writesame)(struct tcmu_device *dev, struct tcmur_cmd *cmd, uint64_t off, 155 | uint64_t len, struct iovec *iovec, size_t iov_cnt); 156 | int (*caw)(struct tcmu_device *dev, struct tcmur_cmd *cmd, uint64_t off, 157 | uint64_t len, struct iovec *iovec, size_t iov_cnt); 158 | 159 | /* 160 | * Notify the handler of an event. 161 | * 162 | * Return 0 on success and a -Exyz error code on error. 163 | */ 164 | int (*report_event)(struct tcmu_device *dev); 165 | 166 | /* 167 | * If the lock is acquired and the tag is not TCMU_INVALID_LOCK_TAG, 168 | * it must be associated with the lock and returned by get_lock_tag on 169 | * local and remote nodes. When unlock is successful, the tag 170 | * associated with the lock must be deleted. 171 | * 172 | * Returns a TCMU_STS indicating success/failure. 173 | */ 174 | int (*lock)(struct tcmu_device *dev, uint16_t tag); 175 | int (*unlock)(struct tcmu_device *dev); 176 | 177 | /* 178 | * Return tag set in lock call in tag buffer and a TCMU_STS 179 | * indicating success/failure. 180 | */ 181 | int (*get_lock_tag)(struct tcmu_device *dev, uint16_t *tag); 182 | 183 | /* 184 | * Must return TCMUR_DEV_LOCK state value. 185 | */ 186 | int (*get_lock_state)(struct tcmu_device *dev); 187 | 188 | /* 189 | * internal field, don't touch this 190 | * 191 | * indicates to tcmu-runner whether this is an internal handler loaded 192 | * via dlopen or an external handler registered via dbus. In the 193 | * latter case opaque will point to a struct dbus_info. 194 | */ 195 | bool _is_dbus_handler; 196 | 197 | /* 198 | * Update the logdir called by dynamic config thread. 199 | */ 200 | bool (*update_logdir)(void); 201 | 202 | /* To init/destroy some global resrouces if needed */ 203 | int (*init)(void); 204 | void (*destroy)(void); 205 | }; 206 | 207 | void tcmur_cmd_complete(struct tcmu_device *dev, void *data, int rc); 208 | 209 | /* 210 | * Each tcmu-runner (tcmur) handler plugin must export the 211 | * following. It usually just calls tcmur_register_handler. 212 | * 213 | * int handler_init(void); 214 | */ 215 | 216 | /* 217 | * APIs for tcmur only 218 | */ 219 | int tcmur_register_handler(struct tcmur_handler *handler); 220 | bool tcmur_unregister_handler(struct tcmur_handler *handler); 221 | 222 | #ifdef __cplusplus 223 | } 224 | #endif 225 | 226 | #endif 227 | -------------------------------------------------------------------------------- /tcmu-runner.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=LIO Userspace-passthrough daemon 3 | Documentation=man:tcmu-runner(8) 4 | Wants=rsyslog.service 5 | After=network.target rsyslog.service 6 | 7 | [Service] 8 | LimitNOFILE=1000000 9 | Type=dbus 10 | BusName=org.kernel.TCMUService1 11 | KillMode=process 12 | ExecStart=/usr/bin/tcmu-runner 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /tcmu-runner.spec: -------------------------------------------------------------------------------- 1 | %global _hardened_build 1 2 | 3 | # without rbd dependency 4 | # if you wish to exclude rbd handlers in RPM, use below command 5 | # rpmbuild -ta @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz --without rbd 6 | %bcond_without rbd 7 | 8 | # without glusterfs dependency 9 | # if you wish to exclude glfs handlers in RPM, use below command 10 | # rpmbuild -ta @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz --without glfs 11 | %bcond_without glfs 12 | 13 | # without qcow dependency 14 | # if you wish to exclude qcow handlers in RPM, use below command 15 | # rpmbuild -ta @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz --without qcow 16 | %bcond_without qcow 17 | 18 | # without zbc dependency 19 | # if you wish to exclude zbc handlers in RPM, use below command 20 | # rpmbuild -ta @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz --without zbc 21 | %bcond_without zbc 22 | 23 | # without file backed optical dependency 24 | # if you wish to exclude fbo handlers in RPM, use below command 25 | # rpmbuild -ta @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz --without fbo 26 | %bcond_without fbo 27 | 28 | # without tcmalloc dependency 29 | # if you wish to exclude tcmalloc, use below command 30 | # rpmbuild -ta @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz --without tcmalloc 31 | %bcond_without tcmalloc 32 | 33 | 34 | Name: tcmu-runner 35 | Summary: A daemon that handles the userspace side of the LIO TCM-User backstore 36 | Group: System Environment/Daemons 37 | License: ASL 2.0 or LGPLv2+ 38 | Version: 1.6.2 39 | URL: https://github.com/open-iscsi/tcmu-runner 40 | 41 | #%define _RC 42 | Release: %{?_RC:%{_RC}}%{?dist} 43 | BuildRoot: %(mktemp -udp %{_tmppath}/%{name}-%{version}%{?_RC:-%{_RC}}) 44 | Source: %{name}-%{version}%{?_RC:-%{_RC}}.tar.gz 45 | ExclusiveOS: Linux 46 | 47 | BuildRequires: cmake make gcc 48 | BuildRequires: libnl3-devel glib2-devel zlib-devel kmod-devel 49 | 50 | %if %{with rbd} 51 | BuildRequires: librbd1-devel librados2-devel 52 | Requires(pre): librados2, librbd1 53 | %endif 54 | 55 | %if %{with glfs} 56 | BuildRequires: glusterfs-api-devel 57 | Requires(pre): glusterfs-api 58 | %endif 59 | 60 | %if %{with tcmalloc} 61 | BuildRequires: gperftools-devel 62 | Requires: gperftools-libs 63 | %endif 64 | 65 | Requires(pre): kmod, zlib, libnl3, glib2, logrotate, rsyslog 66 | Requires: libtcmu = %{version}-%{release} 67 | 68 | %description 69 | A daemon that handles the userspace side of the LIO TCM-User backstore. 70 | 71 | LIO is the SCSI target in the Linux kernel. It is entirely kernel code, and 72 | allows exported SCSI logical units (LUNs) to be backed by regular files or 73 | block devices. But, if we want to get fancier with the capabilities of the 74 | device we're emulating, the kernel is not necessarily the right place. While 75 | there are userspace libraries for compression, encryption, and clustered 76 | storage solutions like Ceph or Gluster, these are not accessible from the 77 | kernel. 78 | 79 | The TCMU userspace-passthrough backstore allows a userspace process to handle 80 | requests to a LUN. But since the kernel-user interface that TCMU provides 81 | must be fast and flexible, it is complex enough that we'd like to avoid each 82 | userspace handler having to write boilerplate code. 83 | 84 | tcmu-runner handles the messy details of the TCMU interface -- UIO, netlink, 85 | pthreads, and DBus -- and exports a more friendly C plugin module API. Modules 86 | using this API are called "TCMU handlers". Handler authors can write code just 87 | to handle the SCSI commands as desired, and can also link with whatever 88 | userspace libraries they like. 89 | 90 | %package -n libtcmu 91 | Summary: A library supporting LIO TCM-User backstores processing 92 | Group: Development/Libraries 93 | 94 | %description -n libtcmu 95 | libtcmu provides a library for processing SCSI commands exposed by the 96 | LIO kernel target's TCM-User backend. 97 | 98 | %package -n libtcmu-devel 99 | Summary: Development headers for libtcmu 100 | Group: Development/Libraries 101 | Requires: %{name} = %{version}-%{release} 102 | Requires: libtcmu = %{version}-%{release} 103 | 104 | %description -n libtcmu-devel 105 | Development header(s) for developing against libtcmu. 106 | 107 | %global debug_package %{nil} 108 | 109 | %prep 110 | %setup -n %{name}-%{version}%{?_RC:-%{_RC}} 111 | 112 | %build 113 | %{__cmake} \ 114 | -DSUPPORT_SYSTEMD=ON -DCMAKE_INSTALL_PREFIX=%{_usr} \ 115 | %{?_without_rbd:-Dwith-rbd=false} \ 116 | %{?_without_zbc:-Dwith-zbc=false} \ 117 | %{?_without_qcow:-Dwith-qcow=false} \ 118 | %{?_without_glfs:-Dwith-glfs=false} \ 119 | %{?_without_fbo:-Dwith-fbo=false} \ 120 | %{?_without_tcmalloc:-Dwith-tcmalloc=false} \ 121 | . 122 | %{__make} 123 | 124 | %install 125 | %{__make} DESTDIR=%{buildroot} install 126 | %{__rm} -f %{buildroot}/etc/tcmu/tcmu.conf.old 127 | %{__rm} -f %{buildroot}/etc/logrotate.d/tcmu-runner.bak/tcmu-runner 128 | 129 | %files 130 | %{_bindir}/tcmu-runner 131 | %dir %{_sysconfdir}/dbus-1/ 132 | %dir %{_sysconfdir}/dbus-1/system.d 133 | %config %{_sysconfdir}/dbus-1/system.d/tcmu-runner.conf 134 | %dir %{_datadir}/dbus-1/ 135 | %dir %{_datadir}/dbus-1/system-services/ 136 | %{_datadir}/dbus-1/system-services/org.kernel.TCMUService1.service 137 | %{_unitdir}/tcmu-runner.service 138 | %dir %{_libdir}/tcmu-runner/ 139 | %{_libdir}/tcmu-runner/*.so 140 | %{_mandir}/man8/* 141 | %doc README.md LICENSE.LGPLv2.1 LICENSE.Apache2 142 | %dir %{_sysconfdir}/tcmu/ 143 | %config %{_sysconfdir}/tcmu/tcmu.conf 144 | %config(noreplace) %{_sysconfdir}/logrotate.d/tcmu-runner 145 | %ghost %attr(0644,-,-) %{_sysconfdir}/tcmu/tcmu.conf.old 146 | %ghost %attr(0644,-,-) %{_sysconfdir}/logrotate.d/tcmu-runner.bak/tcmu-runner 147 | 148 | %files -n libtcmu 149 | %{_libdir}/libtcmu*.so.* 150 | 151 | %files -n libtcmu-devel 152 | %{_libdir}/libtcmu*.so 153 | -------------------------------------------------------------------------------- /tcmu-synthesizer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #define _GNU_SOURCE 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "scsi_defs.h" 20 | 21 | #include "libtcmu.h" 22 | #include "version.h" 23 | #include "libtcmu_config.h" 24 | #include "libtcmu_log.h" 25 | #include "scsi.h" 26 | 27 | #define LOG_DIR "/var/log" 28 | 29 | typedef struct { 30 | GIOChannel *gio; 31 | int watcher_id; 32 | } syn_dev_t; 33 | 34 | static int syn_handle_cmd(struct tcmu_device *dev, uint8_t *cdb, 35 | struct iovec *iovec, size_t iov_cnt) 36 | { 37 | uint8_t cmd; 38 | 39 | cmd = cdb[0]; 40 | tcmu_dbg("syn handle cmd %d\n", cmd); 41 | 42 | switch (cmd) { 43 | case INQUIRY: 44 | return tcmu_emulate_inquiry(dev, NULL, cdb, iovec, iov_cnt); 45 | break; 46 | case TEST_UNIT_READY: 47 | return tcmu_emulate_test_unit_ready(cdb, iovec, iov_cnt); 48 | case SERVICE_ACTION_IN_16: 49 | if (cdb[1] == READ_CAPACITY_16) 50 | return tcmu_emulate_read_capacity_16(1 << 20, 51 | 512, 52 | cdb, iovec, 53 | iov_cnt); 54 | else 55 | return TCMU_STS_NOT_HANDLED; 56 | break; 57 | case MODE_SENSE: 58 | case MODE_SENSE_10: 59 | return tcmu_emulate_mode_sense(dev, cdb, iovec, iov_cnt); 60 | break; 61 | case MODE_SELECT: 62 | case MODE_SELECT_10: 63 | return tcmu_emulate_mode_select(dev, cdb, iovec, iov_cnt); 64 | break; 65 | case READ_6: 66 | case READ_10: 67 | case READ_12: 68 | case READ_16: 69 | return TCMU_STS_OK; 70 | 71 | case WRITE_6: 72 | case WRITE_10: 73 | case WRITE_12: 74 | case WRITE_16: 75 | return TCMU_STS_OK; 76 | 77 | default: 78 | tcmu_dbg("unknown command %x\n", cdb[0]); 79 | return TCMU_STS_NOT_HANDLED; 80 | } 81 | } 82 | 83 | static gboolean syn_dev_callback(GIOChannel *source, 84 | GIOCondition condition, 85 | gpointer data) 86 | { 87 | int ret; 88 | struct tcmu_device *dev = data; 89 | struct tcmulib_cmd *cmd; 90 | 91 | tcmu_dbg("dev fd cb\n"); 92 | tcmulib_processing_start(dev); 93 | 94 | while ((cmd = tcmulib_get_next_command(dev, 0)) != NULL) { 95 | ret = syn_handle_cmd(dev, 96 | cmd->cdb, 97 | cmd->iovec, 98 | cmd->iov_cnt); 99 | tcmulib_command_complete(dev, cmd, ret); 100 | } 101 | 102 | tcmulib_processing_complete(dev); 103 | return TRUE; 104 | } 105 | 106 | static int syn_added(struct tcmu_device *dev) 107 | { 108 | syn_dev_t *s = g_new0(syn_dev_t, 1); 109 | 110 | tcmu_dbg("added %s\n", tcmu_dev_get_cfgstring(dev)); 111 | tcmu_dev_set_private(dev, s); 112 | s->gio = g_io_channel_unix_new(tcmu_dev_get_fd(dev)); 113 | s->watcher_id = g_io_add_watch(s->gio, G_IO_IN, 114 | syn_dev_callback, dev); 115 | return 0; 116 | } 117 | 118 | static void syn_removed(struct tcmu_device *dev) 119 | { 120 | syn_dev_t *s = tcmu_dev_get_private(dev); 121 | tcmu_dbg("removed %s\n", tcmu_dev_get_cfgstring(dev)); 122 | g_source_remove(s->watcher_id); 123 | g_io_channel_unref(s->gio); 124 | g_free(s); 125 | } 126 | 127 | struct tcmulib_handler syn_handler = { 128 | .name = "syn", 129 | .subtype = "syn", 130 | .cfg_desc = "valid options:\n" 131 | "null: a nop storage where R/W requests are completed " 132 | "immediately, like the null_blk device.", 133 | .added = syn_added, 134 | .removed = syn_removed, 135 | }; 136 | 137 | gboolean tcmulib_callback(GIOChannel *source, 138 | GIOCondition condition, 139 | gpointer data) 140 | { 141 | struct tcmulib_context *ctx = data; 142 | 143 | tcmu_dbg("master fd ready\n"); 144 | tcmulib_master_fd_ready(ctx); 145 | 146 | return TRUE; 147 | } 148 | 149 | static void usage(void) { 150 | printf("\nusage:\n"); 151 | printf("\ttcmu-synthesizer [options]\n"); 152 | printf("\noptions:\n"); 153 | printf("\t-h, --help: print this message and exit\n"); 154 | printf("\t-V, --version: print version and exit\n"); 155 | printf("\t-d, --debug: enable debug messages\n"); 156 | printf("\n"); 157 | } 158 | 159 | static struct option long_options[] = { 160 | {"debug", no_argument, 0, 'd'}, 161 | {"help", no_argument, 0, 'h'}, 162 | {"version", no_argument, 0, 'V'}, 163 | {0, 0, 0, 0}, 164 | }; 165 | 166 | int main(int argc, char **argv) 167 | { 168 | GMainLoop *loop; 169 | GIOChannel *libtcmu_gio; 170 | struct tcmulib_context *ctx; 171 | 172 | while (1) { 173 | int c; 174 | int option_index = 0; 175 | 176 | c = getopt_long(argc, argv, "dhV", 177 | long_options, &option_index); 178 | if (c == -1) 179 | break; 180 | 181 | switch (c) { 182 | case 'd': 183 | tcmu_set_log_level(TCMU_CONF_LOG_DEBUG); 184 | break; 185 | case 'V': 186 | printf("tcmu-synthesizer %s\n", TCMUR_VERSION); 187 | exit(1); 188 | default: 189 | case 'h': 190 | usage(); 191 | exit(1); 192 | } 193 | } 194 | 195 | if (tcmu_setup_log(LOG_DIR)) { 196 | fprintf(stderr, "Could not setup tcmu logger.\n"); 197 | exit(1); 198 | } 199 | 200 | ctx = tcmulib_initialize(&syn_handler, 1); 201 | if (!ctx) { 202 | tcmu_err("tcmulib_initialize failed\n"); 203 | tcmu_destroy_log(); 204 | exit(1); 205 | } 206 | 207 | tcmulib_register(ctx); 208 | /* Set up event for libtcmu */ 209 | libtcmu_gio = g_io_channel_unix_new(tcmulib_get_master_fd(ctx)); 210 | g_io_add_watch(libtcmu_gio, G_IO_IN, tcmulib_callback, ctx); 211 | loop = g_main_loop_new(NULL, FALSE); 212 | g_main_loop_run(loop); 213 | g_main_loop_unref(loop); 214 | tcmu_destroy_log(); 215 | return 0; 216 | } 217 | -------------------------------------------------------------------------------- /tcmu.conf: -------------------------------------------------------------------------------- 1 | # Master TCMU configuration file 2 | 3 | # Logging Controls 4 | # There are 6 logging levels supported: 5 | # 0: CRIT 6 | # 1: ERROR 7 | # 2: WARNING 8 | # 3: INFO 9 | # 4: DEBUG 10 | # 5: DEBUG SCSI CMD 11 | # And the default logging level is 3, if you want to change the 12 | # default level, uncomment it and set your level number: 13 | # log_level = 3 14 | # 15 | # Logging Directory Path 16 | # The default logging Directory path is /var/log, uncomment it 17 | # and set your own path: 18 | # log_dir = "/var/log" 19 | -------------------------------------------------------------------------------- /tcmu.conf_install.cmake.in: -------------------------------------------------------------------------------- 1 | if (EXISTS "/etc/tcmu/tcmu.conf") 2 | file(INSTALL "/etc/tcmu/tcmu.conf" DESTINATION "/etc/tcmu/" RENAME "tcmu.conf.old") 3 | endif() 4 | file(INSTALL "${PROJECT_SOURCE_DIR}/tcmu.conf" DESTINATION "/etc/tcmu/") 5 | -------------------------------------------------------------------------------- /tcmu_runner_priv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | /* 10 | * This header defines structures private to tcmu-runner, and should not 11 | * be used by anyone else. 12 | */ 13 | 14 | #ifndef __TCMU_RUNNER_PRIV_H 15 | #define __TCMU_RUNNER_PRIV_H 16 | 17 | struct tcmu_device; 18 | struct tcmur_handler; 19 | 20 | struct tcmur_handler *tcmu_get_runner_handler(struct tcmu_device *dev); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /tcmur_aio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #define _GNU_SOURCE 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "ccan/list/list.h" 16 | 17 | #include "libtcmu.h" 18 | #include "libtcmu_priv.h" 19 | #include "tcmur_device.h" 20 | #include "tcmur_aio.h" 21 | #include "tcmu_runner_priv.h" 22 | #include "tcmu-runner.h" 23 | 24 | struct tcmu_work { 25 | struct tcmu_device *dev; 26 | void *data; 27 | tcmu_work_fn_t work_fn; 28 | tcmu_done_fn_t done_fn; 29 | struct list_node entry; 30 | }; 31 | 32 | static void _cleanup_mutex_lock(void *arg) 33 | { 34 | pthread_mutex_unlock(arg); 35 | } 36 | 37 | void track_aio_request_start(struct tcmur_device *rdev) 38 | { 39 | struct tcmu_track_aio *aio_track = &rdev->track_queue; 40 | 41 | pthread_cleanup_push(_cleanup_mutex_lock, (void *)&aio_track->track_lock); 42 | pthread_mutex_lock(&aio_track->track_lock); 43 | 44 | ++aio_track->tracked_aio_ops; 45 | 46 | pthread_mutex_unlock(&aio_track->track_lock); 47 | pthread_cleanup_pop(0); 48 | } 49 | 50 | void track_aio_request_finish(struct tcmur_device *rdev, int *wake_up) 51 | { 52 | struct tcmu_track_aio *aio_track = &rdev->track_queue; 53 | pthread_cond_t *cond; 54 | 55 | pthread_cleanup_push(_cleanup_mutex_lock, (void *)&aio_track->track_lock); 56 | pthread_mutex_lock(&aio_track->track_lock); 57 | 58 | assert(aio_track->tracked_aio_ops > 0); 59 | --aio_track->tracked_aio_ops; 60 | 61 | if (wake_up) { 62 | ++aio_track->pending_wakeups; 63 | *wake_up = (aio_track->pending_wakeups == 1) ? 1 : 0; 64 | } 65 | 66 | if (!aio_track->tracked_aio_ops && aio_track->is_empty_cond) { 67 | cond = aio_track->is_empty_cond; 68 | aio_track->is_empty_cond = NULL; 69 | pthread_cond_signal(cond); 70 | } 71 | 72 | pthread_mutex_unlock(&aio_track->track_lock); 73 | pthread_cleanup_pop(0); 74 | } 75 | 76 | void track_aio_wakeup_finish(struct tcmur_device *rdev, int *wake_up) 77 | { 78 | struct tcmu_track_aio *aio_track = &rdev->track_queue; 79 | 80 | pthread_cleanup_push(_cleanup_mutex_lock, (void *)&aio_track->track_lock); 81 | pthread_mutex_lock(&aio_track->track_lock); 82 | 83 | if (aio_track->pending_wakeups > 1) { 84 | aio_track->pending_wakeups = 1; 85 | *wake_up = 1; 86 | } else { 87 | assert(aio_track->pending_wakeups > 0); 88 | aio_track->pending_wakeups = 0; 89 | *wake_up = 0; 90 | } 91 | 92 | pthread_mutex_unlock(&aio_track->track_lock); 93 | pthread_cleanup_pop(0); 94 | } 95 | 96 | static void cleanup_empty_queue_wait(void *arg) 97 | { 98 | struct tcmu_track_aio *aio_track = arg; 99 | 100 | pthread_cond_destroy(aio_track->is_empty_cond); 101 | aio_track->is_empty_cond = NULL; 102 | pthread_mutex_unlock(&aio_track->track_lock); 103 | } 104 | 105 | int aio_wait_for_empty_queue(struct tcmur_device *rdev) 106 | { 107 | struct tcmu_track_aio *aio_track = &rdev->track_queue; 108 | pthread_cond_t cond; 109 | int ret; 110 | 111 | ret = pthread_cond_init(&cond, NULL); 112 | if (ret) 113 | return ret; 114 | 115 | pthread_cleanup_push(cleanup_empty_queue_wait, aio_track); 116 | pthread_mutex_lock(&aio_track->track_lock); 117 | 118 | if (!aio_track->tracked_aio_ops) 119 | goto unlock; 120 | 121 | tcmu_dev_dbg(rdev->dev, "waiting for %d commands\n", 122 | rdev->track_queue.tracked_aio_ops); 123 | 124 | aio_track->is_empty_cond = &cond; 125 | ret = pthread_cond_wait(&cond, &aio_track->track_lock); 126 | aio_track->is_empty_cond = NULL; 127 | 128 | unlock: 129 | pthread_mutex_unlock(&aio_track->track_lock); 130 | pthread_cleanup_pop(0); 131 | 132 | pthread_cond_destroy(&cond); 133 | return ret; 134 | } 135 | 136 | static void _cleanup_io_work(void *arg) 137 | { 138 | free(arg); 139 | } 140 | 141 | static void *io_work_queue(void *arg) 142 | { 143 | struct tcmu_device *dev = arg; 144 | struct tcmur_device *rdev = tcmu_dev_get_private(dev); 145 | struct tcmu_io_queue *io_wq = &rdev->work_queue; 146 | int ret; 147 | 148 | tcmu_set_thread_name("aio", dev); 149 | 150 | while (1) { 151 | struct tcmu_work *work; 152 | void *data; 153 | 154 | pthread_cleanup_push(_cleanup_mutex_lock, &io_wq->io_lock); 155 | pthread_mutex_lock(&io_wq->io_lock); 156 | 157 | while (list_empty(&io_wq->io_queue)) { 158 | pthread_cond_wait(&io_wq->io_cond, 159 | &io_wq->io_lock); 160 | } 161 | 162 | work = list_first_entry(&io_wq->io_queue, struct tcmu_work, 163 | entry); 164 | list_del(&work->entry); 165 | 166 | pthread_mutex_unlock(&io_wq->io_lock); 167 | pthread_cleanup_pop(0); 168 | 169 | /* kick start I/O request */ 170 | data = work->data; 171 | pthread_cleanup_push(_cleanup_io_work, work); 172 | 173 | ret = work->work_fn(work->dev, data); 174 | work->done_fn(dev, data, ret); 175 | 176 | pthread_cleanup_pop(1); /* cleanup work */ 177 | } 178 | 179 | return NULL; 180 | } 181 | 182 | static int aio_queue(struct tcmu_device *dev, void *data, tcmu_work_fn_t work_fn, 183 | tcmu_done_fn_t done_fn) 184 | { 185 | struct tcmu_work *work; 186 | struct tcmur_device *rdev = tcmu_dev_get_private(dev); 187 | struct tcmu_io_queue *io_wq = &rdev->work_queue; 188 | 189 | work = malloc(sizeof(*work)); 190 | if (!work) 191 | return TCMU_STS_NO_RESOURCE; 192 | 193 | work->work_fn = work_fn; 194 | work->done_fn = done_fn; 195 | work->dev = dev; 196 | work->data = data; 197 | list_node_init(&work->entry); 198 | 199 | /* cleanup push/pop not _really_ required here atm */ 200 | pthread_cleanup_push(_cleanup_mutex_lock, &io_wq->io_lock); 201 | pthread_mutex_lock(&io_wq->io_lock); 202 | 203 | list_add_tail(&io_wq->io_queue, &work->entry); 204 | pthread_cond_signal(&io_wq->io_cond); // TODO: conditional 205 | 206 | pthread_mutex_unlock(&io_wq->io_lock); 207 | pthread_cleanup_pop(0); 208 | 209 | return TCMU_STS_ASYNC_HANDLED; 210 | } 211 | 212 | int aio_request_schedule(struct tcmu_device *dev, void *data, 213 | tcmu_work_fn_t work_fn, tcmu_done_fn_t done_fn) 214 | { 215 | struct tcmur_handler *rhandler = tcmu_get_runner_handler(dev); 216 | int ret; 217 | 218 | if (!rhandler->nr_threads) { 219 | ret = work_fn(dev, data); 220 | if (!ret) 221 | ret = TCMU_STS_ASYNC_HANDLED; 222 | } else { 223 | ret = aio_queue(dev, data, work_fn, done_fn); 224 | } 225 | 226 | return ret; 227 | } 228 | 229 | int setup_aio_tracking(struct tcmur_device *rdev) 230 | { 231 | int ret; 232 | struct tcmu_track_aio *aio_track = &rdev->track_queue; 233 | 234 | aio_track->pending_wakeups = 0; 235 | aio_track->tracked_aio_ops = 0; 236 | ret = pthread_mutex_init(&aio_track->track_lock, NULL); 237 | if (ret != 0) { 238 | return -ret; 239 | } 240 | 241 | return 0; 242 | } 243 | 244 | void cleanup_aio_tracking(struct tcmur_device *rdev) 245 | { 246 | int ret; 247 | struct tcmu_track_aio *aio_track = &rdev->track_queue; 248 | 249 | assert(aio_track->tracked_aio_ops == 0); 250 | 251 | ret = pthread_mutex_destroy(&aio_track->track_lock); 252 | if (ret != 0) { 253 | tcmu_err("failed to destroy track lock\n"); 254 | } 255 | } 256 | 257 | void cleanup_io_work_queue_threads(struct tcmu_device *dev) 258 | { 259 | struct tcmur_handler *r_handler = tcmu_get_runner_handler(dev); 260 | struct tcmur_device *rdev = tcmu_dev_get_private(dev); 261 | struct tcmu_io_queue *io_wq = &rdev->work_queue; 262 | int i, nr_threads = r_handler->nr_threads; 263 | 264 | if (!io_wq->io_wq_threads) { 265 | return; 266 | } 267 | 268 | for (i = 0; i < nr_threads; i++) { 269 | if (io_wq->io_wq_threads[i]) { 270 | tcmu_thread_cancel(io_wq->io_wq_threads[i]); 271 | } 272 | } 273 | } 274 | 275 | int setup_io_work_queue(struct tcmu_device *dev) 276 | { 277 | struct tcmur_handler *r_handler = tcmu_get_runner_handler(dev); 278 | struct tcmur_device *rdev = tcmu_dev_get_private(dev); 279 | struct tcmu_io_queue *io_wq = &rdev->work_queue; 280 | int ret, i, nr_threads = r_handler->nr_threads; 281 | 282 | if (!nr_threads) 283 | return 0; 284 | 285 | list_head_init(&io_wq->io_queue); 286 | 287 | ret = pthread_mutex_init(&io_wq->io_lock, NULL); 288 | if (ret != 0) { 289 | goto out; 290 | } 291 | ret = pthread_cond_init(&io_wq->io_cond, NULL); 292 | if (ret != 0) { 293 | goto cleanup_lock; 294 | } 295 | 296 | /* TODO: Allow user to override device defaults */ 297 | io_wq->io_wq_threads = calloc(nr_threads, sizeof(pthread_t)); 298 | if (!io_wq->io_wq_threads) { 299 | ret = ENOMEM; 300 | goto cleanup_cond; 301 | } 302 | 303 | for (i = 0; i < nr_threads; i++) { 304 | ret = pthread_create(&io_wq->io_wq_threads[i], NULL, 305 | io_work_queue, dev); 306 | if (ret != 0) { 307 | goto cleanup_threads; 308 | } 309 | } 310 | 311 | return 0; 312 | 313 | cleanup_threads: 314 | cleanup_io_work_queue_threads(dev); 315 | free(io_wq->io_wq_threads); 316 | cleanup_cond: 317 | pthread_cond_destroy(&io_wq->io_cond); 318 | cleanup_lock: 319 | pthread_mutex_destroy(&io_wq->io_lock); 320 | out: 321 | return -ret; 322 | } 323 | 324 | void cleanup_io_work_queue(struct tcmu_device *dev, bool cancel) 325 | { 326 | struct tcmur_device *rdev = tcmu_dev_get_private(dev); 327 | struct tcmu_io_queue *io_wq = &rdev->work_queue; 328 | int ret; 329 | 330 | if (!io_wq->io_wq_threads) { 331 | return; 332 | } 333 | 334 | if (cancel) { 335 | cleanup_io_work_queue_threads(dev); 336 | } 337 | 338 | /* 339 | * Note that there's no need to drain ->io_queue at this point 340 | * as it _should_ be empty (target layer would call this path 341 | * when no commands are running - thanks Mike). 342 | * 343 | * Out of tree handlers which do not use the aio code are not 344 | * supported in this path. 345 | */ 346 | 347 | ret = pthread_mutex_destroy(&io_wq->io_lock); 348 | if (ret != 0) { 349 | tcmu_err("failed to destroy io workqueue lock\n"); 350 | } 351 | 352 | ret = pthread_cond_destroy(&io_wq->io_cond); 353 | if (ret != 0) { 354 | tcmu_err("failed to destroy io workqueue cond\n"); 355 | } 356 | 357 | free(io_wq->io_wq_threads); 358 | } 359 | -------------------------------------------------------------------------------- /tcmur_aio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #ifndef __TCMUR_AIO_H 10 | #define __TCMUR_AIO_H 11 | 12 | #include 13 | 14 | #include "ccan/list/list.h" 15 | 16 | struct tcmur_device; 17 | struct tcmu_device; 18 | struct tcmulib_cmd; 19 | 20 | struct tcmu_track_aio { 21 | unsigned int pending_wakeups; 22 | unsigned int tracked_aio_ops; 23 | pthread_mutex_t track_lock; 24 | pthread_cond_t *is_empty_cond; 25 | }; 26 | 27 | struct tcmu_io_queue { 28 | pthread_mutex_t io_lock; 29 | pthread_cond_t io_cond; 30 | 31 | pthread_t *io_wq_threads; 32 | struct list_head io_queue; 33 | }; 34 | 35 | int setup_io_work_queue(struct tcmu_device *); 36 | void cleanup_io_work_queue(struct tcmu_device *, bool); 37 | void cleanup_io_work_queue_threads(struct tcmu_device *); 38 | 39 | int setup_aio_tracking(struct tcmur_device *); 40 | void cleanup_aio_tracking(struct tcmur_device *); 41 | 42 | typedef int (*tcmu_work_fn_t)(struct tcmu_device *dev, void *data); 43 | typedef void (*tcmu_done_fn_t)(struct tcmu_device *dev, void *data, int rc); 44 | 45 | int aio_request_schedule(struct tcmu_device *, void *, tcmu_work_fn_t, 46 | tcmu_done_fn_t); 47 | 48 | /* aio request tracking */ 49 | void track_aio_request_start(struct tcmur_device *); 50 | void track_aio_request_finish(struct tcmur_device *, int *); 51 | void track_aio_wakeup_finish(struct tcmur_device *, int *); 52 | int aio_wait_for_empty_queue(struct tcmur_device *rdev); 53 | 54 | #endif /* __TCMUR_AIO_H */ 55 | -------------------------------------------------------------------------------- /tcmur_cmd_handler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #ifndef __TCMUR_CMD_HANDLER_H 10 | #define __TCMUR_CMD_HANDLER_H 11 | 12 | #include 13 | 14 | struct tcmu_device; 15 | struct tcmulib_cmd; 16 | struct tcmur_cmd; 17 | struct timespec; 18 | 19 | int tcmur_get_time(struct tcmu_device *dev, struct timespec *time); 20 | int tcmur_dev_update_size(struct tcmu_device *dev, uint64_t new_size); 21 | void tcmur_set_pending_ua(struct tcmu_device *dev, int ua); 22 | int tcmur_generic_handle_cmd(struct tcmu_device *dev, struct tcmulib_cmd *cmd); 23 | int tcmur_cmd_passthrough_handler(struct tcmu_device *dev, struct tcmulib_cmd *cmd); 24 | bool tcmur_handler_is_passthrough_only(struct tcmur_handler *rhandler); 25 | void tcmur_tcmulib_cmd_complete(struct tcmu_device *dev, 26 | struct tcmulib_cmd *cmd, int ret); 27 | 28 | typedef int (*tcmur_writesame_fn_t)(struct tcmu_device *dev, 29 | struct tcmur_cmd *tcmur_cmd, uint64_t off, 30 | uint64_t len, struct iovec *iov, 31 | size_t iov_cnt); 32 | 33 | typedef int (*tcmur_caw_fn_t)(struct tcmu_device *dev, 34 | struct tcmur_cmd *tcmur_cmd, uint64_t off, 35 | uint64_t len, struct iovec *iov, size_t iov_cnt); 36 | 37 | #endif /* __TCMUR_CMD_HANDLER_H */ 38 | -------------------------------------------------------------------------------- /tcmur_device.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #ifndef __TCMUR_DEVICE_H 10 | #define __TCMUR_DEVICE_H 11 | 12 | #include "pthread.h" 13 | 14 | #include "ccan/list/list.h" 15 | 16 | #include "tcmur_aio.h" 17 | 18 | #define TCMU_INVALID_LOCK_TAG USHRT_MAX 19 | 20 | #define TCMUR_DEV_FLAG_FORMATTING (1 << 0) 21 | #define TCMUR_DEV_FLAG_IN_RECOVERY (1 << 1) 22 | #define TCMUR_DEV_FLAG_IS_OPEN (1 << 2) 23 | #define TCMUR_DEV_FLAG_STOPPING (1 << 3) 24 | #define TCMUR_DEV_FLAG_STOPPED (1 << 4) 25 | 26 | #define TCMUR_UA_DEV_SIZE_CHANGED 0 27 | 28 | enum { 29 | TCMUR_DEV_FAILOVER_ALL_ACTIVE, 30 | TCMUR_DEV_FAILOVER_IMPLICIT, 31 | TCMUR_DEV_FAILOVER_EXPLICIT, 32 | }; 33 | 34 | enum { 35 | TCMUR_DEV_LOCK_UNKNOWN, 36 | TCMUR_DEV_LOCK_UNLOCKED, 37 | TCMUR_DEV_LOCK_READ_LOCKING, 38 | TCMUR_DEV_LOCK_READ_LOCKED, 39 | TCMUR_DEV_LOCK_WRITE_LOCKING, 40 | TCMUR_DEV_LOCK_WRITE_LOCKED, 41 | }; 42 | 43 | struct tcmur_work; 44 | 45 | struct tcmur_device { 46 | struct tcmu_device *dev; 47 | void *hm_private; 48 | 49 | pthread_t cmdproc_thread; 50 | 51 | /* General lock for the members from "flags" to "pending_uas" */ 52 | pthread_mutex_t rdev_lock; 53 | 54 | /* TCMUR_DEV flags */ 55 | uint32_t flags; 56 | uint8_t failover_type; 57 | 58 | struct list_node recovery_entry; 59 | 60 | /* tcmur_event counters */ 61 | uint64_t lock_lost_cnt; 62 | uint64_t conn_lost_cnt; 63 | uint64_t cmd_timed_out_cnt; 64 | struct tcmur_work *event_work; 65 | 66 | bool lock_lost; 67 | uint8_t lock_state; 68 | 69 | int pending_uas; 70 | 71 | /* 72 | * lock order: 73 | * work_queue->aio_lock 74 | * track_queue->track_lock 75 | */ 76 | struct tcmu_io_queue work_queue; 77 | struct tcmu_track_aio track_queue; 78 | 79 | pthread_mutex_t caw_lock; /* for atomic CAW operation */ 80 | 81 | uint32_t format_progress; 82 | pthread_mutex_t format_lock; /* for atomic format operations */ 83 | 84 | int cmd_time_out; 85 | 86 | pthread_spinlock_t cmds_list_lock; /* protects cmds_list */ 87 | struct list_head cmds_list; 88 | }; 89 | 90 | bool tcmu_dev_in_recovery(struct tcmu_device *dev); 91 | void tcmu_cancel_recovery(struct tcmu_device *dev); 92 | 93 | void tcmu_notify_conn_lost(struct tcmu_device *dev); 94 | void tcmu_notify_lock_lost(struct tcmu_device *dev); 95 | void tcmu_notify_cmd_timed_out(struct tcmu_device *dev); 96 | 97 | int __tcmu_reopen_dev(struct tcmu_device *dev, int retries); 98 | int tcmu_reopen_dev(struct tcmu_device *dev, int retries); 99 | 100 | int tcmu_acquire_dev_lock(struct tcmu_device *dev, uint16_t tag); 101 | void tcmu_release_dev_lock(struct tcmu_device *dev); 102 | int tcmu_get_lock_tag(struct tcmu_device *dev, uint16_t *tag); 103 | void tcmu_update_dev_lock_state(struct tcmu_device *dev); 104 | 105 | void tcmur_dev_set_private(struct tcmu_device *dev, void *private); 106 | void *tcmur_dev_get_private(struct tcmu_device *dev); 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /tcmur_work.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | #define _GNU_SOURCE 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "libtcmu.h" 15 | #include "libtcmu_log.h" 16 | #include "tcmur_device.h" 17 | #include "tcmur_work.h" 18 | 19 | struct tcmur_work *tcmur_create_work(void) 20 | { 21 | struct tcmur_work *work; 22 | 23 | work = calloc(1, sizeof(*work)); 24 | if (!work) 25 | return NULL; 26 | 27 | if (pthread_mutex_init(&work->lock, NULL)) 28 | goto free_work; 29 | 30 | if (pthread_cond_init(&work->cond, NULL)) 31 | goto destroy_mutex; 32 | 33 | return work; 34 | 35 | destroy_mutex: 36 | pthread_mutex_destroy(&work->lock); 37 | free_work: 38 | free(work); 39 | return NULL; 40 | } 41 | 42 | static void __tcmur_flush_work(struct tcmur_work *work) 43 | { 44 | /* 45 | * The event work thread may need to do a handler reopen 46 | * call and try to flush itself. Just ignore. 47 | */ 48 | if (__tcmu_is_ework_thread) 49 | return; 50 | 51 | /* 52 | * Some handlers will crash if we do a cancel so we just wait. 53 | */ 54 | tcmu_dbg("waiting for %d work thread to complete\n", work->refcnt); 55 | if (work->refcnt) 56 | pthread_cond_wait(&work->cond, &work->lock); 57 | } 58 | 59 | void tcmur_flush_work(struct tcmur_work *work) 60 | { 61 | pthread_mutex_lock(&work->lock); 62 | __tcmur_flush_work(work); 63 | pthread_mutex_unlock(&work->lock); 64 | } 65 | 66 | struct private { 67 | void *data; 68 | void (*work_fn)(void *); 69 | struct tcmur_work *work; 70 | }; 71 | 72 | static void *tcmur_work_fn(void *data) 73 | { 74 | struct private *p = data; 75 | 76 | tcmu_set_thread_name("ework-thread", NULL); 77 | __tcmu_is_ework_thread = 1; 78 | 79 | p->work_fn(p->data); 80 | 81 | pthread_mutex_lock(&p->work->lock); 82 | if (--p->work->refcnt == 0) 83 | pthread_cond_signal(&p->work->cond); 84 | pthread_mutex_unlock(&p->work->lock); 85 | 86 | free(p); 87 | return NULL; 88 | } 89 | 90 | int tcmur_run_work(struct tcmur_work *work, void *data, void (*work_fn)(void *)) 91 | { 92 | pthread_attr_t attr; 93 | pthread_t thread; 94 | struct private *p; 95 | int ret; 96 | 97 | p = malloc(sizeof(struct private)); 98 | if (!p) 99 | return -ENOMEM; 100 | 101 | p->data = data; 102 | p->work_fn = work_fn; 103 | p->work = work; 104 | 105 | pthread_attr_init(&attr); 106 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 107 | 108 | pthread_mutex_lock(&work->lock); 109 | ret = pthread_create(&thread, &attr, tcmur_work_fn, p); 110 | if (!ret) 111 | work->refcnt++; 112 | pthread_mutex_unlock(&work->lock); 113 | 114 | pthread_attr_destroy(&attr); 115 | 116 | if (ret) 117 | free(p); 118 | return ret; 119 | } 120 | 121 | void tcmur_destroy_work(struct tcmur_work *work) 122 | { 123 | tcmur_flush_work(work); 124 | pthread_mutex_destroy(&work->lock); 125 | pthread_cond_destroy(&work->cond); 126 | free(work); 127 | } 128 | -------------------------------------------------------------------------------- /tcmur_work.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Red Hat, Inc. 3 | * 4 | * This file is licensed to you under your choice of the GNU Lesser 5 | * General Public License, version 2.1 or any later version (LGPLv2.1 or 6 | * later), or the Apache License 2.0. 7 | */ 8 | 9 | #ifndef __TCMU_WORK_H 10 | #define __TCMU_WORK_H 11 | 12 | #include 13 | 14 | #include "ccan/list/list.h" 15 | 16 | struct tcmu_device; 17 | 18 | struct tcmur_work { 19 | pthread_mutex_t lock; 20 | pthread_cond_t cond; 21 | int refcnt; 22 | }; 23 | 24 | struct tcmur_work *tcmur_create_work(void); 25 | void tcmur_destroy_work(struct tcmur_work *work); 26 | int tcmur_run_work(struct tcmur_work *work, void *data, 27 | void (*work_fn)(void *)); 28 | void tcmur_flush_work(struct tcmur_work *work); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /version.h.in: -------------------------------------------------------------------------------- 1 | 2 | #define TCMUR_VERSION "@VERSION@" 3 | 4 | #define DEFAULT_HANDLER_PATH "@tcmu-runner_HANDLER_PATH@" 5 | 6 | #define GFAPI_VERSION760 @GFAPI_VERSION760@ 7 | 8 | #define GFAPI_VERSION766 @GFAPI_VERSION766@ 9 | --------------------------------------------------------------------------------