1 /* This file contains the device independent block driver interface.
3 * Block drivers support the following requests. Message format m10 is used.
4 * Field names are prefixed with BDEV_. Separate field names are used for the
5 * "access", "request", and "user" fields.
7 * m_type MINOR COUNT GRANT FLAGS ID REQUEST POS
8 * +--------------+--------+----------+-------+-------+------+---------+------+
9 * | BDEV_OPEN | minor | access | | | id | | |
10 * |--------------+--------+----------+-------+-------+------+---------+------|
11 * | BDEV_CLOSE | minor | | | | id | | |
12 * |--------------+--------+----------+-------+-------+------+---------+------|
13 * | BDEV_READ | minor | bytes | grant | flags | id | | pos. |
14 * |--------------+--------+----------+-------+-------+------+---------+------|
15 * | BDEV_WRITE | minor | bytes | grant | flags | id | | pos. |
16 * |--------------+--------+----------+-------+-------+------+---------+------|
17 * | BDEV_GATHER | minor | elements | grant | flags | id | | pos. |
18 * |--------------+--------+----------+-------+-------+------+---------+------|
19 * | BDEV_SCATTER | minor | elements | grant | flags | id | | pos. |
20 * |--------------+--------+----------+-------+-------+------+---------+------|
21 * | BDEV_IOCTL | minor | | grant | user | id | request | |
22 * ----------------------------------------------------------------------------
24 * The following reply message is used for all requests.
27 * +--------------+--------+----------+-------+-------+------+---------+------+
28 * | BDEV_REPLY | status | | | | id | | |
29 * ----------------------------------------------------------------------------
32 * Oct 16, 2011 split character and block protocol (D.C. van Moolenbroek)
33 * Aug 27, 2011 move common functions into driver.c (A. Welzel)
34 * Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder)
35 * Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder)
36 * Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder)
37 * Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot)
40 #include <minix/drivers.h>
41 #include <minix/blockdriver.h>
43 #include <sys/ioc_block.h>
44 #include <sys/ioc_disk.h>
50 /* Management data for opened devices. */
51 static int open_devs
[MAX_NR_OPEN_DEVICES
];
52 static int next_open_devs_slot
= 0;
54 /*===========================================================================*
56 *===========================================================================*/
57 static void clear_open_devs(void)
59 /* Reset the set of previously opened minor devices. */
60 next_open_devs_slot
= 0;
63 /*===========================================================================*
65 *===========================================================================*/
66 static int is_open_dev(int device
)
68 /* Check whether the given minor device has previously been opened. */
71 for (i
= 0; i
< next_open_devs_slot
; i
++)
72 if (open_devs
[i
] == device
)
78 /*===========================================================================*
80 *===========================================================================*/
81 static void set_open_dev(int device
)
83 /* Mark the given minor device as having been opened. */
85 if (next_open_devs_slot
>= MAX_NR_OPEN_DEVICES
)
86 panic("out of slots for open devices");
88 open_devs
[next_open_devs_slot
] = device
;
89 next_open_devs_slot
++;
92 /*===========================================================================*
93 * blockdriver_announce *
94 *===========================================================================*/
95 void blockdriver_announce(int type
)
97 /* Announce we are up after a fresh start or a restart. */
99 char key
[DS_MAX_KEYLEN
];
100 char label
[DS_MAX_KEYLEN
];
101 const char *driver_prefix
= "drv.blk.";
103 /* Callers are allowed to use ipc_sendrec to communicate with drivers.
104 * For this reason, there may blocked callers when a driver restarts.
105 * Ask the kernel to unblock them (if any). Note that most block drivers
106 * will not restart statefully, and thus will skip this code.
108 if (type
== SEF_INIT_RESTART
) {
109 if ((r
= sys_statectl(SYS_STATE_CLEAR_IPC_REFS
, 0, 0)) != OK
)
110 panic("blockdriver_init: sys_statectl failed: %d", r
);
113 /* Publish a driver up event. */
114 if ((r
= ds_retrieve_label_name(label
, sef_self())) != OK
)
115 panic("blockdriver_init: unable to get own label: %d", r
);
117 snprintf(key
, DS_MAX_KEYLEN
, "%s%s", driver_prefix
, label
);
118 if ((r
= ds_publish_u32(key
, DS_DRIVER_UP
, DSF_OVERWRITE
)) != OK
)
119 panic("blockdriver_init: unable to publish driver up event: %d", r
);
121 /* Expect an open for any device before serving regular driver requests. */
124 /* Initialize or reset the message queue. */
128 /*===========================================================================*
130 *===========================================================================*/
131 static void send_reply(endpoint_t endpt
, message
*m_ptr
, int ipc_status
)
133 /* Send a reply message to a request. */
136 /* If we would block sending the message, send it asynchronously. The NOREPLY
137 * flag is set because the caller may also issue a SENDREC (mixing sync and
138 * async comm), and the asynchronous reply could otherwise end up satisfying
139 * the SENDREC's receive part, after which our next SENDNB call would fail.
141 if (IPC_STATUS_CALL(ipc_status
) == SENDREC
)
142 r
= ipc_sendnb(endpt
, m_ptr
);
144 r
= asynsend3(endpt
, m_ptr
, AMF_NOREPLY
);
147 printf("blockdriver: unable to send reply to %d: %d\n", endpt
, r
);
150 /*===========================================================================*
151 * blockdriver_reply *
152 *===========================================================================*/
153 void blockdriver_reply(message
*m_ptr
, int ipc_status
, int reply
)
155 /* Reply to a block request sent to the driver. */
158 if (reply
== EDONTREPLY
)
161 memset(&m_reply
, 0, sizeof(m_reply
));
163 m_reply
.m_type
= BDEV_REPLY
;
164 m_reply
.m_lblockdriver_lbdev_reply
.status
= reply
;
165 m_reply
.m_lblockdriver_lbdev_reply
.id
= m_ptr
->m_lbdev_lblockdriver_msg
.id
;
167 send_reply(m_ptr
->m_source
, &m_reply
, ipc_status
);
170 /*===========================================================================*
172 *===========================================================================*/
173 static int do_open(struct blockdriver
*bdp
, message
*mp
)
175 /* Open a minor device. */
177 return (*bdp
->bdr_open
)(mp
->m_lbdev_lblockdriver_msg
.minor
, mp
->m_lbdev_lblockdriver_msg
.access
);
180 /*===========================================================================*
182 *===========================================================================*/
183 static int do_close(struct blockdriver
*bdp
, message
*mp
)
185 /* Close a minor device. */
187 return (*bdp
->bdr_close
)(mp
->m_lbdev_lblockdriver_msg
.minor
);
190 /*===========================================================================*
192 *===========================================================================*/
193 static int do_rdwt(struct blockdriver
*bdp
, message
*mp
)
195 /* Carry out a single read or write request. */
201 /* Disk address? Address and length of the user buffer? */
202 if (mp
->m_lbdev_lblockdriver_msg
.count
< 0) return EINVAL
;
204 /* Create a one element scatter/gather vector for the buffer. */
205 iovec1
.iov_addr
= mp
->m_lbdev_lblockdriver_msg
.grant
;
206 iovec1
.iov_size
= mp
->m_lbdev_lblockdriver_msg
.count
;
208 /* Transfer bytes from/to the device. */
209 do_write
= (mp
->m_type
== BDEV_WRITE
);
210 position
= mp
->m_lbdev_lblockdriver_msg
.pos
;
212 r
= (*bdp
->bdr_transfer
)(mp
->m_lbdev_lblockdriver_msg
.minor
, do_write
, position
, mp
->m_source
,
213 &iovec1
, 1, mp
->m_lbdev_lblockdriver_msg
.flags
);
215 /* Return the number of bytes transferred or an error code. */
219 /*===========================================================================*
221 *===========================================================================*/
222 static int do_vrdwt(struct blockdriver
*bdp
, message
*mp
, thread_id_t id
)
224 /* Carry out an device read or write to/from a vector of buffers. */
225 iovec_t iovec
[NR_IOREQS
];
226 unsigned int i
, nr_req
;
231 /* Copy the vector from the caller to kernel space. */
232 nr_req
= mp
->m_lbdev_lblockdriver_msg
.count
; /* Length of I/O vector */
233 if (nr_req
> NR_IOREQS
) nr_req
= NR_IOREQS
;
235 if (OK
!= sys_safecopyfrom(mp
->m_source
, (vir_bytes
) mp
->m_lbdev_lblockdriver_msg
.grant
,
236 0, (vir_bytes
) iovec
, nr_req
* sizeof(iovec
[0]))) {
237 printf("blockdriver: bad I/O vector by: %d\n", mp
->m_source
);
241 /* Check for overflow condition, and update the size for block tracing. */
242 for (i
= size
= 0; i
< nr_req
; i
++) {
243 if ((ssize_t
) (size
+ iovec
[i
].iov_size
) < size
) return EINVAL
;
244 size
+= iovec
[i
].iov_size
;
247 trace_setsize(id
, size
);
249 /* Transfer bytes from/to the device. */
250 do_write
= (mp
->m_type
== BDEV_SCATTER
);
251 position
= mp
->m_lbdev_lblockdriver_msg
.pos
;
253 r
= (*bdp
->bdr_transfer
)(mp
->m_lbdev_lblockdriver_msg
.minor
, do_write
, position
, mp
->m_source
,
254 iovec
, nr_req
, mp
->m_lbdev_lblockdriver_msg
.flags
);
256 /* Return the number of bytes transferred or an error code. */
260 /*===========================================================================*
262 *===========================================================================*/
263 static int do_dioctl(struct blockdriver
*bdp
, devminor_t minor
,
264 unsigned long request
, endpoint_t endpt
, cp_grant_id_t grant
)
266 /* Carry out a disk-specific I/O control request. */
268 struct part_geom entry
;
273 /* Copy just this one partition table entry. */
274 r
= sys_safecopyfrom(endpt
, grant
, 0, (vir_bytes
) &entry
,
279 if ((dv
= (*bdp
->bdr_part
)(minor
)) == NULL
)
281 dv
->dv_base
= entry
.base
;
282 dv
->dv_size
= entry
.size
;
287 /* Return a partition table entry and the geometry of the drive. */
288 if ((dv
= (*bdp
->bdr_part
)(minor
)) == NULL
)
290 entry
.base
= dv
->dv_base
;
291 entry
.size
= dv
->dv_size
;
292 if (bdp
->bdr_geometry
) {
293 (*bdp
->bdr_geometry
)(minor
, &entry
);
295 /* The driver doesn't care -- make up fake geometry. */
296 entry
.cylinders
= (unsigned long)(entry
.size
/ SECTOR_SIZE
) /
302 r
= sys_safecopyto(endpt
, grant
, 0, (vir_bytes
) &entry
, sizeof(entry
));
310 /*===========================================================================*
312 *===========================================================================*/
313 static int do_ioctl(struct blockdriver
*bdp
, message
*mp
)
315 /* Carry out an I/O control request. We forward block trace control requests
316 * to the tracing module, and handle setting/getting partitions when the driver
317 * has specified that it is a disk driver.
320 unsigned long request
;
322 endpoint_t user_endpt
;
325 minor
= mp
->m_lbdev_lblockdriver_msg
.minor
;
326 request
= mp
->m_lbdev_lblockdriver_msg
.request
;
327 grant
= mp
->m_lbdev_lblockdriver_msg
.grant
;
328 user_endpt
= mp
->m_lbdev_lblockdriver_msg
.user
;
334 /* Block trace control. */
335 r
= trace_ctl(minor
, request
, mp
->m_source
, grant
);
341 /* Handle disk-specific IOCTLs only for disk-type drivers. */
342 if (bdp
->bdr_type
== BLOCKDRIVER_TYPE_DISK
) {
343 /* Disk partition control. */
344 r
= do_dioctl(bdp
, minor
, request
, mp
->m_source
, grant
);
352 r
= (*bdp
->bdr_ioctl
)(minor
, request
, mp
->m_source
, grant
,
361 /*===========================================================================*
363 *===========================================================================*/
364 static void do_char_open(message
*m_ptr
, int ipc_status
)
366 /* Reply to a character driver open request stating there is no such device. */
369 memset(&m_reply
, 0, sizeof(m_reply
));
371 m_reply
.m_type
= CDEV_REPLY
;
372 m_reply
.m_lchardriver_vfs_reply
.status
= ENXIO
;
373 m_reply
.m_lchardriver_vfs_reply
.id
= m_ptr
->m_vfs_lchardriver_openclose
.id
;
375 send_reply(m_ptr
->m_source
, &m_reply
, ipc_status
);
378 /*===========================================================================*
379 * blockdriver_process_on_thread *
380 *===========================================================================*/
381 void blockdriver_process_on_thread(struct blockdriver
*bdp
, message
*m_ptr
,
382 int ipc_status
, thread_id_t id
)
384 /* Call the appropiate driver function, based on the type of request. Send
385 * a result code to the caller. The call is processed in the context of the
386 * given thread ID, which may be SINGLE_THREAD for single-threaded callers.
390 /* Check for notifications first. We never reply to notifications. */
391 if (is_ipc_notify(ipc_status
)) {
392 switch (_ENDPOINT_P(m_ptr
->m_source
)) {
395 (*bdp
->bdr_intr
)(m_ptr
->m_notify
.interrupts
);
400 (*bdp
->bdr_alarm
)(m_ptr
->m_notify
.timestamp
);
405 (*bdp
->bdr_other
)(m_ptr
, ipc_status
);
408 return; /* do not send a reply */
411 /* Reply to character driver open requests with an error code. Otherwise, if
412 * someone creates a character device node for a block driver, opening that
413 * device node will cause the corresponding VFS thread to block forever.
415 if (m_ptr
->m_type
== CDEV_OPEN
) {
416 do_char_open(m_ptr
, ipc_status
);
421 /* We might get spurious requests if the driver has been restarted. Deny any
422 * requests on devices that have not previously been opened, signaling the
423 * caller that something went wrong.
425 if (IS_BDEV_RQ(m_ptr
->m_type
) && !is_open_dev(m_ptr
->m_lbdev_lblockdriver_msg
.minor
)) {
426 /* Reply ERESTART to spurious requests for unopened devices. */
427 if (m_ptr
->m_type
!= BDEV_OPEN
) {
428 blockdriver_reply(m_ptr
, ipc_status
, ERESTART
);
433 /* Mark the device as opened otherwise. */
434 set_open_dev(m_ptr
->m_lbdev_lblockdriver_msg
.minor
);
437 trace_start(id
, m_ptr
);
439 /* Call the appropriate function(s) for this request. */
440 switch (m_ptr
->m_type
) {
441 case BDEV_OPEN
: r
= do_open(bdp
, m_ptr
); break;
442 case BDEV_CLOSE
: r
= do_close(bdp
, m_ptr
); break;
444 case BDEV_WRITE
: r
= do_rdwt(bdp
, m_ptr
); break;
446 case BDEV_SCATTER
: r
= do_vrdwt(bdp
, m_ptr
, id
); break;
447 case BDEV_IOCTL
: r
= do_ioctl(bdp
, m_ptr
); break;
449 if (bdp
->bdr_other
!= NULL
)
450 (*bdp
->bdr_other
)(m_ptr
, ipc_status
);
452 return; /* do not send a reply */
455 /* Let the driver perform any cleanup. */
456 if (bdp
->bdr_cleanup
!= NULL
)
457 (*bdp
->bdr_cleanup
)();
461 blockdriver_reply(m_ptr
, ipc_status
, r
);