Lines 36-41
__FBSDID("$FreeBSD$");
Link Here
|
36 |
#include <sys/mutex.h> |
36 |
#include <sys/mutex.h> |
37 |
#include <sys/malloc.h> |
37 |
#include <sys/malloc.h> |
38 |
#include <sys/conf.h> |
38 |
#include <sys/conf.h> |
|
|
39 |
#include <sys/proc.h> |
39 |
#include <sys/sysctl.h> |
40 |
#include <sys/sysctl.h> |
40 |
#include <sys/libkern.h> |
41 |
#include <sys/libkern.h> |
41 |
#include <sys/ioccom.h> |
42 |
#include <sys/ioccom.h> |
Lines 746-804
vmmdev_destroy(void *arg)
Link Here
|
746 |
free(sc, M_VMMDEV); |
747 |
free(sc, M_VMMDEV); |
747 |
} |
748 |
} |
748 |
|
749 |
|
749 |
static int |
|
|
750 |
sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) |
751 |
{ |
752 |
int error; |
753 |
char buf[VM_MAX_NAMELEN]; |
754 |
struct devmem_softc *dsc; |
755 |
struct vmmdev_softc *sc; |
756 |
struct cdev *cdev; |
757 |
|
758 |
strlcpy(buf, "beavis", sizeof(buf)); |
759 |
error = sysctl_handle_string(oidp, buf, sizeof(buf), req); |
760 |
if (error != 0 || req->newptr == NULL) |
761 |
return (error); |
762 |
|
763 |
mtx_lock(&vmmdev_mtx); |
764 |
sc = vmmdev_lookup(buf); |
765 |
if (sc == NULL || sc->cdev == NULL) { |
766 |
mtx_unlock(&vmmdev_mtx); |
767 |
return (EINVAL); |
768 |
} |
769 |
|
770 |
/* |
771 |
* The 'cdev' will be destroyed asynchronously when 'si_threadcount' |
772 |
* goes down to 0 so we should not do it again in the callback. |
773 |
* |
774 |
* Setting 'sc->cdev' to NULL is also used to indicate that the VM |
775 |
* is scheduled for destruction. |
776 |
*/ |
777 |
cdev = sc->cdev; |
778 |
sc->cdev = NULL; |
779 |
mtx_unlock(&vmmdev_mtx); |
780 |
|
781 |
/* |
782 |
* Schedule all cdevs to be destroyed: |
783 |
* |
784 |
* - any new operations on the 'cdev' will return an error (ENXIO). |
785 |
* |
786 |
* - when the 'si_threadcount' dwindles down to zero the 'cdev' will |
787 |
* be destroyed and the callback will be invoked in a taskqueue |
788 |
* context. |
789 |
* |
790 |
* - the 'devmem' cdevs are destroyed before the virtual machine 'cdev' |
791 |
*/ |
792 |
SLIST_FOREACH(dsc, &sc->devmem, link) { |
793 |
KASSERT(dsc->cdev != NULL, ("devmem cdev already destroyed")); |
794 |
destroy_dev_sched_cb(dsc->cdev, devmem_destroy, dsc); |
795 |
} |
796 |
destroy_dev_sched_cb(cdev, vmmdev_destroy, sc); |
797 |
return (0); |
798 |
} |
799 |
SYSCTL_PROC(_hw_vmm, OID_AUTO, destroy, CTLTYPE_STRING | CTLFLAG_RW, |
800 |
NULL, 0, sysctl_vmm_destroy, "A", NULL); |
801 |
|
802 |
static struct cdevsw vmmdevsw = { |
750 |
static struct cdevsw vmmdevsw = { |
803 |
.d_name = "vmmdev", |
751 |
.d_name = "vmmdev", |
804 |
.d_version = D_VERSION, |
752 |
.d_version = D_VERSION, |
Lines 808-875
static struct cdevsw vmmdevsw = {
Link Here
|
808 |
.d_write = vmmdev_rw, |
756 |
.d_write = vmmdev_rw, |
809 |
}; |
757 |
}; |
810 |
|
758 |
|
811 |
static int |
|
|
812 |
sysctl_vmm_create(SYSCTL_HANDLER_ARGS) |
813 |
{ |
814 |
int error; |
815 |
struct vm *vm; |
816 |
struct cdev *cdev; |
817 |
struct vmmdev_softc *sc, *sc2; |
818 |
char buf[VM_MAX_NAMELEN]; |
819 |
|
820 |
strlcpy(buf, "beavis", sizeof(buf)); |
821 |
error = sysctl_handle_string(oidp, buf, sizeof(buf), req); |
822 |
if (error != 0 || req->newptr == NULL) |
823 |
return (error); |
824 |
|
825 |
mtx_lock(&vmmdev_mtx); |
826 |
sc = vmmdev_lookup(buf); |
827 |
mtx_unlock(&vmmdev_mtx); |
828 |
if (sc != NULL) |
829 |
return (EEXIST); |
830 |
|
831 |
error = vm_create(buf, &vm); |
832 |
if (error != 0) |
833 |
return (error); |
834 |
|
835 |
sc = malloc(sizeof(struct vmmdev_softc), M_VMMDEV, M_WAITOK | M_ZERO); |
836 |
sc->vm = vm; |
837 |
SLIST_INIT(&sc->devmem); |
838 |
|
839 |
/* |
840 |
* Lookup the name again just in case somebody sneaked in when we |
841 |
* dropped the lock. |
842 |
*/ |
843 |
mtx_lock(&vmmdev_mtx); |
844 |
sc2 = vmmdev_lookup(buf); |
845 |
if (sc2 == NULL) { |
846 |
SLIST_INSERT_HEAD(&head, sc, link); |
847 |
sc->flags |= VSC_LINKED; |
848 |
} |
849 |
mtx_unlock(&vmmdev_mtx); |
850 |
|
851 |
if (sc2 != NULL) { |
852 |
vmmdev_destroy(sc); |
853 |
return (EEXIST); |
854 |
} |
855 |
|
856 |
error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &vmmdevsw, NULL, |
857 |
UID_ROOT, GID_WHEEL, 0600, "vmm/%s", buf); |
858 |
if (error != 0) { |
859 |
vmmdev_destroy(sc); |
860 |
return (error); |
861 |
} |
862 |
|
863 |
mtx_lock(&vmmdev_mtx); |
864 |
sc->cdev = cdev; |
865 |
sc->cdev->si_drv1 = sc; |
866 |
mtx_unlock(&vmmdev_mtx); |
867 |
|
868 |
return (0); |
869 |
} |
870 |
SYSCTL_PROC(_hw_vmm, OID_AUTO, create, CTLTYPE_STRING | CTLFLAG_RW, |
871 |
NULL, 0, sysctl_vmm_create, "A", NULL); |
872 |
|
873 |
void |
759 |
void |
874 |
vmmdev_init(void) |
760 |
vmmdev_init(void) |
875 |
{ |
761 |
{ |
Lines 941-962
devmem_create_cdev(const char *vmname, int segid, char *devname)
Link Here
|
941 |
struct vmmdev_softc *sc; |
827 |
struct vmmdev_softc *sc; |
942 |
struct cdev *cdev; |
828 |
struct cdev *cdev; |
943 |
int error; |
829 |
int error; |
|
|
830 |
uid_t uid; |
831 |
|
832 |
mtx_lock(&vmmdev_mtx); |
833 |
sc = vmmdev_lookup(vmname); |
834 |
KASSERT(sc != NULL, ("%s: vm %s softc not found", __func__, vmname)); |
835 |
if (sc->cdev == NULL) { |
836 |
/* virtual machine is being created or destroyed */ |
837 |
mtx_unlock(&vmmdev_mtx); |
838 |
return (ENODEV); |
839 |
} |
840 |
uid = sc->cdev->si_uid; |
841 |
mtx_unlock(&vmmdev_mtx); |
944 |
|
842 |
|
945 |
error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &devmemsw, NULL, |
843 |
error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &devmemsw, NULL, |
946 |
UID_ROOT, GID_WHEEL, 0600, "vmm.io/%s.%s", vmname, devname); |
844 |
uid, GID_WHEEL, 0600, "vmm.io/%s.%s", vmname, devname); |
947 |
if (error) |
845 |
if (error) |
948 |
return (error); |
846 |
return (error); |
949 |
|
|
|
950 |
dsc = malloc(sizeof(struct devmem_softc), M_VMMDEV, M_WAITOK | M_ZERO); |
847 |
dsc = malloc(sizeof(struct devmem_softc), M_VMMDEV, M_WAITOK | M_ZERO); |
951 |
|
848 |
|
|
|
849 |
/* The lock was released, check again, if the machine is being destroyed */ |
952 |
mtx_lock(&vmmdev_mtx); |
850 |
mtx_lock(&vmmdev_mtx); |
953 |
sc = vmmdev_lookup(vmname); |
851 |
sc = vmmdev_lookup(vmname); |
954 |
KASSERT(sc != NULL, ("%s: vm %s softc not found", __func__, vmname)); |
852 |
KASSERT(sc != NULL, ("%s: vm %s softc not found", __func__, vmname)); |
955 |
if (sc->cdev == NULL) { |
853 |
if (sc->cdev == NULL) { |
956 |
/* virtual machine is being created or destroyed */ |
|
|
957 |
mtx_unlock(&vmmdev_mtx); |
854 |
mtx_unlock(&vmmdev_mtx); |
958 |
free(dsc, M_VMMDEV); |
855 |
free(dsc, M_VMMDEV); |
959 |
destroy_dev_sched_cb(cdev, NULL, 0); |
856 |
destroy_dev_sched(cdev); |
960 |
return (ENODEV); |
857 |
return (ENODEV); |
961 |
} |
858 |
} |
962 |
|
859 |
|
Lines 981-983
devmem_destroy(void *arg)
Link Here
|
981 |
dsc->cdev = NULL; |
878 |
dsc->cdev = NULL; |
982 |
dsc->sc = NULL; |
879 |
dsc->sc = NULL; |
983 |
} |
880 |
} |
|
|
881 |
|
882 |
/* ARGSUSED */ |
883 |
static int |
884 |
vmmctl_rw(struct cdev *cdev __unused, struct uio *uio __unused, int flags __unused) |
885 |
{ |
886 |
return EOPNOTSUPP; |
887 |
} |
888 |
|
889 |
static int |
890 |
vmmctl_create (char *name, uid_t uid) |
891 |
{ |
892 |
int error; |
893 |
struct vmmdev_softc *sc, *sc2; |
894 |
struct cdev *cdev; |
895 |
struct vm *vm; |
896 |
|
897 |
mtx_lock(&vmmdev_mtx); |
898 |
sc = vmmdev_lookup(name); |
899 |
mtx_unlock(&vmmdev_mtx); |
900 |
if (sc != NULL) |
901 |
return (EEXIST); |
902 |
|
903 |
error = vm_create(name, &vm); |
904 |
if (error != 0) |
905 |
return error; |
906 |
|
907 |
sc = malloc(sizeof(struct vmmdev_softc), M_VMMDEV, M_WAITOK | M_ZERO); |
908 |
sc->vm = vm; |
909 |
SLIST_INIT(&sc->devmem); |
910 |
|
911 |
/* |
912 |
* Lookup the name again just in case somebody sneaked in when we |
913 |
* dropped the lock. |
914 |
*/ |
915 |
mtx_lock(&vmmdev_mtx); |
916 |
sc2 = vmmdev_lookup(name); |
917 |
if (sc2 == NULL) { |
918 |
SLIST_INSERT_HEAD(&head, sc, link); |
919 |
sc->flags |= VSC_LINKED; |
920 |
} |
921 |
mtx_unlock(&vmmdev_mtx); |
922 |
|
923 |
if (sc2 != NULL) { |
924 |
vmmdev_destroy(sc); |
925 |
return EEXIST; |
926 |
} |
927 |
|
928 |
error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &vmmdevsw, NULL, |
929 |
uid, GID_WHEEL, 0600, "vmm/%s", name); |
930 |
if (error != 0) { |
931 |
vmmdev_destroy(sc); |
932 |
return error; |
933 |
} |
934 |
|
935 |
mtx_lock(&vmmdev_mtx); |
936 |
sc->cdev = cdev; |
937 |
sc->cdev->si_drv1 = sc; |
938 |
mtx_unlock(&vmmdev_mtx); |
939 |
|
940 |
return 0; |
941 |
} |
942 |
|
943 |
static int |
944 |
vmmctl_destroy (char *name) |
945 |
{ |
946 |
struct devmem_softc *dsc; |
947 |
struct vmmdev_softc *sc; |
948 |
struct cdev *cdev; |
949 |
|
950 |
mtx_lock(&vmmdev_mtx); |
951 |
sc = vmmdev_lookup(name); |
952 |
if (sc == NULL || sc->cdev == NULL) { |
953 |
mtx_unlock(&vmmdev_mtx); |
954 |
return EINVAL; |
955 |
} |
956 |
/* |
957 |
* The 'cdev' will be destroyed asynchronously when 'si_threadcount' |
958 |
* goes down to 0 so we should not do it again in the callback. |
959 |
* |
960 |
* Setting 'sc->cdev' to NULL is also used to indicate that the VM |
961 |
* is scheduled for destruction. |
962 |
*/ |
963 |
cdev = sc->cdev; |
964 |
sc->cdev = NULL; |
965 |
mtx_unlock(&vmmdev_mtx); |
966 |
|
967 |
SLIST_FOREACH(dsc, &sc->devmem, link) { |
968 |
KASSERT(dsc->cdev != NULL, ("devmem cdev already destroyed")); |
969 |
destroy_dev_sched_cb(dsc->cdev, devmem_destroy, dsc); |
970 |
} |
971 |
destroy_dev_sched_cb(cdev, vmmdev_destroy, sc); |
972 |
return 0; |
973 |
} |
974 |
|
975 |
/* ARGSUSED */ |
976 |
static int |
977 |
vmmctl_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, |
978 |
int flags __unused, struct thread *td) |
979 |
{ |
980 |
int error; |
981 |
char *name; |
982 |
int len; |
983 |
|
984 |
error = 0; |
985 |
|
986 |
switch (cmd) { |
987 |
case VMCTL_CREATE: |
988 |
name = (char*) data; |
989 |
len = strnlen (name, VM_MAX_NAMELEN); |
990 |
if (len == VM_MAX_NAMELEN) { |
991 |
error = EINVAL; |
992 |
goto done; |
993 |
} |
994 |
printf ("Creating VM with name: %s\n", name); |
995 |
error = vmmctl_create (name, td->td_ucred->cr_uid); |
996 |
break; |
997 |
|
998 |
case VMCTL_DESTROY: |
999 |
name = (char*) data; |
1000 |
len = strnlen (name, VM_MAX_NAMELEN); |
1001 |
if (len == VM_MAX_NAMELEN) { |
1002 |
error = EINVAL; |
1003 |
goto done; |
1004 |
} |
1005 |
printf ("Destroying VM with name: %s\n", (char*)data); |
1006 |
error = vmmctl_destroy (name); |
1007 |
break; |
1008 |
|
1009 |
default: |
1010 |
error = ENOIOCTL; |
1011 |
} |
1012 |
done: |
1013 |
/* Make sure that no handler returns a bogus value like ERESTART */ |
1014 |
KASSERT(error >= 0, ("vmmdev_ioctl: invalid error return %d", error)); |
1015 |
return (error); |
1016 |
} |
1017 |
|
1018 |
static struct cdevsw vmmctl_cdevsw = { |
1019 |
.d_version = D_VERSION, |
1020 |
.d_open = (d_open_t*) nullop, |
1021 |
.d_read = vmmctl_rw, |
1022 |
.d_write = vmmctl_rw, |
1023 |
.d_ioctl = vmmctl_ioctl, |
1024 |
.d_name = "vmmctl", |
1025 |
}; |
1026 |
|
1027 |
static struct cdev *vmmctl_dev; |
1028 |
|
1029 |
static void |
1030 |
vmmctl_sysinit(void *p) |
1031 |
{ |
1032 |
vmmctl_dev = make_dev(&vmmctl_cdevsw, 0, |
1033 |
UID_ROOT, GID_WHEEL, 0660, "vmmctl"); |
1034 |
} |
1035 |
SYSINIT(vmmctl_sysinit, SI_SUB_DEVFS, SI_ORDER_ANY, vmmctl_sysinit, NULL); |
1036 |
|
1037 |
static void |
1038 |
vmmctl_sysuninit(void *p) |
1039 |
{ |
1040 |
if (vmmctl_dev != NULL) { |
1041 |
/* destroy_dev() will wait for all references to go away */ |
1042 |
destroy_dev(vmmctl_dev); |
1043 |
} |
1044 |
} |
1045 |
SYSUNINIT(vmmctl_sysuninit, SI_SUB_DEVFS, SI_ORDER_ANY, vmmctl_sysuninit, NULL); |