Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: ublk: kublk: improve behavior on init failure

Some failure modes are handled poorly by kublk. For example, if ublk_drv
is built as a module but not currently loaded into the kernel, ./kublk
add ... just hangs forever. This happens because in this case (and a few
others), the worker process does not notify its parent (via a write to
the shared eventfd) that it has tried and failed to initialize, so the
parent hangs forever. Fix this by ensuring that we always notify the
parent process of any initialization failure, and have the parent print
a (not very descriptive) log line when this happens.

Signed-off-by: Uday Shankar <ushankar@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20250603-ublk_init_fail-v1-1-87c91486230e@purestorage.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Uday Shankar and committed by
Jens Axboe
a2f4c1ae 43a67dd8

+23 -11
+23 -11
tools/testing/selftests/ublk/kublk.c
··· 1112 1112 __u64 features; 1113 1113 const struct ublk_tgt_ops *ops; 1114 1114 struct ublksrv_ctrl_dev_info *info; 1115 - struct ublk_dev *dev; 1115 + struct ublk_dev *dev = NULL; 1116 1116 int dev_id = ctx->dev_id; 1117 1117 int ret, i; 1118 1118 ··· 1120 1120 if (!ops) { 1121 1121 ublk_err("%s: no such tgt type, type %s\n", 1122 1122 __func__, tgt_type); 1123 - return -ENODEV; 1123 + ret = -ENODEV; 1124 + goto fail; 1124 1125 } 1125 1126 1126 1127 if (nr_queues > UBLK_MAX_QUEUES || depth > UBLK_QUEUE_DEPTH) { 1127 1128 ublk_err("%s: invalid nr_queues or depth queues %u depth %u\n", 1128 1129 __func__, nr_queues, depth); 1129 - return -EINVAL; 1130 + ret = -EINVAL; 1131 + goto fail; 1130 1132 } 1131 1133 1132 1134 /* default to 1:1 threads:queues if nthreads is unspecified */ ··· 1138 1136 if (nthreads > UBLK_MAX_THREADS) { 1139 1137 ublk_err("%s: %u is too many threads (max %u)\n", 1140 1138 __func__, nthreads, UBLK_MAX_THREADS); 1141 - return -EINVAL; 1139 + ret = -EINVAL; 1140 + goto fail; 1142 1141 } 1143 1142 1144 1143 if (nthreads != nr_queues && !ctx->per_io_tasks) { 1145 1144 ublk_err("%s: threads %u must be same as queues %u if " 1146 1145 "not using per_io_tasks\n", 1147 1146 __func__, nthreads, nr_queues); 1148 - return -EINVAL; 1147 + ret = -EINVAL; 1148 + goto fail; 1149 1149 } 1150 1150 1151 1151 dev = ublk_ctrl_init(); 1152 1152 if (!dev) { 1153 1153 ublk_err("%s: can't alloc dev id %d, type %s\n", 1154 1154 __func__, dev_id, tgt_type); 1155 - return -ENOMEM; 1155 + ret = -ENOMEM; 1156 + goto fail; 1156 1157 } 1157 1158 1158 1159 /* kernel doesn't support get_features */ 1159 1160 ret = ublk_ctrl_get_features(dev, &features); 1160 - if (ret < 0) 1161 - return -EINVAL; 1161 + if (ret < 0) { 1162 + ret = -EINVAL; 1163 + goto fail; 1164 + } 1162 1165 1163 - if (!(features & UBLK_F_CMD_IOCTL_ENCODE)) 1164 - return -ENOTSUP; 1166 + if (!(features & UBLK_F_CMD_IOCTL_ENCODE)) { 1167 + ret = -ENOTSUP; 1168 + goto fail; 1169 + } 1165 1170 1166 1171 info = &dev->dev_info; 1167 1172 info->dev_id = ctx->dev_id; ··· 1209 1200 fail: 1210 1201 if (ret < 0) 1211 1202 ublk_send_dev_event(ctx, dev, -1); 1212 - ublk_ctrl_deinit(dev); 1203 + if (dev) 1204 + ublk_ctrl_deinit(dev); 1213 1205 return ret; 1214 1206 } 1215 1207 ··· 1272 1262 shmctl(ctx->_shmid, IPC_RMID, NULL); 1273 1263 /* wait for child and detach from it */ 1274 1264 wait(NULL); 1265 + if (exit_code == EXIT_FAILURE) 1266 + ublk_err("%s: command failed\n", __func__); 1275 1267 exit(exit_code); 1276 1268 } else { 1277 1269 exit(EXIT_FAILURE);