Add 'devctl delete' that calls device_delete_child().

'devctl delete' can be used to delete a device that is no longer present.
As an anti-foot-shooting measure, 'delete' will not delete a device
unless it's parent bus says it is no longer present.  This can be
overridden by passing the force ('-f') flag.

Note that this command should be used with care.  If a device is deleted
that is actually present it can't be resurrected unless the parent bus
device's driver supports rescans.

Differential Revision:	https://reviews.freebsd.org/D6019
This commit is contained in:
John Baldwin 2016-04-27 16:33:17 +00:00
parent 3d0338a092
commit 88eb5c506d
7 changed files with 113 additions and 4 deletions

View file

@ -31,6 +31,7 @@
.Sh NAME
.Nm devctl ,
.Nm devctl_attach ,
.Nm devctl_delete ,
.Nm devctl_detach ,
.Nm devctl_disable ,
.Nm devctl_enable ,
@ -46,6 +47,8 @@
.Ft int
.Fn devctl_attach "const char *device"
.Ft int
.Fn devctl_delete "const char *device" "bool force"
.Ft int
.Fn devctl_detach "const char *device" "bool force"
.Ft int
.Fn devctl_disable "const char *device" "bool force_detach"
@ -110,6 +113,15 @@ is true,
the current device driver will be detached even if the device is busy.
.Pp
The
.Fn devctl_delete
function deletes a device from the device tree.
No
If
.Fa force
is true,
the device is deleted even if the device is physically present.
.Pp
The
.Fn devctl_disable
function disables a device.
If the device is currently attached to a device driver,
@ -158,8 +170,8 @@ The
function rescans a bus device checking for devices that have been added or
removed.
.Sh RETURN VALUES
.Rv -std devctl_attach devctl_detach devctl_disable devctl_enable \
devctl_suspend devctl_rescan devctl_resume devctl_set_driver
.Rv -std devctl_attach devctl_delete devctl_detach devctl_disable \
devctl_enable devctl_suspend devctl_rescan devctl_resume devctl_set_driver
.Sh ERRORS
In addition to specific errors noted below,
all of the
@ -298,6 +310,19 @@ The device is not attached to a driver.
.It Bq Er ENXIO
The bus driver does not support rescanning.
.El
.Pp
The
.Fn devctl_delete
function may fail if:
.Bl -tag -width Er
.It Bq Er EBUSY
The device is physically present and
.Fa force
is false.
.It Bq Er EINVAL
.Fa dev
is the root device of the device tree.
.El
.Sh SEE ALSO
.Xr devinfo 3 ,
.Xr devstat 3 ,

View file

@ -129,3 +129,11 @@ devctl_rescan(const char *device)
return (devctl_simple_request(DEV_RESCAN, device, 0));
}
int
devctl_delete(const char *device, bool force)
{
return (devctl_simple_request(DEV_DELETE, device, force ?
DEVF_FORCE_DELETE : 0));
}

View file

@ -39,5 +39,6 @@ int devctl_suspend(const char *device);
int devctl_resume(const char *device);
int devctl_set_driver(const char *device, const char *driver, bool force);
int devctl_rescan(const char *device);
int devctl_delete(const char *device, bool force);
#endif /* !__DEVCTL_H__ */

View file

@ -5204,6 +5204,7 @@ devctl2_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
case DEV_RESUME:
case DEV_SET_DRIVER:
case DEV_RESCAN:
case DEV_DELETE:
error = priv_check(td, PRIV_DRIVER);
if (error == 0)
error = find_device(req, &dev);
@ -5374,6 +5375,24 @@ devctl2_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
}
error = BUS_RESCAN(dev);
break;
case DEV_DELETE: {
device_t parent;
parent = device_get_parent(dev);
if (parent == NULL) {
error = EINVAL;
break;
}
if (!(req->dr_flags & DEVF_FORCE_DELETE)) {
if (bus_child_present(dev) != 0) {
error = EBUSY;
break;
}
}
error = device_delete_child(parent, dev);
break;
}
}
mtx_unlock(&Giant);
return (error);

View file

@ -118,6 +118,7 @@ struct devreq {
#define DEV_RESUME _IOW('D', 6, struct devreq)
#define DEV_SET_DRIVER _IOW('D', 7, struct devreq)
#define DEV_RESCAN _IOW('D', 9, struct devreq)
#define DEV_DELETE _IOW('D', 10, struct devreq)
/* Flags for DEV_DETACH and DEV_DISABLE. */
#define DEVF_FORCE_DETACH 0x0000001
@ -125,6 +126,9 @@ struct devreq {
/* Flags for DEV_SET_DRIVER. */
#define DEVF_SET_DRIVER_DETACH 0x0000001 /* Detach existing driver. */
/* Flags for DEV_DELETE. */
#define DEVF_FORCE_DELETE 0x0000001
#ifdef _KERNEL
#include <sys/eventhandler.h>

View file

@ -59,6 +59,10 @@
.Nm
.Cm rescan
.Ar device
.Nm
.Cm delete
.Op Fl f
.Ar device
.Sh DESCRIPTION
The
.Nm
@ -132,6 +136,18 @@ the device will not be changed.
.It Cm rescan Ar device
Rescan a bus device checking for devices that have been added or
removed.
.It Xo Cm delete
.Op Fl
.Ar device
.Xc
Delete the device from the device tree.
If the
.Fl f
flag is specified,
the device will be deleted even if it is physically present.
This command should be used with care as a device that is deleted but present
can no longer be used unless the parent bus device rediscovers the device via
a rescan request.
.El
.Sh SEE ALSO
.Xr devctl 3 ,

View file

@ -70,14 +70,16 @@ DEVCTL_TABLE(top, set);
static void
usage(void)
{
fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
"usage: devctl attach device",
" devctl detach [-f] device",
" devctl disable [-f] device",
" devctl enable device",
" devctl suspend device",
" devctl resume device",
" devctl set driver [-f] device driver");
" devctl set driver [-f] device driver",
" devctl rescan device",
" devctl delete [-f] device");
exit(1);
}
@ -271,6 +273,40 @@ rescan(int ac, char **av)
}
DEVCTL_COMMAND(top, rescan, rescan);
static void
delete_usage(void)
{
fprintf(stderr, "usage: devctl delete [-f] device\n");
exit(1);
}
static int
delete(int ac, char **av)
{
bool force;
int ch;
force = false;
while ((ch = getopt(ac, av, "f")) != -1)
switch (ch) {
case 'f':
force = true;
break;
default:
delete_usage();
}
ac -= optind;
av += optind;
if (ac != 1)
delete_usage();
if (devctl_delete(av[0], force) < 0)
err(1, "Failed to delete %s", av[0]);
return (0);
}
DEVCTL_COMMAND(top, delete, delete);
int
main(int ac, char *av[])
{