1*94c4a1e1SFrank Piva // SPDX-License-Identifier: MIT or GPL-2.0-only
2*94c4a1e1SFrank Piva
3*94c4a1e1SFrank Piva #include "config.h"
4*94c4a1e1SFrank Piva #include "ublksrv_tgt.h"
5*94c4a1e1SFrank Piva
6*94c4a1e1SFrank Piva /* per-task variable */
7*94c4a1e1SFrank Piva static pthread_mutex_t jbuf_lock;
8*94c4a1e1SFrank Piva static int jbuf_size = 0;
9*94c4a1e1SFrank Piva static int queues_stored = 0;
10*94c4a1e1SFrank Piva static char *jbuf = NULL;
11*94c4a1e1SFrank Piva
12*94c4a1e1SFrank Piva struct ublksrv_queue_info {
13*94c4a1e1SFrank Piva const struct ublksrv_dev *dev;
14*94c4a1e1SFrank Piva int qid;
15*94c4a1e1SFrank Piva pthread_t thread;
16*94c4a1e1SFrank Piva };
17*94c4a1e1SFrank Piva
18*94c4a1e1SFrank Piva
19*94c4a1e1SFrank Piva /********************cmd handling************************/
20*94c4a1e1SFrank Piva static char *full_cmd;
21*94c4a1e1SFrank Piva static struct ublksrv_tgt_type *tgt_list[UBLKSRV_TGT_TYPE_MAX] = {};
22*94c4a1e1SFrank Piva
ublk_json_write_dev_info(struct ublksrv_dev * dev,char ** jbuf,int * len)23*94c4a1e1SFrank Piva int ublk_json_write_dev_info(struct ublksrv_dev *dev, char **jbuf, int *len)
24*94c4a1e1SFrank Piva {
25*94c4a1e1SFrank Piva int ret = 0;
26*94c4a1e1SFrank Piva
27*94c4a1e1SFrank Piva do {
28*94c4a1e1SFrank Piva ret = ublksrv_json_write_dev_info(ublksrv_get_ctrl_dev(dev),
29*94c4a1e1SFrank Piva *jbuf, *len);
30*94c4a1e1SFrank Piva if (ret < 0)
31*94c4a1e1SFrank Piva *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
32*94c4a1e1SFrank Piva } while (ret < 0);
33*94c4a1e1SFrank Piva
34*94c4a1e1SFrank Piva return ret;
35*94c4a1e1SFrank Piva }
36*94c4a1e1SFrank Piva
ublk_json_write_params(struct ublksrv_dev * dev,char ** jbuf,int * len,const struct ublk_params * p)37*94c4a1e1SFrank Piva int ublk_json_write_params(struct ublksrv_dev *dev, char **jbuf, int *len,
38*94c4a1e1SFrank Piva const struct ublk_params *p)
39*94c4a1e1SFrank Piva {
40*94c4a1e1SFrank Piva int ret = 0;
41*94c4a1e1SFrank Piva
42*94c4a1e1SFrank Piva do {
43*94c4a1e1SFrank Piva ret = ublksrv_json_write_params(p, *jbuf, *len);
44*94c4a1e1SFrank Piva if (ret < 0)
45*94c4a1e1SFrank Piva *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
46*94c4a1e1SFrank Piva } while (ret < 0);
47*94c4a1e1SFrank Piva
48*94c4a1e1SFrank Piva return ret;
49*94c4a1e1SFrank Piva }
50*94c4a1e1SFrank Piva
ublk_json_write_target_base(struct ublksrv_dev * dev,char ** jbuf,int * len,const struct ublksrv_tgt_base_json * tgt)51*94c4a1e1SFrank Piva int ublk_json_write_target_base(struct ublksrv_dev *dev, char **jbuf, int *len,
52*94c4a1e1SFrank Piva const struct ublksrv_tgt_base_json *tgt)
53*94c4a1e1SFrank Piva {
54*94c4a1e1SFrank Piva int ret = 0;
55*94c4a1e1SFrank Piva
56*94c4a1e1SFrank Piva do {
57*94c4a1e1SFrank Piva ret = ublksrv_json_write_target_base_info(*jbuf, *len, tgt);
58*94c4a1e1SFrank Piva if (ret < 0)
59*94c4a1e1SFrank Piva *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
60*94c4a1e1SFrank Piva } while (ret < 0);
61*94c4a1e1SFrank Piva
62*94c4a1e1SFrank Piva return ret;
63*94c4a1e1SFrank Piva
64*94c4a1e1SFrank Piva }
65*94c4a1e1SFrank Piva
ublk_json_write_tgt_str(struct ublksrv_dev * dev,char ** jbuf,int * len,const char * name,const char * val)66*94c4a1e1SFrank Piva int ublk_json_write_tgt_str(struct ublksrv_dev *dev, char **jbuf,
67*94c4a1e1SFrank Piva int *len, const char *name, const char *val)
68*94c4a1e1SFrank Piva {
69*94c4a1e1SFrank Piva int ret = 0;
70*94c4a1e1SFrank Piva
71*94c4a1e1SFrank Piva do {
72*94c4a1e1SFrank Piva if (val)
73*94c4a1e1SFrank Piva ret = ublksrv_json_write_target_str_info(*jbuf,
74*94c4a1e1SFrank Piva *len, name, val);
75*94c4a1e1SFrank Piva
76*94c4a1e1SFrank Piva if (ret < 0)
77*94c4a1e1SFrank Piva *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
78*94c4a1e1SFrank Piva } while (ret < 0);
79*94c4a1e1SFrank Piva
80*94c4a1e1SFrank Piva return ret;
81*94c4a1e1SFrank Piva }
82*94c4a1e1SFrank Piva
ublk_json_write_tgt_ulong(struct ublksrv_dev * dev,char ** jbuf,int * len,const char * name,unsigned long val)83*94c4a1e1SFrank Piva int ublk_json_write_tgt_ulong(struct ublksrv_dev *dev, char **jbuf,
84*94c4a1e1SFrank Piva int *len, const char *name, unsigned long val)
85*94c4a1e1SFrank Piva {
86*94c4a1e1SFrank Piva int ret = 0;
87*94c4a1e1SFrank Piva
88*94c4a1e1SFrank Piva do {
89*94c4a1e1SFrank Piva ret = ublksrv_json_write_target_ulong_info(*jbuf,
90*94c4a1e1SFrank Piva *len, name, val);
91*94c4a1e1SFrank Piva if (ret < 0)
92*94c4a1e1SFrank Piva *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
93*94c4a1e1SFrank Piva } while (ret < 0);
94*94c4a1e1SFrank Piva
95*94c4a1e1SFrank Piva return ret;
96*94c4a1e1SFrank Piva }
97*94c4a1e1SFrank Piva
ublk_json_write_tgt_long(struct ublksrv_dev * dev,char ** jbuf,int * len,const char * name,long val)98*94c4a1e1SFrank Piva int ublk_json_write_tgt_long(struct ublksrv_dev *dev, char **jbuf,
99*94c4a1e1SFrank Piva int *len, const char *name, long val)
100*94c4a1e1SFrank Piva {
101*94c4a1e1SFrank Piva int ret = 0;
102*94c4a1e1SFrank Piva
103*94c4a1e1SFrank Piva do {
104*94c4a1e1SFrank Piva ret = ublksrv_json_write_target_long_info(*jbuf,
105*94c4a1e1SFrank Piva *len, name, val);
106*94c4a1e1SFrank Piva if (ret < 0)
107*94c4a1e1SFrank Piva *jbuf = ublksrv_tgt_realloc_json_buf(dev, len);
108*94c4a1e1SFrank Piva } while (ret < 0);
109*94c4a1e1SFrank Piva
110*94c4a1e1SFrank Piva return ret;
111*94c4a1e1SFrank Piva }
112*94c4a1e1SFrank Piva
ublksrv_find_tgt_type(const char * name)113*94c4a1e1SFrank Piva static const struct ublksrv_tgt_type *ublksrv_find_tgt_type(const char *name)
114*94c4a1e1SFrank Piva {
115*94c4a1e1SFrank Piva int i;
116*94c4a1e1SFrank Piva
117*94c4a1e1SFrank Piva for (i = 0; i < UBLKSRV_TGT_TYPE_MAX; i++) {
118*94c4a1e1SFrank Piva const struct ublksrv_tgt_type *type = tgt_list[i];
119*94c4a1e1SFrank Piva
120*94c4a1e1SFrank Piva if (type == NULL)
121*94c4a1e1SFrank Piva continue;
122*94c4a1e1SFrank Piva
123*94c4a1e1SFrank Piva if (!strcmp(type->name, name))
124*94c4a1e1SFrank Piva return type;
125*94c4a1e1SFrank Piva }
126*94c4a1e1SFrank Piva
127*94c4a1e1SFrank Piva return NULL;
128*94c4a1e1SFrank Piva }
129*94c4a1e1SFrank Piva
ublksrv_for_each_tgt_type(void (* handle_tgt_type)(unsigned idx,const struct ublksrv_tgt_type * type,void * data),void * data)130*94c4a1e1SFrank Piva static void ublksrv_for_each_tgt_type(void (*handle_tgt_type)(unsigned idx,
131*94c4a1e1SFrank Piva const struct ublksrv_tgt_type *type, void *data),
132*94c4a1e1SFrank Piva void *data)
133*94c4a1e1SFrank Piva {
134*94c4a1e1SFrank Piva int i;
135*94c4a1e1SFrank Piva
136*94c4a1e1SFrank Piva for (i = 0; i < UBLKSRV_TGT_TYPE_MAX; i++) {
137*94c4a1e1SFrank Piva const struct ublksrv_tgt_type *type = tgt_list[i];
138*94c4a1e1SFrank Piva
139*94c4a1e1SFrank Piva if (!type)
140*94c4a1e1SFrank Piva continue;
141*94c4a1e1SFrank Piva handle_tgt_type(i, type, data);
142*94c4a1e1SFrank Piva }
143*94c4a1e1SFrank Piva }
144*94c4a1e1SFrank Piva
ublksrv_register_tgt_type(struct ublksrv_tgt_type * type)145*94c4a1e1SFrank Piva int ublksrv_register_tgt_type(struct ublksrv_tgt_type *type)
146*94c4a1e1SFrank Piva {
147*94c4a1e1SFrank Piva if (type->type < UBLKSRV_TGT_TYPE_MAX && !tgt_list[type->type]) {
148*94c4a1e1SFrank Piva tgt_list[type->type] = type;
149*94c4a1e1SFrank Piva return 0;
150*94c4a1e1SFrank Piva }
151*94c4a1e1SFrank Piva return -1;
152*94c4a1e1SFrank Piva }
153*94c4a1e1SFrank Piva
ublksrv_unregister_tgt_type(struct ublksrv_tgt_type * type)154*94c4a1e1SFrank Piva void ublksrv_unregister_tgt_type(struct ublksrv_tgt_type *type)
155*94c4a1e1SFrank Piva {
156*94c4a1e1SFrank Piva if (type->type < UBLKSRV_TGT_TYPE_MAX && tgt_list[type->type]) {
157*94c4a1e1SFrank Piva tgt_list[type->type] = NULL;
158*94c4a1e1SFrank Piva }
159*94c4a1e1SFrank Piva }
160*94c4a1e1SFrank Piva
161*94c4a1e1SFrank Piva
mprintf(const char * fmt,...)162*94c4a1e1SFrank Piva static char *mprintf(const char *fmt, ...)
163*94c4a1e1SFrank Piva {
164*94c4a1e1SFrank Piva va_list args;
165*94c4a1e1SFrank Piva char *str;
166*94c4a1e1SFrank Piva int ret;
167*94c4a1e1SFrank Piva
168*94c4a1e1SFrank Piva va_start(args, fmt);
169*94c4a1e1SFrank Piva ret = vasprintf(&str, fmt, args);
170*94c4a1e1SFrank Piva va_end(args);
171*94c4a1e1SFrank Piva
172*94c4a1e1SFrank Piva if (ret < 0) {
173*94c4a1e1SFrank Piva return NULL;
174*94c4a1e1SFrank Piva }
175*94c4a1e1SFrank Piva
176*94c4a1e1SFrank Piva return str;
177*94c4a1e1SFrank Piva }
178*94c4a1e1SFrank Piva
pop_cmd(int * argc,char * argv[])179*94c4a1e1SFrank Piva static char *pop_cmd(int *argc, char *argv[])
180*94c4a1e1SFrank Piva {
181*94c4a1e1SFrank Piva char *cmd = argv[1];
182*94c4a1e1SFrank Piva if (*argc < 2) {
183*94c4a1e1SFrank Piva return NULL;
184*94c4a1e1SFrank Piva }
185*94c4a1e1SFrank Piva
186*94c4a1e1SFrank Piva memmove(&argv[1], &argv[2], *argc * sizeof(argv[0]));
187*94c4a1e1SFrank Piva (*argc)--;
188*94c4a1e1SFrank Piva
189*94c4a1e1SFrank Piva full_cmd = mprintf("%s %s", full_cmd, cmd);
190*94c4a1e1SFrank Piva return cmd;
191*94c4a1e1SFrank Piva }
192*94c4a1e1SFrank Piva
start_daemon(void (* child_fn)(void *),void * data)193*94c4a1e1SFrank Piva static int start_daemon(void (*child_fn)(void *), void *data)
194*94c4a1e1SFrank Piva {
195*94c4a1e1SFrank Piva char path[PATH_MAX];
196*94c4a1e1SFrank Piva int fd;
197*94c4a1e1SFrank Piva char *res;
198*94c4a1e1SFrank Piva
199*94c4a1e1SFrank Piva if (setsid() == -1)
200*94c4a1e1SFrank Piva return -1;
201*94c4a1e1SFrank Piva
202*94c4a1e1SFrank Piva res = getcwd(path, PATH_MAX);
203*94c4a1e1SFrank Piva if (!res)
204*94c4a1e1SFrank Piva ublk_err("%s: %d getcwd failed %m\n", __func__, __LINE__);
205*94c4a1e1SFrank Piva
206*94c4a1e1SFrank Piva switch (fork()) {
207*94c4a1e1SFrank Piva case -1: return -1;
208*94c4a1e1SFrank Piva case 0: break;
209*94c4a1e1SFrank Piva default: _exit(EXIT_SUCCESS);
210*94c4a1e1SFrank Piva }
211*94c4a1e1SFrank Piva
212*94c4a1e1SFrank Piva if (chdir(path) != 0)
213*94c4a1e1SFrank Piva ublk_err("%s: %d chdir failed %m\n", __func__, __LINE__);
214*94c4a1e1SFrank Piva
215*94c4a1e1SFrank Piva close(STDIN_FILENO);
216*94c4a1e1SFrank Piva fd = open("/dev/null", O_RDWR);
217*94c4a1e1SFrank Piva if (fd != STDIN_FILENO)
218*94c4a1e1SFrank Piva return -1;
219*94c4a1e1SFrank Piva if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO)
220*94c4a1e1SFrank Piva return -1;
221*94c4a1e1SFrank Piva if (dup2(fd, STDERR_FILENO) != STDERR_FILENO)
222*94c4a1e1SFrank Piva return -1;
223*94c4a1e1SFrank Piva
224*94c4a1e1SFrank Piva child_fn(data);
225*94c4a1e1SFrank Piva return 0;
226*94c4a1e1SFrank Piva }
227*94c4a1e1SFrank Piva
__ublksrv_tgt_return_json_buf(struct ublksrv_dev * dev,int * size)228*94c4a1e1SFrank Piva char *__ublksrv_tgt_return_json_buf(struct ublksrv_dev *dev, int *size)
229*94c4a1e1SFrank Piva {
230*94c4a1e1SFrank Piva if (jbuf == NULL) {
231*94c4a1e1SFrank Piva jbuf_size = 1024;
232*94c4a1e1SFrank Piva jbuf = (char *)realloc((void *)jbuf, jbuf_size);
233*94c4a1e1SFrank Piva }
234*94c4a1e1SFrank Piva *size = jbuf_size;
235*94c4a1e1SFrank Piva
236*94c4a1e1SFrank Piva return jbuf;
237*94c4a1e1SFrank Piva }
238*94c4a1e1SFrank Piva
ublksrv_tgt_return_json_buf(struct ublksrv_dev * dev,int * size)239*94c4a1e1SFrank Piva char *ublksrv_tgt_return_json_buf(struct ublksrv_dev *dev, int *size)
240*94c4a1e1SFrank Piva {
241*94c4a1e1SFrank Piva char *buf;
242*94c4a1e1SFrank Piva
243*94c4a1e1SFrank Piva pthread_mutex_lock(&jbuf_lock);
244*94c4a1e1SFrank Piva buf = __ublksrv_tgt_return_json_buf(dev, size);
245*94c4a1e1SFrank Piva pthread_mutex_unlock(&jbuf_lock);
246*94c4a1e1SFrank Piva
247*94c4a1e1SFrank Piva return buf;
248*94c4a1e1SFrank Piva }
249*94c4a1e1SFrank Piva
__ublksrv_tgt_realloc_json_buf(const struct ublksrv_dev * dev,int * size)250*94c4a1e1SFrank Piva static char *__ublksrv_tgt_realloc_json_buf(const struct ublksrv_dev *dev, int *size)
251*94c4a1e1SFrank Piva {
252*94c4a1e1SFrank Piva if (jbuf == NULL)
253*94c4a1e1SFrank Piva jbuf_size = 1024;
254*94c4a1e1SFrank Piva else
255*94c4a1e1SFrank Piva jbuf_size += 1024;
256*94c4a1e1SFrank Piva
257*94c4a1e1SFrank Piva jbuf = (char *)realloc((void *)jbuf, jbuf_size);
258*94c4a1e1SFrank Piva *size = jbuf_size;
259*94c4a1e1SFrank Piva
260*94c4a1e1SFrank Piva return jbuf;
261*94c4a1e1SFrank Piva }
262*94c4a1e1SFrank Piva
ublksrv_tgt_realloc_json_buf(struct ublksrv_dev * dev,int * size)263*94c4a1e1SFrank Piva char *ublksrv_tgt_realloc_json_buf(struct ublksrv_dev *dev, int *size)
264*94c4a1e1SFrank Piva {
265*94c4a1e1SFrank Piva char *buf;
266*94c4a1e1SFrank Piva
267*94c4a1e1SFrank Piva pthread_mutex_lock(&jbuf_lock);
268*94c4a1e1SFrank Piva buf = __ublksrv_tgt_realloc_json_buf(dev, size);
269*94c4a1e1SFrank Piva pthread_mutex_unlock(&jbuf_lock);
270*94c4a1e1SFrank Piva
271*94c4a1e1SFrank Piva return buf;
272*94c4a1e1SFrank Piva }
273*94c4a1e1SFrank Piva
ublksrv_tgt_store_dev_data(const struct ublksrv_dev * dev,const char * buf)274*94c4a1e1SFrank Piva static int ublksrv_tgt_store_dev_data(const struct ublksrv_dev *dev,
275*94c4a1e1SFrank Piva const char *buf)
276*94c4a1e1SFrank Piva {
277*94c4a1e1SFrank Piva int ret;
278*94c4a1e1SFrank Piva int len = ublksrv_json_get_length(buf);
279*94c4a1e1SFrank Piva int fd = ublksrv_get_pidfile_fd(dev);
280*94c4a1e1SFrank Piva
281*94c4a1e1SFrank Piva if (fd < 0) {
282*94c4a1e1SFrank Piva ublk_err( "fail to get fd of pid file, ret %d\n",
283*94c4a1e1SFrank Piva fd);
284*94c4a1e1SFrank Piva return fd;
285*94c4a1e1SFrank Piva }
286*94c4a1e1SFrank Piva
287*94c4a1e1SFrank Piva ret = pwrite(fd, buf, len, JSON_OFFSET);
288*94c4a1e1SFrank Piva if (ret <= 0)
289*94c4a1e1SFrank Piva ublk_err( "fail to write json data to pid file, ret %d\n",
290*94c4a1e1SFrank Piva ret);
291*94c4a1e1SFrank Piva
292*94c4a1e1SFrank Piva return ret;
293*94c4a1e1SFrank Piva }
294*94c4a1e1SFrank Piva
ublksrv_tgt_get_dev_data(struct ublksrv_ctrl_dev * cdev)295*94c4a1e1SFrank Piva static char *ublksrv_tgt_get_dev_data(struct ublksrv_ctrl_dev *cdev)
296*94c4a1e1SFrank Piva {
297*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *info =
298*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(cdev);
299*94c4a1e1SFrank Piva int dev_id = info->dev_id;
300*94c4a1e1SFrank Piva struct stat st;
301*94c4a1e1SFrank Piva char pid_file[256];
302*94c4a1e1SFrank Piva char *buf;
303*94c4a1e1SFrank Piva int size, fd, ret;
304*94c4a1e1SFrank Piva const char *run_dir = ublksrv_ctrl_get_run_dir(cdev);
305*94c4a1e1SFrank Piva
306*94c4a1e1SFrank Piva if (!run_dir)
307*94c4a1e1SFrank Piva return 0;
308*94c4a1e1SFrank Piva
309*94c4a1e1SFrank Piva snprintf(pid_file, 256, "%s/%d.pid", run_dir, dev_id);
310*94c4a1e1SFrank Piva fd = open(pid_file, O_RDONLY);
311*94c4a1e1SFrank Piva
312*94c4a1e1SFrank Piva if (fd <= 0)
313*94c4a1e1SFrank Piva return NULL;
314*94c4a1e1SFrank Piva
315*94c4a1e1SFrank Piva if (fstat(fd, &st) < 0)
316*94c4a1e1SFrank Piva return NULL;
317*94c4a1e1SFrank Piva
318*94c4a1e1SFrank Piva if (st.st_size <= JSON_OFFSET)
319*94c4a1e1SFrank Piva return NULL;
320*94c4a1e1SFrank Piva
321*94c4a1e1SFrank Piva size = st.st_size - JSON_OFFSET;
322*94c4a1e1SFrank Piva buf = (char *)malloc(size);
323*94c4a1e1SFrank Piva ret = pread(fd, buf, size, JSON_OFFSET);
324*94c4a1e1SFrank Piva if (ret <= 0)
325*94c4a1e1SFrank Piva fprintf(stderr, "fail to read json from %s ret %d\n",
326*94c4a1e1SFrank Piva pid_file, ret);
327*94c4a1e1SFrank Piva close(fd);
328*94c4a1e1SFrank Piva
329*94c4a1e1SFrank Piva return buf;
330*94c4a1e1SFrank Piva }
331*94c4a1e1SFrank Piva
ublksrv_io_handler_fn(void * data)332*94c4a1e1SFrank Piva static void *ublksrv_io_handler_fn(void *data)
333*94c4a1e1SFrank Piva {
334*94c4a1e1SFrank Piva struct ublksrv_queue_info *info = (struct ublksrv_queue_info *)data;
335*94c4a1e1SFrank Piva const struct ublksrv_dev *dev = info->dev;
336*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev *cdev = ublksrv_get_ctrl_dev(dev);
337*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *dinfo =
338*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(cdev);
339*94c4a1e1SFrank Piva unsigned dev_id = dinfo->dev_id;
340*94c4a1e1SFrank Piva unsigned short q_id = info->qid;
341*94c4a1e1SFrank Piva const struct ublksrv_queue *q;
342*94c4a1e1SFrank Piva int ret;
343*94c4a1e1SFrank Piva int buf_size;
344*94c4a1e1SFrank Piva char *buf;
345*94c4a1e1SFrank Piva const char *jbuf;
346*94c4a1e1SFrank Piva
347*94c4a1e1SFrank Piva pthread_mutex_lock(&jbuf_lock);
348*94c4a1e1SFrank Piva
349*94c4a1e1SFrank Piva if (!ublksrv_is_recovering(cdev)) {
350*94c4a1e1SFrank Piva do {
351*94c4a1e1SFrank Piva buf = __ublksrv_tgt_realloc_json_buf(dev, &buf_size);
352*94c4a1e1SFrank Piva ret = ublksrv_json_write_queue_info(cdev, buf, buf_size,
353*94c4a1e1SFrank Piva q_id, ublksrv_gettid());
354*94c4a1e1SFrank Piva } while (ret < 0);
355*94c4a1e1SFrank Piva jbuf = buf;
356*94c4a1e1SFrank Piva } else {
357*94c4a1e1SFrank Piva jbuf = ublksrv_ctrl_get_recovery_jbuf(cdev);
358*94c4a1e1SFrank Piva }
359*94c4a1e1SFrank Piva queues_stored++;
360*94c4a1e1SFrank Piva
361*94c4a1e1SFrank Piva /*
362*94c4a1e1SFrank Piva * A bit ugly to store json buffer to pid file here, but no easy
363*94c4a1e1SFrank Piva * way to do it in control task side, so far, so good
364*94c4a1e1SFrank Piva */
365*94c4a1e1SFrank Piva if (queues_stored == dinfo->nr_hw_queues)
366*94c4a1e1SFrank Piva ublksrv_tgt_store_dev_data(dev, jbuf);
367*94c4a1e1SFrank Piva pthread_mutex_unlock(&jbuf_lock);
368*94c4a1e1SFrank Piva
369*94c4a1e1SFrank Piva q = ublksrv_queue_init(dev, q_id, NULL);
370*94c4a1e1SFrank Piva if (!q) {
371*94c4a1e1SFrank Piva ublk_err("ublk dev %d queue %d init queue failed",
372*94c4a1e1SFrank Piva dev_id, q_id);
373*94c4a1e1SFrank Piva return NULL;
374*94c4a1e1SFrank Piva }
375*94c4a1e1SFrank Piva
376*94c4a1e1SFrank Piva ublk_log("tid %d: ublk dev %d queue %d started", ublksrv_gettid(),
377*94c4a1e1SFrank Piva dev_id, q->q_id);
378*94c4a1e1SFrank Piva do {
379*94c4a1e1SFrank Piva if (ublksrv_process_io(q) < 0)
380*94c4a1e1SFrank Piva break;
381*94c4a1e1SFrank Piva } while (1);
382*94c4a1e1SFrank Piva
383*94c4a1e1SFrank Piva ublk_log("ublk dev %d queue %d exited", dev_id, q->q_id);
384*94c4a1e1SFrank Piva ublksrv_queue_deinit(q);
385*94c4a1e1SFrank Piva return NULL;
386*94c4a1e1SFrank Piva }
387*94c4a1e1SFrank Piva
sig_handler(int sig)388*94c4a1e1SFrank Piva static void sig_handler(int sig)
389*94c4a1e1SFrank Piva {
390*94c4a1e1SFrank Piva if (sig == SIGTERM)
391*94c4a1e1SFrank Piva ublk_log("got TERM signal");
392*94c4a1e1SFrank Piva }
393*94c4a1e1SFrank Piva
setup_pthread_sigmask()394*94c4a1e1SFrank Piva static void setup_pthread_sigmask()
395*94c4a1e1SFrank Piva {
396*94c4a1e1SFrank Piva sigset_t signal_mask;
397*94c4a1e1SFrank Piva
398*94c4a1e1SFrank Piva if (signal(SIGTERM, sig_handler) == SIG_ERR)
399*94c4a1e1SFrank Piva return;
400*94c4a1e1SFrank Piva
401*94c4a1e1SFrank Piva /* make sure SIGTERM won't be blocked */
402*94c4a1e1SFrank Piva sigemptyset(&signal_mask);
403*94c4a1e1SFrank Piva sigaddset(&signal_mask, SIGINT);
404*94c4a1e1SFrank Piva sigaddset(&signal_mask, SIGTERM);
405*94c4a1e1SFrank Piva pthread_sigmask(SIG_BLOCK, &signal_mask, NULL);
406*94c4a1e1SFrank Piva }
407*94c4a1e1SFrank Piva
408*94c4a1e1SFrank Piva /*
409*94c4a1e1SFrank Piva * Now STOP DEV ctrl command has been sent to /dev/ublk-control,
410*94c4a1e1SFrank Piva * and wait until all pending fetch commands are canceled
411*94c4a1e1SFrank Piva */
ublksrv_drain_fetch_commands(const struct ublksrv_dev * dev,struct ublksrv_queue_info * info)412*94c4a1e1SFrank Piva static void ublksrv_drain_fetch_commands(const struct ublksrv_dev *dev,
413*94c4a1e1SFrank Piva struct ublksrv_queue_info *info)
414*94c4a1e1SFrank Piva {
415*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *dinfo =
416*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(ublksrv_get_ctrl_dev(dev));
417*94c4a1e1SFrank Piva unsigned nr_queues = dinfo->nr_hw_queues;
418*94c4a1e1SFrank Piva int i;
419*94c4a1e1SFrank Piva void *ret;
420*94c4a1e1SFrank Piva
421*94c4a1e1SFrank Piva for (i = 0; i < nr_queues; i++)
422*94c4a1e1SFrank Piva pthread_join(info[i].thread, &ret);
423*94c4a1e1SFrank Piva }
424*94c4a1e1SFrank Piva
425*94c4a1e1SFrank Piva
ublksrv_io_handler(void * data)426*94c4a1e1SFrank Piva static void ublksrv_io_handler(void *data)
427*94c4a1e1SFrank Piva {
428*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev *ctrl_dev = (struct ublksrv_ctrl_dev *)data;
429*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *dinfo =
430*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(ctrl_dev);
431*94c4a1e1SFrank Piva int dev_id = dinfo->dev_id;
432*94c4a1e1SFrank Piva int i;
433*94c4a1e1SFrank Piva char buf[32];
434*94c4a1e1SFrank Piva const struct ublksrv_dev *dev;
435*94c4a1e1SFrank Piva struct ublksrv_queue_info *info_array;
436*94c4a1e1SFrank Piva
437*94c4a1e1SFrank Piva snprintf(buf, 32, "%s-%d", "ublksrvd", dev_id);
438*94c4a1e1SFrank Piva openlog(buf, LOG_PID, LOG_USER);
439*94c4a1e1SFrank Piva
440*94c4a1e1SFrank Piva ublk_log("start ublksrv io daemon %s\n", buf);
441*94c4a1e1SFrank Piva
442*94c4a1e1SFrank Piva pthread_mutex_init(&jbuf_lock, NULL);
443*94c4a1e1SFrank Piva
444*94c4a1e1SFrank Piva dev = ublksrv_dev_init(ctrl_dev);
445*94c4a1e1SFrank Piva if (!dev) {
446*94c4a1e1SFrank Piva ublk_err( "dev-%d start ubsrv failed", dev_id);
447*94c4a1e1SFrank Piva goto out;
448*94c4a1e1SFrank Piva }
449*94c4a1e1SFrank Piva
450*94c4a1e1SFrank Piva setup_pthread_sigmask();
451*94c4a1e1SFrank Piva
452*94c4a1e1SFrank Piva if (!(dinfo->flags & UBLK_F_UNPRIVILEGED_DEV))
453*94c4a1e1SFrank Piva ublksrv_apply_oom_protection();
454*94c4a1e1SFrank Piva
455*94c4a1e1SFrank Piva info_array = (struct ublksrv_queue_info *)calloc(sizeof(
456*94c4a1e1SFrank Piva struct ublksrv_queue_info),
457*94c4a1e1SFrank Piva dinfo->nr_hw_queues);
458*94c4a1e1SFrank Piva
459*94c4a1e1SFrank Piva for (i = 0; i < dinfo->nr_hw_queues; i++) {
460*94c4a1e1SFrank Piva info_array[i].dev = dev;
461*94c4a1e1SFrank Piva info_array[i].qid = i;
462*94c4a1e1SFrank Piva pthread_create(&info_array[i].thread, NULL,
463*94c4a1e1SFrank Piva ublksrv_io_handler_fn,
464*94c4a1e1SFrank Piva &info_array[i]);
465*94c4a1e1SFrank Piva }
466*94c4a1e1SFrank Piva
467*94c4a1e1SFrank Piva /* wait until we are terminated */
468*94c4a1e1SFrank Piva ublksrv_drain_fetch_commands(dev, info_array);
469*94c4a1e1SFrank Piva free(info_array);
470*94c4a1e1SFrank Piva free(jbuf);
471*94c4a1e1SFrank Piva
472*94c4a1e1SFrank Piva ublksrv_dev_deinit(dev);
473*94c4a1e1SFrank Piva out:
474*94c4a1e1SFrank Piva ublk_log("end ublksrv io daemon");
475*94c4a1e1SFrank Piva closelog();
476*94c4a1e1SFrank Piva }
477*94c4a1e1SFrank Piva
478*94c4a1e1SFrank Piva /* Not called from ublksrv daemon */
ublksrv_start_io_daemon(const struct ublksrv_ctrl_dev * dev)479*94c4a1e1SFrank Piva static int ublksrv_start_io_daemon(const struct ublksrv_ctrl_dev *dev)
480*94c4a1e1SFrank Piva {
481*94c4a1e1SFrank Piva start_daemon(ublksrv_io_handler, (void *)dev);
482*94c4a1e1SFrank Piva return 0;
483*94c4a1e1SFrank Piva }
484*94c4a1e1SFrank Piva
ublksrv_check_dev_data(const char * buf,int size)485*94c4a1e1SFrank Piva static int ublksrv_check_dev_data(const char *buf, int size)
486*94c4a1e1SFrank Piva {
487*94c4a1e1SFrank Piva struct ublk_params p;
488*94c4a1e1SFrank Piva
489*94c4a1e1SFrank Piva if (size < JSON_OFFSET)
490*94c4a1e1SFrank Piva return -EINVAL;
491*94c4a1e1SFrank Piva
492*94c4a1e1SFrank Piva return ublksrv_json_read_params(&p, &buf[JSON_OFFSET]);
493*94c4a1e1SFrank Piva }
494*94c4a1e1SFrank Piva
ublksrv_get_io_daemon_pid(const struct ublksrv_ctrl_dev * ctrl_dev,bool check_data)495*94c4a1e1SFrank Piva static int ublksrv_get_io_daemon_pid(const struct ublksrv_ctrl_dev *ctrl_dev,
496*94c4a1e1SFrank Piva bool check_data)
497*94c4a1e1SFrank Piva {
498*94c4a1e1SFrank Piva const char *run_dir = ublksrv_ctrl_get_run_dir(ctrl_dev);
499*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *info =
500*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(ctrl_dev);
501*94c4a1e1SFrank Piva int ret = -1, pid_fd;
502*94c4a1e1SFrank Piva char path[256];
503*94c4a1e1SFrank Piva char *buf = NULL;
504*94c4a1e1SFrank Piva int size = JSON_OFFSET;
505*94c4a1e1SFrank Piva int daemon_pid;
506*94c4a1e1SFrank Piva struct stat st;
507*94c4a1e1SFrank Piva
508*94c4a1e1SFrank Piva if (!run_dir)
509*94c4a1e1SFrank Piva return -EINVAL;
510*94c4a1e1SFrank Piva
511*94c4a1e1SFrank Piva snprintf(path, 256, "%s/%d.pid", run_dir, info->dev_id);
512*94c4a1e1SFrank Piva
513*94c4a1e1SFrank Piva pid_fd = open(path, O_RDONLY);
514*94c4a1e1SFrank Piva if (pid_fd < 0)
515*94c4a1e1SFrank Piva goto out;
516*94c4a1e1SFrank Piva
517*94c4a1e1SFrank Piva if (fstat(pid_fd, &st) < 0)
518*94c4a1e1SFrank Piva goto out;
519*94c4a1e1SFrank Piva
520*94c4a1e1SFrank Piva if (check_data)
521*94c4a1e1SFrank Piva size = st.st_size;
522*94c4a1e1SFrank Piva else
523*94c4a1e1SFrank Piva size = JSON_OFFSET;
524*94c4a1e1SFrank Piva
525*94c4a1e1SFrank Piva buf = (char *)malloc(size);
526*94c4a1e1SFrank Piva if (read(pid_fd, buf, size) <= 0)
527*94c4a1e1SFrank Piva goto out;
528*94c4a1e1SFrank Piva
529*94c4a1e1SFrank Piva daemon_pid = strtol(buf, NULL, 10);
530*94c4a1e1SFrank Piva if (daemon_pid < 0)
531*94c4a1e1SFrank Piva goto out;
532*94c4a1e1SFrank Piva
533*94c4a1e1SFrank Piva ret = kill(daemon_pid, 0);
534*94c4a1e1SFrank Piva if (ret)
535*94c4a1e1SFrank Piva goto out;
536*94c4a1e1SFrank Piva
537*94c4a1e1SFrank Piva if (check_data) {
538*94c4a1e1SFrank Piva ret = ublksrv_check_dev_data(buf, size);
539*94c4a1e1SFrank Piva if (ret)
540*94c4a1e1SFrank Piva goto out;
541*94c4a1e1SFrank Piva }
542*94c4a1e1SFrank Piva ret = daemon_pid;
543*94c4a1e1SFrank Piva out:
544*94c4a1e1SFrank Piva if (pid_fd > 0)
545*94c4a1e1SFrank Piva close(pid_fd);
546*94c4a1e1SFrank Piva free(buf);
547*94c4a1e1SFrank Piva return ret;
548*94c4a1e1SFrank Piva }
549*94c4a1e1SFrank Piva
550*94c4a1e1SFrank Piva /* Not called from ublksrv daemon */
ublksrv_stop_io_daemon(const struct ublksrv_ctrl_dev * ctrl_dev)551*94c4a1e1SFrank Piva static int ublksrv_stop_io_daemon(const struct ublksrv_ctrl_dev *ctrl_dev)
552*94c4a1e1SFrank Piva {
553*94c4a1e1SFrank Piva int daemon_pid, cnt = 0;
554*94c4a1e1SFrank Piva
555*94c4a1e1SFrank Piva /* wait until daemon is exited, or timeout after 3 seconds */
556*94c4a1e1SFrank Piva do {
557*94c4a1e1SFrank Piva daemon_pid = ublksrv_get_io_daemon_pid(ctrl_dev, false);
558*94c4a1e1SFrank Piva if (daemon_pid > 0) {
559*94c4a1e1SFrank Piva usleep(100000);
560*94c4a1e1SFrank Piva cnt++;
561*94c4a1e1SFrank Piva }
562*94c4a1e1SFrank Piva } while (daemon_pid > 0 && cnt < 30);
563*94c4a1e1SFrank Piva
564*94c4a1e1SFrank Piva if (daemon_pid > 0)
565*94c4a1e1SFrank Piva return -1;
566*94c4a1e1SFrank Piva
567*94c4a1e1SFrank Piva return 0;
568*94c4a1e1SFrank Piva }
569*94c4a1e1SFrank Piva
570*94c4a1e1SFrank Piva /* Wait until ublk device is setup by udev */
ublksrv_check_dev(const struct ublksrv_ctrl_dev_info * info)571*94c4a1e1SFrank Piva static void ublksrv_check_dev(const struct ublksrv_ctrl_dev_info *info)
572*94c4a1e1SFrank Piva {
573*94c4a1e1SFrank Piva unsigned int max_time = 1000000, wait = 0;
574*94c4a1e1SFrank Piva char buf[64];
575*94c4a1e1SFrank Piva
576*94c4a1e1SFrank Piva snprintf(buf, 64, "%s%d", "/dev/ublkc", info->dev_id);
577*94c4a1e1SFrank Piva
578*94c4a1e1SFrank Piva while (wait < max_time) {
579*94c4a1e1SFrank Piva int fd = open(buf, O_RDWR);
580*94c4a1e1SFrank Piva
581*94c4a1e1SFrank Piva if (fd > 0) {
582*94c4a1e1SFrank Piva close(fd);
583*94c4a1e1SFrank Piva break;
584*94c4a1e1SFrank Piva }
585*94c4a1e1SFrank Piva
586*94c4a1e1SFrank Piva usleep(100000);
587*94c4a1e1SFrank Piva wait += 100000;
588*94c4a1e1SFrank Piva }
589*94c4a1e1SFrank Piva }
590*94c4a1e1SFrank Piva
ublksrv_start_daemon(struct ublksrv_ctrl_dev * ctrl_dev)591*94c4a1e1SFrank Piva static int ublksrv_start_daemon(struct ublksrv_ctrl_dev *ctrl_dev)
592*94c4a1e1SFrank Piva {
593*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *dinfo =
594*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(ctrl_dev);
595*94c4a1e1SFrank Piva int cnt = 0, daemon_pid, ret;
596*94c4a1e1SFrank Piva
597*94c4a1e1SFrank Piva ublksrv_check_dev(dinfo);
598*94c4a1e1SFrank Piva
599*94c4a1e1SFrank Piva ret = ublksrv_ctrl_get_affinity(ctrl_dev);
600*94c4a1e1SFrank Piva if (ret < 0) {
601*94c4a1e1SFrank Piva fprintf(stderr, "dev %d get affinity failed %d\n",
602*94c4a1e1SFrank Piva dinfo->dev_id, ret);
603*94c4a1e1SFrank Piva return -1;
604*94c4a1e1SFrank Piva }
605*94c4a1e1SFrank Piva
606*94c4a1e1SFrank Piva switch (fork()) {
607*94c4a1e1SFrank Piva case -1:
608*94c4a1e1SFrank Piva return -1;
609*94c4a1e1SFrank Piva case 0:
610*94c4a1e1SFrank Piva ublksrv_start_io_daemon(ctrl_dev);
611*94c4a1e1SFrank Piva break;
612*94c4a1e1SFrank Piva }
613*94c4a1e1SFrank Piva
614*94c4a1e1SFrank Piva /* wait until daemon is started, or timeout after 3 seconds */
615*94c4a1e1SFrank Piva do {
616*94c4a1e1SFrank Piva daemon_pid = ublksrv_get_io_daemon_pid(ctrl_dev, true);
617*94c4a1e1SFrank Piva if (daemon_pid < 0) {
618*94c4a1e1SFrank Piva usleep(100000);
619*94c4a1e1SFrank Piva cnt++;
620*94c4a1e1SFrank Piva }
621*94c4a1e1SFrank Piva } while (daemon_pid < 0 && cnt < 30);
622*94c4a1e1SFrank Piva
623*94c4a1e1SFrank Piva return daemon_pid;
624*94c4a1e1SFrank Piva }
625*94c4a1e1SFrank Piva
626*94c4a1e1SFrank Piva //todo: resolve stack usage warning for mkpath/__mkpath
627*94c4a1e1SFrank Piva #pragma GCC diagnostic push
628*94c4a1e1SFrank Piva #pragma GCC diagnostic ignored "-Wstack-usage="
__mkpath(char * dir,mode_t mode)629*94c4a1e1SFrank Piva static int __mkpath(char *dir, mode_t mode)
630*94c4a1e1SFrank Piva {
631*94c4a1e1SFrank Piva struct stat sb;
632*94c4a1e1SFrank Piva int ret;
633*94c4a1e1SFrank Piva mode_t mask;
634*94c4a1e1SFrank Piva
635*94c4a1e1SFrank Piva if (!dir)
636*94c4a1e1SFrank Piva return -EINVAL;
637*94c4a1e1SFrank Piva
638*94c4a1e1SFrank Piva if (!stat(dir, &sb))
639*94c4a1e1SFrank Piva return 0;
640*94c4a1e1SFrank Piva
641*94c4a1e1SFrank Piva __mkpath(dirname(strdupa(dir)), mode);
642*94c4a1e1SFrank Piva
643*94c4a1e1SFrank Piva mask = umask(0);
644*94c4a1e1SFrank Piva ret = mkdir(dir, mode);
645*94c4a1e1SFrank Piva umask(mask);
646*94c4a1e1SFrank Piva
647*94c4a1e1SFrank Piva return ret;
648*94c4a1e1SFrank Piva }
649*94c4a1e1SFrank Piva
mkpath(const char * dir)650*94c4a1e1SFrank Piva static int mkpath(const char *dir)
651*94c4a1e1SFrank Piva {
652*94c4a1e1SFrank Piva return __mkpath(strdupa(dir), S_IRWXU | S_IRWXG | S_IRWXO);
653*94c4a1e1SFrank Piva }
654*94c4a1e1SFrank Piva #pragma GCC diagnostic pop
655*94c4a1e1SFrank Piva
ublksrv_tgt_set_params(struct ublksrv_ctrl_dev * cdev,const char * jbuf)656*94c4a1e1SFrank Piva static void ublksrv_tgt_set_params(struct ublksrv_ctrl_dev *cdev,
657*94c4a1e1SFrank Piva const char *jbuf)
658*94c4a1e1SFrank Piva {
659*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *info =
660*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(cdev);
661*94c4a1e1SFrank Piva int dev_id = info->dev_id;
662*94c4a1e1SFrank Piva struct ublk_params p;
663*94c4a1e1SFrank Piva int ret;
664*94c4a1e1SFrank Piva
665*94c4a1e1SFrank Piva ret = ublksrv_json_read_params(&p, jbuf);
666*94c4a1e1SFrank Piva if (ret >= 0) {
667*94c4a1e1SFrank Piva ret = ublksrv_ctrl_set_params(cdev, &p);
668*94c4a1e1SFrank Piva if (ret)
669*94c4a1e1SFrank Piva fprintf(stderr, "set param for dev %d failed %d\n",
670*94c4a1e1SFrank Piva dev_id, ret);
671*94c4a1e1SFrank Piva } else {
672*94c4a1e1SFrank Piva fprintf(stderr, "params not found for dev %d failed %d\n",
673*94c4a1e1SFrank Piva dev_id, ret);
674*94c4a1e1SFrank Piva }
675*94c4a1e1SFrank Piva }
676*94c4a1e1SFrank Piva
cmd_dev_add(int argc,char * argv[])677*94c4a1e1SFrank Piva static int cmd_dev_add(int argc, char *argv[])
678*94c4a1e1SFrank Piva {
679*94c4a1e1SFrank Piva static const struct option longopts[] = {
680*94c4a1e1SFrank Piva { "type", 1, NULL, 't' },
681*94c4a1e1SFrank Piva { "number", 1, NULL, 'n' },
682*94c4a1e1SFrank Piva { "queues", 1, NULL, 'q' },
683*94c4a1e1SFrank Piva { "depth", 1, NULL, 'd' },
684*94c4a1e1SFrank Piva { "zero_copy", 1, NULL, 'z' },
685*94c4a1e1SFrank Piva { "uring_comp", 1, NULL, 'u' },
686*94c4a1e1SFrank Piva { "need_get_data", 1, NULL, 'g' },
687*94c4a1e1SFrank Piva { "user_recovery", 1, NULL, 'r'},
688*94c4a1e1SFrank Piva { "user_recovery_reissue", 1, NULL, 'i'},
689*94c4a1e1SFrank Piva { "debug_mask", 1, NULL, 0},
690*94c4a1e1SFrank Piva { "unprivileged", 0, NULL, 0},
691*94c4a1e1SFrank Piva { "usercopy", 0, NULL, 0},
692*94c4a1e1SFrank Piva { NULL }
693*94c4a1e1SFrank Piva };
694*94c4a1e1SFrank Piva struct ublksrv_dev_data data = {0};
695*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev *dev;
696*94c4a1e1SFrank Piva const struct ublksrv_tgt_type *tgt_type;
697*94c4a1e1SFrank Piva int opt, ret;
698*94c4a1e1SFrank Piva int uring_comp = 0;
699*94c4a1e1SFrank Piva int need_get_data = 0;
700*94c4a1e1SFrank Piva int user_recovery = 0;
701*94c4a1e1SFrank Piva int user_recovery_reissue = 0;
702*94c4a1e1SFrank Piva int unprivileged = 0;
703*94c4a1e1SFrank Piva const char *dump_buf;
704*94c4a1e1SFrank Piva int option_index = 0;
705*94c4a1e1SFrank Piva unsigned int debug_mask = 0;
706*94c4a1e1SFrank Piva
707*94c4a1e1SFrank Piva data.queue_depth = DEF_QD;
708*94c4a1e1SFrank Piva data.nr_hw_queues = DEF_NR_HW_QUEUES;
709*94c4a1e1SFrank Piva data.dev_id = -1;
710*94c4a1e1SFrank Piva data.run_dir = UBLKSRV_PID_DIR;
711*94c4a1e1SFrank Piva
712*94c4a1e1SFrank Piva mkpath(data.run_dir);
713*94c4a1e1SFrank Piva
714*94c4a1e1SFrank Piva while ((opt = getopt_long(argc, argv, "-:t:n:d:q:u:g:r:i:z",
715*94c4a1e1SFrank Piva longopts, &option_index)) != -1) {
716*94c4a1e1SFrank Piva switch (opt) {
717*94c4a1e1SFrank Piva case 'n':
718*94c4a1e1SFrank Piva data.dev_id = strtol(optarg, NULL, 10);
719*94c4a1e1SFrank Piva break;
720*94c4a1e1SFrank Piva case 't':
721*94c4a1e1SFrank Piva data.tgt_type = optarg;
722*94c4a1e1SFrank Piva break;
723*94c4a1e1SFrank Piva case 'z':
724*94c4a1e1SFrank Piva data.flags |= UBLK_F_SUPPORT_ZERO_COPY;
725*94c4a1e1SFrank Piva break;
726*94c4a1e1SFrank Piva case 'q':
727*94c4a1e1SFrank Piva data.nr_hw_queues = strtol(optarg, NULL, 10);
728*94c4a1e1SFrank Piva break;
729*94c4a1e1SFrank Piva case 'd':
730*94c4a1e1SFrank Piva data.queue_depth = strtol(optarg, NULL, 10);
731*94c4a1e1SFrank Piva break;
732*94c4a1e1SFrank Piva case 'u':
733*94c4a1e1SFrank Piva uring_comp = strtol(optarg, NULL, 10);
734*94c4a1e1SFrank Piva break;
735*94c4a1e1SFrank Piva case 'g':
736*94c4a1e1SFrank Piva need_get_data = strtol(optarg, NULL, 10);
737*94c4a1e1SFrank Piva break;
738*94c4a1e1SFrank Piva case 'r':
739*94c4a1e1SFrank Piva user_recovery = strtol(optarg, NULL, 10);
740*94c4a1e1SFrank Piva break;
741*94c4a1e1SFrank Piva case 'i':
742*94c4a1e1SFrank Piva user_recovery_reissue = strtol(optarg, NULL, 10);
743*94c4a1e1SFrank Piva break;
744*94c4a1e1SFrank Piva case 0:
745*94c4a1e1SFrank Piva if (!strcmp(longopts[option_index].name, "debug_mask"))
746*94c4a1e1SFrank Piva debug_mask = strtol(optarg, NULL, 16);
747*94c4a1e1SFrank Piva if (!strcmp(longopts[option_index].name, "unprivileged"))
748*94c4a1e1SFrank Piva unprivileged = 1;
749*94c4a1e1SFrank Piva if (!strcmp(longopts[option_index].name, "usercopy"))
750*94c4a1e1SFrank Piva data.flags |= UBLK_F_USER_COPY;
751*94c4a1e1SFrank Piva break;
752*94c4a1e1SFrank Piva }
753*94c4a1e1SFrank Piva }
754*94c4a1e1SFrank Piva
755*94c4a1e1SFrank Piva ublk_set_debug_mask(debug_mask);
756*94c4a1e1SFrank Piva
757*94c4a1e1SFrank Piva data.max_io_buf_bytes = DEF_BUF_SIZE;
758*94c4a1e1SFrank Piva if (data.nr_hw_queues > MAX_NR_HW_QUEUES)
759*94c4a1e1SFrank Piva data.nr_hw_queues = MAX_NR_HW_QUEUES;
760*94c4a1e1SFrank Piva if (data.queue_depth > MAX_QD)
761*94c4a1e1SFrank Piva data.queue_depth = MAX_QD;
762*94c4a1e1SFrank Piva if (uring_comp)
763*94c4a1e1SFrank Piva data.flags |= UBLK_F_URING_CMD_COMP_IN_TASK;
764*94c4a1e1SFrank Piva if (need_get_data)
765*94c4a1e1SFrank Piva data.flags |= UBLK_F_NEED_GET_DATA;
766*94c4a1e1SFrank Piva if (user_recovery)
767*94c4a1e1SFrank Piva data.flags |= UBLK_F_USER_RECOVERY;
768*94c4a1e1SFrank Piva if (user_recovery_reissue)
769*94c4a1e1SFrank Piva data.flags |= UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_REISSUE;
770*94c4a1e1SFrank Piva if (unprivileged)
771*94c4a1e1SFrank Piva data.flags |= UBLK_F_UNPRIVILEGED_DEV;
772*94c4a1e1SFrank Piva
773*94c4a1e1SFrank Piva if (data.tgt_type == NULL) {
774*94c4a1e1SFrank Piva fprintf(stderr, "no dev type specified\n");
775*94c4a1e1SFrank Piva return -EINVAL;
776*94c4a1e1SFrank Piva }
777*94c4a1e1SFrank Piva tgt_type = ublksrv_find_tgt_type(data.tgt_type);
778*94c4a1e1SFrank Piva if (tgt_type == NULL) {
779*94c4a1e1SFrank Piva fprintf(stderr, "unknown dev type: %s\n", data.tgt_type);
780*94c4a1e1SFrank Piva return -EINVAL;
781*94c4a1e1SFrank Piva }
782*94c4a1e1SFrank Piva data.tgt_ops = tgt_type;
783*94c4a1e1SFrank Piva data.flags |= tgt_type->ublk_flags;
784*94c4a1e1SFrank Piva data.ublksrv_flags |= tgt_type->ublksrv_flags;
785*94c4a1e1SFrank Piva
786*94c4a1e1SFrank Piva //optind = 0; /* so that tgt code can parse their arguments */
787*94c4a1e1SFrank Piva data.tgt_argc = argc;
788*94c4a1e1SFrank Piva data.tgt_argv = argv;
789*94c4a1e1SFrank Piva dev = ublksrv_ctrl_init(&data);
790*94c4a1e1SFrank Piva if (!dev) {
791*94c4a1e1SFrank Piva fprintf(stderr, "can't init dev %d\n", data.dev_id);
792*94c4a1e1SFrank Piva return -ENODEV;
793*94c4a1e1SFrank Piva }
794*94c4a1e1SFrank Piva
795*94c4a1e1SFrank Piva ret = ublksrv_ctrl_add_dev(dev);
796*94c4a1e1SFrank Piva if (ret < 0) {
797*94c4a1e1SFrank Piva fprintf(stderr, "can't add dev %d, ret %d\n", data.dev_id, ret);
798*94c4a1e1SFrank Piva goto fail;
799*94c4a1e1SFrank Piva }
800*94c4a1e1SFrank Piva
801*94c4a1e1SFrank Piva {
802*94c4a1e1SFrank Piva const struct ublksrv_ctrl_dev_info *info =
803*94c4a1e1SFrank Piva ublksrv_ctrl_get_dev_info(dev);
804*94c4a1e1SFrank Piva data.dev_id = info->dev_id;
805*94c4a1e1SFrank Piva }
806*94c4a1e1SFrank Piva ret = ublksrv_start_daemon(dev);
807*94c4a1e1SFrank Piva if (ret <= 0) {
808*94c4a1e1SFrank Piva fprintf(stderr, "start dev %d daemon failed, ret %d\n",
809*94c4a1e1SFrank Piva data.dev_id, ret);
810*94c4a1e1SFrank Piva goto fail_del_dev;
811*94c4a1e1SFrank Piva }
812*94c4a1e1SFrank Piva
813*94c4a1e1SFrank Piva dump_buf = ublksrv_tgt_get_dev_data(dev);
814*94c4a1e1SFrank Piva ublksrv_tgt_set_params(dev, dump_buf);
815*94c4a1e1SFrank Piva
816*94c4a1e1SFrank Piva ret = ublksrv_ctrl_start_dev(dev, ret);
817*94c4a1e1SFrank Piva if (ret < 0) {
818*94c4a1e1SFrank Piva fprintf(stderr, "start dev %d failed, ret %d\n", data.dev_id,
819*94c4a1e1SFrank Piva ret);
820*94c4a1e1SFrank Piva goto fail_stop_daemon;
821*94c4a1e1SFrank Piva }
822*94c4a1e1SFrank Piva ret = ublksrv_ctrl_get_info(dev);
823*94c4a1e1SFrank Piva ublksrv_ctrl_dump(dev, dump_buf);
824*94c4a1e1SFrank Piva ublksrv_ctrl_deinit(dev);
825*94c4a1e1SFrank Piva return 0;
826*94c4a1e1SFrank Piva
827*94c4a1e1SFrank Piva fail_stop_daemon:
828*94c4a1e1SFrank Piva ublksrv_stop_io_daemon(dev);
829*94c4a1e1SFrank Piva fail_del_dev:
830*94c4a1e1SFrank Piva ublksrv_ctrl_del_dev(dev);
831*94c4a1e1SFrank Piva fail:
832*94c4a1e1SFrank Piva ublksrv_ctrl_deinit(dev);
833*94c4a1e1SFrank Piva
834*94c4a1e1SFrank Piva return ret;
835*94c4a1e1SFrank Piva }
836*94c4a1e1SFrank Piva
837*94c4a1e1SFrank Piva struct tgt_types_name {
838*94c4a1e1SFrank Piva unsigned pos;
839*94c4a1e1SFrank Piva char names[4096 - sizeof(unsigned)];
840*94c4a1e1SFrank Piva };
841*94c4a1e1SFrank Piva
collect_tgt_types(unsigned int idx,const struct ublksrv_tgt_type * type,void * pdata)842*94c4a1e1SFrank Piva static void collect_tgt_types(unsigned int idx,
843*94c4a1e1SFrank Piva const struct ublksrv_tgt_type *type, void *pdata)
844*94c4a1e1SFrank Piva {
845*94c4a1e1SFrank Piva struct tgt_types_name *data = (struct tgt_types_name *)pdata;
846*94c4a1e1SFrank Piva
847*94c4a1e1SFrank Piva if (idx > 0)
848*94c4a1e1SFrank Piva data->pos += snprintf(data->names + data->pos,
849*94c4a1e1SFrank Piva sizeof(data->names) - data->pos, "|");
850*94c4a1e1SFrank Piva data->pos += snprintf(data->names + data->pos,
851*94c4a1e1SFrank Piva sizeof(data->names) - data->pos, "%s", type->name);
852*94c4a1e1SFrank Piva }
853*94c4a1e1SFrank Piva
show_tgt_add_usage(unsigned int idx,const struct ublksrv_tgt_type * type,void * data)854*94c4a1e1SFrank Piva static void show_tgt_add_usage(unsigned int idx,
855*94c4a1e1SFrank Piva const struct ublksrv_tgt_type *type, void *data)
856*94c4a1e1SFrank Piva {
857*94c4a1e1SFrank Piva if (type->usage_for_add)
858*94c4a1e1SFrank Piva type->usage_for_add();
859*94c4a1e1SFrank Piva }
860*94c4a1e1SFrank Piva
cmd_dev_add_usage(const char * cmd)861*94c4a1e1SFrank Piva static void cmd_dev_add_usage(const char *cmd)
862*94c4a1e1SFrank Piva {
863*94c4a1e1SFrank Piva struct tgt_types_name data = {
864*94c4a1e1SFrank Piva .pos = 0,
865*94c4a1e1SFrank Piva };
866*94c4a1e1SFrank Piva
867*94c4a1e1SFrank Piva data.pos += snprintf(data.names + data.pos, sizeof(data.names) - data.pos, "{");
868*94c4a1e1SFrank Piva ublksrv_for_each_tgt_type(collect_tgt_types, &data);
869*94c4a1e1SFrank Piva data.pos += snprintf(data.names + data.pos, sizeof(data.names) - data.pos, "}");
870*94c4a1e1SFrank Piva
871*94c4a1e1SFrank Piva printf("%s add -t %s\n", cmd, data.names);
872*94c4a1e1SFrank Piva printf("\t-n DEV_ID -q NR_HW_QUEUES -d QUEUE_DEPTH\n");
873*94c4a1e1SFrank Piva printf("\t-u URING_COMP -g NEED_GET_DATA -r USER_RECOVERY\n");
874*94c4a1e1SFrank Piva printf("\t-i USER_RECOVERY_REISSUE --debug_mask=0x{DBG_MASK}\n");
875*94c4a1e1SFrank Piva printf("\t--unprivileged\n\n");
876*94c4a1e1SFrank Piva printf("\ttarget specific command line:\n");
877*94c4a1e1SFrank Piva ublksrv_for_each_tgt_type(show_tgt_add_usage, NULL);
878*94c4a1e1SFrank Piva }
879*94c4a1e1SFrank Piva
__cmd_dev_del(int number,bool log,bool async)880*94c4a1e1SFrank Piva static int __cmd_dev_del(int number, bool log, bool async)
881*94c4a1e1SFrank Piva {
882*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev *dev;
883*94c4a1e1SFrank Piva int ret;
884*94c4a1e1SFrank Piva struct ublksrv_dev_data data = {
885*94c4a1e1SFrank Piva .dev_id = number,
886*94c4a1e1SFrank Piva .run_dir = UBLKSRV_PID_DIR,
887*94c4a1e1SFrank Piva };
888*94c4a1e1SFrank Piva
889*94c4a1e1SFrank Piva dev = ublksrv_ctrl_init(&data);
890*94c4a1e1SFrank Piva
891*94c4a1e1SFrank Piva ret = ublksrv_ctrl_get_info(dev);
892*94c4a1e1SFrank Piva if (ret < 0) {
893*94c4a1e1SFrank Piva ret = 0;
894*94c4a1e1SFrank Piva if (log)
895*94c4a1e1SFrank Piva fprintf(stderr, "can't get dev info from %d: %d\n", number, ret);
896*94c4a1e1SFrank Piva goto fail;
897*94c4a1e1SFrank Piva }
898*94c4a1e1SFrank Piva
899*94c4a1e1SFrank Piva ret = ublksrv_ctrl_stop_dev(dev);
900*94c4a1e1SFrank Piva if (ret < 0) {
901*94c4a1e1SFrank Piva fprintf(stderr, "stop dev %d failed\n", number);
902*94c4a1e1SFrank Piva goto fail;
903*94c4a1e1SFrank Piva }
904*94c4a1e1SFrank Piva
905*94c4a1e1SFrank Piva ret = ublksrv_stop_io_daemon(dev);
906*94c4a1e1SFrank Piva if (ret < 0)
907*94c4a1e1SFrank Piva fprintf(stderr, "stop daemon %d failed\n", number);
908*94c4a1e1SFrank Piva
909*94c4a1e1SFrank Piva if (async)
910*94c4a1e1SFrank Piva ret = ublksrv_ctrl_del_dev_async(dev);
911*94c4a1e1SFrank Piva else
912*94c4a1e1SFrank Piva ret = ublksrv_ctrl_del_dev(dev);
913*94c4a1e1SFrank Piva if (ret < 0) {
914*94c4a1e1SFrank Piva fprintf(stderr, "delete dev %d failed %d\n", number, ret);
915*94c4a1e1SFrank Piva goto fail;
916*94c4a1e1SFrank Piva }
917*94c4a1e1SFrank Piva
918*94c4a1e1SFrank Piva fail:
919*94c4a1e1SFrank Piva ublksrv_ctrl_deinit(dev);
920*94c4a1e1SFrank Piva return ret;
921*94c4a1e1SFrank Piva }
922*94c4a1e1SFrank Piva
cmd_dev_del(int argc,char * argv[])923*94c4a1e1SFrank Piva static int cmd_dev_del(int argc, char *argv[])
924*94c4a1e1SFrank Piva {
925*94c4a1e1SFrank Piva static const struct option longopts[] = {
926*94c4a1e1SFrank Piva { "number", 1, NULL, 'n' },
927*94c4a1e1SFrank Piva { "all", 0, NULL, 'a' },
928*94c4a1e1SFrank Piva { "async", 0, NULL, 0 },
929*94c4a1e1SFrank Piva { NULL }
930*94c4a1e1SFrank Piva };
931*94c4a1e1SFrank Piva int number = -1;
932*94c4a1e1SFrank Piva int opt, ret, i;
933*94c4a1e1SFrank Piva unsigned async = 0;
934*94c4a1e1SFrank Piva int option_index = 0;
935*94c4a1e1SFrank Piva
936*94c4a1e1SFrank Piva while ((opt = getopt_long(argc, argv, "n:a",
937*94c4a1e1SFrank Piva longopts, &option_index)) != -1) {
938*94c4a1e1SFrank Piva switch (opt) {
939*94c4a1e1SFrank Piva case 'a':
940*94c4a1e1SFrank Piva break;
941*94c4a1e1SFrank Piva
942*94c4a1e1SFrank Piva case 'n':
943*94c4a1e1SFrank Piva number = strtol(optarg, NULL, 10);
944*94c4a1e1SFrank Piva break;
945*94c4a1e1SFrank Piva case 0:
946*94c4a1e1SFrank Piva if (!strcmp(longopts[option_index].name, "async"))
947*94c4a1e1SFrank Piva async = 1;
948*94c4a1e1SFrank Piva }
949*94c4a1e1SFrank Piva }
950*94c4a1e1SFrank Piva
951*94c4a1e1SFrank Piva if (number >= 0)
952*94c4a1e1SFrank Piva return __cmd_dev_del(number, true, async);
953*94c4a1e1SFrank Piva
954*94c4a1e1SFrank Piva for (i = 0; i < MAX_NR_UBLK_DEVS; i++)
955*94c4a1e1SFrank Piva ret = __cmd_dev_del(i, false, async);
956*94c4a1e1SFrank Piva
957*94c4a1e1SFrank Piva return ret;
958*94c4a1e1SFrank Piva }
959*94c4a1e1SFrank Piva
cmd_dev_del_usage(const char * cmd)960*94c4a1e1SFrank Piva static void cmd_dev_del_usage(const char *cmd)
961*94c4a1e1SFrank Piva {
962*94c4a1e1SFrank Piva printf("%s del -n DEV_ID [-a | --all]\n", cmd);
963*94c4a1e1SFrank Piva }
964*94c4a1e1SFrank Piva
list_one_dev(int number,bool log,bool verbose)965*94c4a1e1SFrank Piva static int list_one_dev(int number, bool log, bool verbose)
966*94c4a1e1SFrank Piva {
967*94c4a1e1SFrank Piva struct ublksrv_dev_data data = {
968*94c4a1e1SFrank Piva .dev_id = number,
969*94c4a1e1SFrank Piva .run_dir = UBLKSRV_PID_DIR,
970*94c4a1e1SFrank Piva };
971*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev *dev = ublksrv_ctrl_init(&data);
972*94c4a1e1SFrank Piva int ret;
973*94c4a1e1SFrank Piva
974*94c4a1e1SFrank Piva ret = ublksrv_ctrl_get_info(dev);
975*94c4a1e1SFrank Piva if (ret < 0) {
976*94c4a1e1SFrank Piva if (log)
977*94c4a1e1SFrank Piva fprintf(stderr, "can't get dev info from %d: %d\n", number, ret);
978*94c4a1e1SFrank Piva } else {
979*94c4a1e1SFrank Piva const char *buf = ublksrv_tgt_get_dev_data(dev);
980*94c4a1e1SFrank Piva
981*94c4a1e1SFrank Piva if (verbose)
982*94c4a1e1SFrank Piva ublksrv_json_dump(buf);
983*94c4a1e1SFrank Piva else
984*94c4a1e1SFrank Piva ublksrv_ctrl_dump(dev, buf);
985*94c4a1e1SFrank Piva }
986*94c4a1e1SFrank Piva
987*94c4a1e1SFrank Piva ublksrv_ctrl_deinit(dev);
988*94c4a1e1SFrank Piva
989*94c4a1e1SFrank Piva return ret;
990*94c4a1e1SFrank Piva }
991*94c4a1e1SFrank Piva
cmd_list_dev_info(int argc,char * argv[])992*94c4a1e1SFrank Piva static int cmd_list_dev_info(int argc, char *argv[])
993*94c4a1e1SFrank Piva {
994*94c4a1e1SFrank Piva static const struct option longopts[] = {
995*94c4a1e1SFrank Piva { "number", 0, NULL, 'n' },
996*94c4a1e1SFrank Piva { "verbose", 0, NULL, 'v' },
997*94c4a1e1SFrank Piva { NULL }
998*94c4a1e1SFrank Piva };
999*94c4a1e1SFrank Piva int number = -1;
1000*94c4a1e1SFrank Piva int opt, i;
1001*94c4a1e1SFrank Piva bool verbose = false;
1002*94c4a1e1SFrank Piva
1003*94c4a1e1SFrank Piva while ((opt = getopt_long(argc, argv, "n:v",
1004*94c4a1e1SFrank Piva longopts, NULL)) != -1) {
1005*94c4a1e1SFrank Piva switch (opt) {
1006*94c4a1e1SFrank Piva case 'n':
1007*94c4a1e1SFrank Piva number = strtol(optarg, NULL, 10);
1008*94c4a1e1SFrank Piva break;
1009*94c4a1e1SFrank Piva case 'v':
1010*94c4a1e1SFrank Piva verbose = 1;
1011*94c4a1e1SFrank Piva break;
1012*94c4a1e1SFrank Piva }
1013*94c4a1e1SFrank Piva }
1014*94c4a1e1SFrank Piva
1015*94c4a1e1SFrank Piva if (number >= 0)
1016*94c4a1e1SFrank Piva return list_one_dev(number, true, verbose);
1017*94c4a1e1SFrank Piva
1018*94c4a1e1SFrank Piva for (i = 0; i < MAX_NR_UBLK_DEVS; i++)
1019*94c4a1e1SFrank Piva list_one_dev(i, false, verbose);
1020*94c4a1e1SFrank Piva
1021*94c4a1e1SFrank Piva return 0;
1022*94c4a1e1SFrank Piva }
1023*94c4a1e1SFrank Piva
cmd_dev_list_usage(const char * cmd)1024*94c4a1e1SFrank Piva static void cmd_dev_list_usage(const char *cmd)
1025*94c4a1e1SFrank Piva {
1026*94c4a1e1SFrank Piva printf("%s list [-n DEV_ID]\n", cmd);
1027*94c4a1e1SFrank Piva }
1028*94c4a1e1SFrank Piva
1029*94c4a1e1SFrank Piva #define const_ilog2(x) (63 - __builtin_clzll(x))
1030*94c4a1e1SFrank Piva
cmd_dev_get_features(int argc,char * argv[])1031*94c4a1e1SFrank Piva static int cmd_dev_get_features(int argc, char *argv[])
1032*94c4a1e1SFrank Piva {
1033*94c4a1e1SFrank Piva struct ublksrv_dev_data data = {
1034*94c4a1e1SFrank Piva .dev_id = -1,
1035*94c4a1e1SFrank Piva .run_dir = UBLKSRV_PID_DIR,
1036*94c4a1e1SFrank Piva };
1037*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev *dev = ublksrv_ctrl_init(&data);
1038*94c4a1e1SFrank Piva __u64 features = 0;
1039*94c4a1e1SFrank Piva int ret;
1040*94c4a1e1SFrank Piva static const char *feat_map[] = {
1041*94c4a1e1SFrank Piva [const_ilog2(UBLK_F_SUPPORT_ZERO_COPY)] = "ZERO_COPY",
1042*94c4a1e1SFrank Piva [const_ilog2(UBLK_F_URING_CMD_COMP_IN_TASK)] = "COMP_IN_TASK",
1043*94c4a1e1SFrank Piva [const_ilog2(UBLK_F_NEED_GET_DATA)] = "GET_DATA",
1044*94c4a1e1SFrank Piva [const_ilog2(UBLK_F_USER_RECOVERY)] = "USER_RECOVERY",
1045*94c4a1e1SFrank Piva [const_ilog2(UBLK_F_USER_RECOVERY_REISSUE)] = "RECOVERY_REISSUE",
1046*94c4a1e1SFrank Piva [const_ilog2(UBLK_F_UNPRIVILEGED_DEV)] = "UNPRIVILEGED_DEV",
1047*94c4a1e1SFrank Piva [const_ilog2(UBLK_F_CMD_IOCTL_ENCODE)] = "CMD_IOCTL_ENCODE",
1048*94c4a1e1SFrank Piva };
1049*94c4a1e1SFrank Piva
1050*94c4a1e1SFrank Piva ret = ublksrv_ctrl_get_features(dev, &features);
1051*94c4a1e1SFrank Piva if (!ret) {
1052*94c4a1e1SFrank Piva int i;
1053*94c4a1e1SFrank Piva
1054*94c4a1e1SFrank Piva printf("ublk_drv features: 0x%llx\n", features);
1055*94c4a1e1SFrank Piva
1056*94c4a1e1SFrank Piva for (i = 0; i < sizeof(features); i++) {
1057*94c4a1e1SFrank Piva const char *feat;
1058*94c4a1e1SFrank Piva
1059*94c4a1e1SFrank Piva if (!((1ULL << i) & features))
1060*94c4a1e1SFrank Piva continue;
1061*94c4a1e1SFrank Piva if (i < sizeof(feat_map) / sizeof(feat_map[0]))
1062*94c4a1e1SFrank Piva feat = feat_map[i];
1063*94c4a1e1SFrank Piva else
1064*94c4a1e1SFrank Piva feat = "unknown";
1065*94c4a1e1SFrank Piva printf("\t%-20s: 0x%llx\n", feat, 1ULL << i);
1066*94c4a1e1SFrank Piva }
1067*94c4a1e1SFrank Piva }
1068*94c4a1e1SFrank Piva
1069*94c4a1e1SFrank Piva return ret;
1070*94c4a1e1SFrank Piva }
1071*94c4a1e1SFrank Piva
cmd_dev_get_features_help(const char * cmd)1072*94c4a1e1SFrank Piva static void cmd_dev_get_features_help(const char *cmd)
1073*94c4a1e1SFrank Piva {
1074*94c4a1e1SFrank Piva printf("%s features\n", cmd);
1075*94c4a1e1SFrank Piva }
1076*94c4a1e1SFrank Piva
__cmd_dev_user_recover(int number,bool verbose)1077*94c4a1e1SFrank Piva static int __cmd_dev_user_recover(int number, bool verbose)
1078*94c4a1e1SFrank Piva {
1079*94c4a1e1SFrank Piva const struct ublksrv_tgt_type *tgt_type;
1080*94c4a1e1SFrank Piva struct ublksrv_dev_data data = {
1081*94c4a1e1SFrank Piva .dev_id = number,
1082*94c4a1e1SFrank Piva .run_dir = UBLKSRV_PID_DIR,
1083*94c4a1e1SFrank Piva };
1084*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev_info dev_info;
1085*94c4a1e1SFrank Piva struct ublksrv_ctrl_dev *dev;
1086*94c4a1e1SFrank Piva struct ublksrv_tgt_base_json tgt_json = {0};
1087*94c4a1e1SFrank Piva char *buf = NULL;
1088*94c4a1e1SFrank Piva char pid_file[64];
1089*94c4a1e1SFrank Piva int ret;
1090*94c4a1e1SFrank Piva unsigned elapsed = 0;
1091*94c4a1e1SFrank Piva
1092*94c4a1e1SFrank Piva dev = ublksrv_ctrl_init(&data);
1093*94c4a1e1SFrank Piva if (!dev) {
1094*94c4a1e1SFrank Piva fprintf(stderr, "ublksrv_ctrl_init failure dev %d\n", number);
1095*94c4a1e1SFrank Piva return -ENOMEM;
1096*94c4a1e1SFrank Piva }
1097*94c4a1e1SFrank Piva
1098*94c4a1e1SFrank Piva ret = ublksrv_ctrl_get_info(dev);
1099*94c4a1e1SFrank Piva if (ret < 0) {
1100*94c4a1e1SFrank Piva fprintf(stderr, "can't get dev info from %d\n", number);
1101*94c4a1e1SFrank Piva goto fail;
1102*94c4a1e1SFrank Piva }
1103*94c4a1e1SFrank Piva
1104*94c4a1e1SFrank Piva while (elapsed < 30000000) {
1105*94c4a1e1SFrank Piva unsigned unit = 100000;
1106*94c4a1e1SFrank Piva ret = ublksrv_ctrl_start_recovery(dev);
1107*94c4a1e1SFrank Piva if (ret < 0 && ret != -EBUSY) {
1108*94c4a1e1SFrank Piva fprintf(stderr, "can't start recovery for %d ret %d\n",
1109*94c4a1e1SFrank Piva number, ret);
1110*94c4a1e1SFrank Piva goto fail;
1111*94c4a1e1SFrank Piva }
1112*94c4a1e1SFrank Piva if (ret >= 0)
1113*94c4a1e1SFrank Piva break;
1114*94c4a1e1SFrank Piva usleep(unit);
1115*94c4a1e1SFrank Piva elapsed += unit;
1116*94c4a1e1SFrank Piva }
1117*94c4a1e1SFrank Piva
1118*94c4a1e1SFrank Piva buf = ublksrv_tgt_get_dev_data(dev);
1119*94c4a1e1SFrank Piva if (!buf) {
1120*94c4a1e1SFrank Piva fprintf(stderr, "get dev %d data failed\n", number);
1121*94c4a1e1SFrank Piva ret = -1;
1122*94c4a1e1SFrank Piva goto fail;
1123*94c4a1e1SFrank Piva }
1124*94c4a1e1SFrank Piva
1125*94c4a1e1SFrank Piva ret = ublksrv_json_read_dev_info(buf, &dev_info);
1126*94c4a1e1SFrank Piva if (ret < 0) {
1127*94c4a1e1SFrank Piva fprintf(stderr, "can't read dev info for %d\n", number);
1128*94c4a1e1SFrank Piva goto fail;
1129*94c4a1e1SFrank Piva }
1130*94c4a1e1SFrank Piva
1131*94c4a1e1SFrank Piva if (dev_info.dev_id != number) {
1132*94c4a1e1SFrank Piva fprintf(stderr, "dev id doesn't match read %d for dev %d\n",
1133*94c4a1e1SFrank Piva dev_info.dev_id, number);
1134*94c4a1e1SFrank Piva goto fail;
1135*94c4a1e1SFrank Piva }
1136*94c4a1e1SFrank Piva
1137*94c4a1e1SFrank Piva ret = ublksrv_json_read_target_base_info(buf, &tgt_json);
1138*94c4a1e1SFrank Piva if (ret < 0) {
1139*94c4a1e1SFrank Piva fprintf(stderr, "can't read dev info for %d\n", number);
1140*94c4a1e1SFrank Piva goto fail;
1141*94c4a1e1SFrank Piva }
1142*94c4a1e1SFrank Piva
1143*94c4a1e1SFrank Piva snprintf(pid_file, 64, "%s/%d.pid", data.run_dir, number);
1144*94c4a1e1SFrank Piva ret = unlink(pid_file);
1145*94c4a1e1SFrank Piva if (ret < 0) {
1146*94c4a1e1SFrank Piva fprintf(stderr, "can't delete old pid_file for %d, error:%s\n",
1147*94c4a1e1SFrank Piva number, strerror(errno));
1148*94c4a1e1SFrank Piva goto fail;
1149*94c4a1e1SFrank Piva }
1150*94c4a1e1SFrank Piva
1151*94c4a1e1SFrank Piva tgt_type = ublksrv_find_tgt_type(tgt_json.name);
1152*94c4a1e1SFrank Piva if (!tgt_type) {
1153*94c4a1e1SFrank Piva fprintf(stderr, "can't find target type %s\n", tgt_json.name);
1154*94c4a1e1SFrank Piva goto fail;
1155*94c4a1e1SFrank Piva }
1156*94c4a1e1SFrank Piva
1157*94c4a1e1SFrank Piva ublksrv_ctrl_prep_recovery(dev, tgt_json.name, tgt_type, buf);
1158*94c4a1e1SFrank Piva
1159*94c4a1e1SFrank Piva ret = ublksrv_start_daemon(dev);
1160*94c4a1e1SFrank Piva if (ret < 0) {
1161*94c4a1e1SFrank Piva fprintf(stderr, "start daemon %d failed\n", number);
1162*94c4a1e1SFrank Piva goto fail;
1163*94c4a1e1SFrank Piva }
1164*94c4a1e1SFrank Piva
1165*94c4a1e1SFrank Piva ret = ublksrv_ctrl_end_recovery(dev, ret);
1166*94c4a1e1SFrank Piva if (ret < 0) {
1167*94c4a1e1SFrank Piva fprintf(stderr, "end recovery for %d failed\n", number);
1168*94c4a1e1SFrank Piva goto fail;
1169*94c4a1e1SFrank Piva }
1170*94c4a1e1SFrank Piva
1171*94c4a1e1SFrank Piva ret = ublksrv_ctrl_get_info(dev);
1172*94c4a1e1SFrank Piva if (ret < 0) {
1173*94c4a1e1SFrank Piva fprintf(stderr, "can't get dev info from %d\n", number);
1174*94c4a1e1SFrank Piva goto fail;
1175*94c4a1e1SFrank Piva }
1176*94c4a1e1SFrank Piva
1177*94c4a1e1SFrank Piva if (verbose) {
1178*94c4a1e1SFrank Piva free(buf);
1179*94c4a1e1SFrank Piva buf = ublksrv_tgt_get_dev_data(dev);
1180*94c4a1e1SFrank Piva ublksrv_ctrl_dump(dev, buf);
1181*94c4a1e1SFrank Piva }
1182*94c4a1e1SFrank Piva
1183*94c4a1e1SFrank Piva fail:
1184*94c4a1e1SFrank Piva free(buf);
1185*94c4a1e1SFrank Piva ublksrv_ctrl_deinit(dev);
1186*94c4a1e1SFrank Piva return ret;
1187*94c4a1e1SFrank Piva }
1188*94c4a1e1SFrank Piva
cmd_dev_user_recover(int argc,char * argv[])1189*94c4a1e1SFrank Piva static int cmd_dev_user_recover(int argc, char *argv[])
1190*94c4a1e1SFrank Piva {
1191*94c4a1e1SFrank Piva static const struct option longopts[] = {
1192*94c4a1e1SFrank Piva { "number", 0, NULL, 'n' },
1193*94c4a1e1SFrank Piva { "verbose", 0, NULL, 'v' },
1194*94c4a1e1SFrank Piva { NULL }
1195*94c4a1e1SFrank Piva };
1196*94c4a1e1SFrank Piva int number = -1;
1197*94c4a1e1SFrank Piva int opt;
1198*94c4a1e1SFrank Piva bool verbose = false;
1199*94c4a1e1SFrank Piva
1200*94c4a1e1SFrank Piva while ((opt = getopt_long(argc, argv, "n:v",
1201*94c4a1e1SFrank Piva longopts, NULL)) != -1) {
1202*94c4a1e1SFrank Piva switch (opt) {
1203*94c4a1e1SFrank Piva case 'n':
1204*94c4a1e1SFrank Piva number = strtol(optarg, NULL, 10);
1205*94c4a1e1SFrank Piva break;
1206*94c4a1e1SFrank Piva case 'v':
1207*94c4a1e1SFrank Piva verbose = true;
1208*94c4a1e1SFrank Piva break;
1209*94c4a1e1SFrank Piva }
1210*94c4a1e1SFrank Piva }
1211*94c4a1e1SFrank Piva
1212*94c4a1e1SFrank Piva return __cmd_dev_user_recover(number, verbose);
1213*94c4a1e1SFrank Piva }
1214*94c4a1e1SFrank Piva
cmd_dev_recover_usage(const char * cmd)1215*94c4a1e1SFrank Piva static void cmd_dev_recover_usage(const char *cmd)
1216*94c4a1e1SFrank Piva {
1217*94c4a1e1SFrank Piva printf("%s recover [-n DEV_ID]\n", cmd);
1218*94c4a1e1SFrank Piva }
1219*94c4a1e1SFrank Piva
cmd_usage(const char * cmd)1220*94c4a1e1SFrank Piva static void cmd_usage(const char *cmd)
1221*94c4a1e1SFrank Piva {
1222*94c4a1e1SFrank Piva cmd_dev_add_usage(cmd);
1223*94c4a1e1SFrank Piva cmd_dev_del_usage(cmd);
1224*94c4a1e1SFrank Piva cmd_dev_list_usage(cmd);
1225*94c4a1e1SFrank Piva cmd_dev_recover_usage(cmd);
1226*94c4a1e1SFrank Piva cmd_dev_get_features_help(cmd);
1227*94c4a1e1SFrank Piva
1228*94c4a1e1SFrank Piva printf("%s -v [--version]\n", cmd);
1229*94c4a1e1SFrank Piva printf("%s -h [--help]\n", cmd);
1230*94c4a1e1SFrank Piva }
1231*94c4a1e1SFrank Piva
main(int argc,char * argv[])1232*94c4a1e1SFrank Piva int main(int argc, char *argv[])
1233*94c4a1e1SFrank Piva {
1234*94c4a1e1SFrank Piva const char *prog_name = "ublk";
1235*94c4a1e1SFrank Piva
1236*94c4a1e1SFrank Piva char *cmd;
1237*94c4a1e1SFrank Piva int ret;
1238*94c4a1e1SFrank Piva char exe[PATH_MAX];
1239*94c4a1e1SFrank Piva
1240*94c4a1e1SFrank Piva full_cmd = argv[0];
1241*94c4a1e1SFrank Piva strncpy(exe, full_cmd, PATH_MAX - 1);
1242*94c4a1e1SFrank Piva
1243*94c4a1e1SFrank Piva setvbuf(stdout, NULL, _IOLBF, 0);
1244*94c4a1e1SFrank Piva
1245*94c4a1e1SFrank Piva cmd = pop_cmd(&argc, argv);
1246*94c4a1e1SFrank Piva if (cmd == NULL) {
1247*94c4a1e1SFrank Piva printf("%s: missing command\n", argv[0]);
1248*94c4a1e1SFrank Piva cmd_usage(prog_name);
1249*94c4a1e1SFrank Piva return EXIT_FAILURE;
1250*94c4a1e1SFrank Piva }
1251*94c4a1e1SFrank Piva
1252*94c4a1e1SFrank Piva if (!strcmp(cmd, "add"))
1253*94c4a1e1SFrank Piva ret = cmd_dev_add(argc, argv);
1254*94c4a1e1SFrank Piva else if (!strcmp(cmd, "del"))
1255*94c4a1e1SFrank Piva ret = cmd_dev_del(argc, argv);
1256*94c4a1e1SFrank Piva else if (!strcmp(cmd, "list"))
1257*94c4a1e1SFrank Piva ret = cmd_list_dev_info(argc, argv);
1258*94c4a1e1SFrank Piva else if (!strcmp(cmd, "recover"))
1259*94c4a1e1SFrank Piva ret = cmd_dev_user_recover(argc, argv);
1260*94c4a1e1SFrank Piva else if (!strcmp(cmd, "features"))
1261*94c4a1e1SFrank Piva ret = cmd_dev_get_features(argc, argv);
1262*94c4a1e1SFrank Piva else if (!strcmp(cmd, "help") || !strcmp(cmd, "-h") || !strcmp(cmd, "--help")) {
1263*94c4a1e1SFrank Piva cmd_usage(prog_name);
1264*94c4a1e1SFrank Piva ret = EXIT_SUCCESS;
1265*94c4a1e1SFrank Piva } else if (!strcmp(cmd, "-v") || !strcmp(cmd, "--version")) {
1266*94c4a1e1SFrank Piva fprintf(stdout, "%s\n", PACKAGE_STRING);
1267*94c4a1e1SFrank Piva ret = EXIT_SUCCESS;
1268*94c4a1e1SFrank Piva } else {
1269*94c4a1e1SFrank Piva fprintf(stderr, "unknown command: %s\n", cmd);
1270*94c4a1e1SFrank Piva cmd_usage(prog_name);
1271*94c4a1e1SFrank Piva ret = EXIT_FAILURE;
1272*94c4a1e1SFrank Piva }
1273*94c4a1e1SFrank Piva
1274*94c4a1e1SFrank Piva ublk_ctrl_dbg(UBLK_DBG_CTRL_CMD, "cmd %s: result %d\n", cmd, ret);
1275*94c4a1e1SFrank Piva
1276*94c4a1e1SFrank Piva return ret;
1277*94c4a1e1SFrank Piva }
1278