mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-01 14:14:56 +00:00
Compare commits
13 commits
c7bb7a2333
...
0e391d97fb
Author | SHA1 | Date | |
---|---|---|---|
0e391d97fb | |||
172fbf12fc | |||
5d5082fd98 | |||
7a8bab3d94 | |||
259f94f8ab | |||
1dc90424b2 | |||
ab9e5b8659 | |||
4451b1de21 | |||
1cc56f881f | |||
43d29dceea | |||
af64741bba | |||
4052c95280 | |||
51bee6ab65 |
|
@ -1141,6 +1141,8 @@
|
|||
usr.sbin
|
||||
chown
|
||||
..
|
||||
ctladm
|
||||
..
|
||||
daemon
|
||||
..
|
||||
etcupdate
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd April 18, 2022
|
||||
.Dd April 20, 2024
|
||||
.Dt GELI 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -867,7 +867,7 @@ If set to 0, a CPU-pinned thread will be started for every active CPU.
|
|||
When set to 1, can speed-up crypto operations by using batching.
|
||||
Batching reduces the number of interrupts by responding to a group of
|
||||
crypto requests with one interrupt.
|
||||
The crypto card and the driver has to support this feature.
|
||||
The crypto card and the driver have to support this feature.
|
||||
.It Va kern.geom.eli.key_cache_limit : No 8192
|
||||
Specifies how many Data Keys to cache.
|
||||
The default limit
|
||||
|
@ -884,7 +884,7 @@ Reports how many times we were looking up a Data Key and it was not in cache.
|
|||
This sysctl is not updated for providers that need fewer Data Keys than the limit
|
||||
specified in
|
||||
.Va kern.geom.eli.key_cache_limit .
|
||||
.Va kern.geom.eli.unmapped_io
|
||||
.It Va kern.geom.eli.unmapped_io
|
||||
Enable support for unmapped I/O buffers, currently implemented only on 64-bit
|
||||
platforms.
|
||||
This is an optimization which reduces the overhead of I/O processing.
|
||||
|
|
|
@ -291,7 +291,7 @@ attach_size_rounddown_body()
|
|||
-x "mdconfig -r -u ${md#md} -s ${ms2}b"
|
||||
check_diskinfo "$md" 16384 2 $ss
|
||||
}
|
||||
attach_size_rounddown()
|
||||
attach_size_rounddown_cleanup()
|
||||
{
|
||||
cleanup_common
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||||
.\" DAMAGE.
|
||||
.\"
|
||||
.Dd July 31, 2011
|
||||
.Dd May 19, 2024
|
||||
.Dt VFS_GETOPT 9
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -46,7 +46,7 @@
|
|||
.Fa "struct vfsoptlist *opts" "const char *name" "void **buf" "int *len"
|
||||
.Fc
|
||||
.Ft "char *"
|
||||
.Fn vfs_getops "struct vfsoptlist *opts" "const char *name" "int *error"
|
||||
.Fn vfs_getopts "struct vfsoptlist *opts" "const char *name" "int *error"
|
||||
.Ft int
|
||||
.Fo vfs_flagopt
|
||||
.Fa "struct vfsoptlist *opts" "const char *name" "uint64_t *flags" "uint64_t flag"
|
||||
|
@ -177,7 +177,7 @@ function returns 0 if the option was found; otherwise,
|
|||
is returned.
|
||||
.Pp
|
||||
The
|
||||
.Fn vfs_getops
|
||||
.Fn vfs_getopts
|
||||
function returns the specified option if it is found, and is
|
||||
.Dv NUL
|
||||
terminated.
|
||||
|
|
|
@ -268,7 +268,7 @@ cfi_ioctl_port_remove(struct ctl_req *req)
|
|||
if (port_id == -1) {
|
||||
req->status = CTL_LUN_ERROR;
|
||||
snprintf(req->error_str, sizeof(req->error_str),
|
||||
"port_id not provided");
|
||||
"Missing required argument: port_id");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -2150,17 +2150,24 @@ cfiscsi_ioctl_port_create(struct ctl_req *req)
|
|||
uint16_t tag;
|
||||
|
||||
target = dnvlist_get_string(req->args_nvl, "cfiscsi_target", NULL);
|
||||
alias = dnvlist_get_string(req->args_nvl, "cfiscsi_target_alias", NULL);
|
||||
val = dnvlist_get_string(req->args_nvl, "cfiscsi_portal_group_tag",
|
||||
NULL);
|
||||
|
||||
if (target == NULL || val == NULL) {
|
||||
if (target == NULL) {
|
||||
req->status = CTL_LUN_ERROR;
|
||||
snprintf(req->error_str, sizeof(req->error_str),
|
||||
"Missing required argument");
|
||||
"Missing required argument: cfiscsi_target");
|
||||
return;
|
||||
}
|
||||
|
||||
val = dnvlist_get_string(req->args_nvl, "cfiscsi_portal_group_tag",
|
||||
NULL);
|
||||
if (val == NULL) {
|
||||
req->status = CTL_LUN_ERROR;
|
||||
snprintf(req->error_str, sizeof(req->error_str),
|
||||
"Missing required argument: cfiscsi_portal_group_tag");
|
||||
return;
|
||||
}
|
||||
|
||||
alias = dnvlist_get_string(req->args_nvl, "cfiscsi_target_alias", NULL);
|
||||
|
||||
tag = strtoul(val, NULL, 0);
|
||||
ct = cfiscsi_target_find_or_create(&cfiscsi_softc, target, alias, tag);
|
||||
if (ct == NULL) {
|
||||
|
@ -2251,13 +2258,19 @@ cfiscsi_ioctl_port_remove(struct ctl_req *req)
|
|||
uint16_t tag;
|
||||
|
||||
target = dnvlist_get_string(req->args_nvl, "cfiscsi_target", NULL);
|
||||
val = dnvlist_get_string(req->args_nvl, "cfiscsi_portal_group_tag",
|
||||
NULL);
|
||||
|
||||
if (target == NULL || val == NULL) {
|
||||
if (target == NULL) {
|
||||
req->status = CTL_LUN_ERROR;
|
||||
snprintf(req->error_str, sizeof(req->error_str),
|
||||
"Missing required argument");
|
||||
"Missing required argument: cfiscsi_target");
|
||||
return;
|
||||
}
|
||||
|
||||
val = dnvlist_get_string(req->args_nvl, "cfiscsi_portal_group_tag",
|
||||
NULL);
|
||||
if (val == NULL) {
|
||||
req->status = CTL_LUN_ERROR;
|
||||
snprintf(req->error_str, sizeof(req->error_str),
|
||||
"Missing required argument: cfiscsi_portal_group_tag");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ static struct zfs_jailparam zfs_jailparam0 = {
|
|||
static int zfs_jailparam_slot;
|
||||
|
||||
SYSCTL_JAIL_PARAM_SYS_NODE(zfs, CTLFLAG_RW, "Jail ZFS parameters");
|
||||
SYSCTL_JAIL_PARAM(_zfs, mount_snapshot, CTLTYPE_INT | CTLFLAG_RW, "I",
|
||||
SYSCTL_JAIL_PARAM(_zfs, mount_snapshot, CTLTYPE_INT | CTLFLAG_RW, "B",
|
||||
"Allow mounting snapshots in the .zfs directory for unjailed datasets");
|
||||
|
||||
SYSCTL_NODE(_vfs_zfs, OID_AUTO, version, CTLFLAG_RD, 0, "ZFS versions");
|
||||
|
|
|
@ -77,8 +77,6 @@ class BmapEof: public Bmap, public WithParamInterface<int> {};
|
|||
|
||||
/*
|
||||
* Test FUSE_BMAP
|
||||
* XXX The FUSE protocol does not include the runp and runb variables, so those
|
||||
* must be guessed in-kernel.
|
||||
*/
|
||||
TEST_F(Bmap, bmap)
|
||||
{
|
||||
|
@ -105,8 +103,19 @@ TEST_F(Bmap, bmap)
|
|||
arg.runb = -1;
|
||||
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
|
||||
EXPECT_EQ(arg.bn, pbn);
|
||||
EXPECT_EQ(arg.runp, m_maxphys / m_maxbcachebuf - 1);
|
||||
EXPECT_EQ(arg.runb, m_maxphys / m_maxbcachebuf - 1);
|
||||
/*
|
||||
* XXX The FUSE protocol does not include the runp and runb variables,
|
||||
* so those must be guessed in-kernel. There's no "right" answer, so
|
||||
* just check that they're within reasonable limits.
|
||||
*/
|
||||
EXPECT_LE(arg.runb, lbn);
|
||||
EXPECT_LE((unsigned long)arg.runb, m_maxreadahead / m_maxbcachebuf);
|
||||
EXPECT_LE((unsigned long)arg.runb, m_maxphys / m_maxbcachebuf);
|
||||
EXPECT_GT(arg.runb, 0);
|
||||
EXPECT_LE(arg.runp, filesize / m_maxbcachebuf - lbn);
|
||||
EXPECT_LE((unsigned long)arg.runp, m_maxreadahead / m_maxbcachebuf);
|
||||
EXPECT_LE((unsigned long)arg.runp, m_maxphys / m_maxbcachebuf);
|
||||
EXPECT_GT(arg.runp, 0);
|
||||
|
||||
leak(fd);
|
||||
}
|
||||
|
@ -142,7 +151,7 @@ TEST_F(Bmap, default_)
|
|||
arg.runb = -1;
|
||||
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
|
||||
EXPECT_EQ(arg.bn, 0);
|
||||
EXPECT_EQ(arg.runp, m_maxphys / m_maxbcachebuf - 1);
|
||||
EXPECT_EQ((unsigned long )arg.runp, m_maxphys / m_maxbcachebuf - 1);
|
||||
EXPECT_EQ(arg.runb, 0);
|
||||
|
||||
/* In the middle */
|
||||
|
@ -152,8 +161,8 @@ TEST_F(Bmap, default_)
|
|||
arg.runb = -1;
|
||||
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
|
||||
EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE);
|
||||
EXPECT_EQ(arg.runp, m_maxphys / m_maxbcachebuf - 1);
|
||||
EXPECT_EQ(arg.runb, m_maxphys / m_maxbcachebuf - 1);
|
||||
EXPECT_EQ((unsigned long )arg.runp, m_maxphys / m_maxbcachebuf - 1);
|
||||
EXPECT_EQ((unsigned long )arg.runb, m_maxphys / m_maxbcachebuf - 1);
|
||||
|
||||
/* Last block */
|
||||
lbn = filesize / m_maxbcachebuf - 1;
|
||||
|
@ -163,7 +172,7 @@ TEST_F(Bmap, default_)
|
|||
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
|
||||
EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE);
|
||||
EXPECT_EQ(arg.runp, 0);
|
||||
EXPECT_EQ(arg.runb, m_maxphys / m_maxbcachebuf - 1);
|
||||
EXPECT_EQ((unsigned long )arg.runb, m_maxphys / m_maxbcachebuf - 1);
|
||||
|
||||
leak(fd);
|
||||
}
|
||||
|
|
|
@ -1339,7 +1339,7 @@ TEST_P(ReadAhead, readahead) {
|
|||
expect_open(ino, 0, 1);
|
||||
maxcontig = m_noclusterr ? m_maxbcachebuf :
|
||||
m_maxbcachebuf + m_maxreadahead;
|
||||
clustersize = MIN(maxcontig, m_maxphys);
|
||||
clustersize = MIN((unsigned long )maxcontig, m_maxphys);
|
||||
for (offs = 0; offs < bufsize; offs += clustersize) {
|
||||
len = std::min((size_t)clustersize, (size_t)(filesize - offs));
|
||||
expect_read(ino, offs, len, len, contents + offs);
|
||||
|
|
|
@ -130,8 +130,7 @@ class FuseEnv: public Environment {
|
|||
void FuseTest::SetUp() {
|
||||
const char *maxbcachebuf_node = "vfs.maxbcachebuf";
|
||||
const char *maxphys_node = "kern.maxphys";
|
||||
int val = 0;
|
||||
size_t size = sizeof(val);
|
||||
size_t size;
|
||||
|
||||
/*
|
||||
* XXX check_environment should be called from FuseEnv::SetUp, but
|
||||
|
@ -141,12 +140,12 @@ void FuseTest::SetUp() {
|
|||
if (IsSkipped())
|
||||
return;
|
||||
|
||||
ASSERT_EQ(0, sysctlbyname(maxbcachebuf_node, &val, &size, NULL, 0))
|
||||
size = sizeof(m_maxbcachebuf);
|
||||
ASSERT_EQ(0, sysctlbyname(maxbcachebuf_node, &m_maxbcachebuf, &size,
|
||||
NULL, 0)) << strerror(errno);
|
||||
size = sizeof(m_maxphys);
|
||||
ASSERT_EQ(0, sysctlbyname(maxphys_node, &m_maxphys, &size, NULL, 0))
|
||||
<< strerror(errno);
|
||||
m_maxbcachebuf = val;
|
||||
ASSERT_EQ(0, sysctlbyname(maxphys_node, &val, &size, NULL, 0))
|
||||
<< strerror(errno);
|
||||
m_maxphys = val;
|
||||
/*
|
||||
* Set the default max_write to a distinct value from MAXPHYS to catch
|
||||
* bugs that confuse the two.
|
||||
|
|
|
@ -77,7 +77,7 @@ class FuseTest : public ::testing::Test {
|
|||
|
||||
public:
|
||||
int m_maxbcachebuf;
|
||||
int m_maxphys;
|
||||
unsigned long m_maxphys;
|
||||
|
||||
FuseTest():
|
||||
m_maxreadahead(0),
|
||||
|
|
|
@ -179,12 +179,12 @@ class WriteCluster: public WriteBack {
|
|||
public:
|
||||
virtual void SetUp() {
|
||||
m_async = true;
|
||||
m_maxwrite = 1 << 25; // Anything larger than MAXPHYS will suffice
|
||||
m_maxwrite = UINT32_MAX; // Anything larger than MAXPHYS will suffice
|
||||
WriteBack::SetUp();
|
||||
if (m_maxphys < 2 * DFLTPHYS)
|
||||
GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS"
|
||||
<< " for this test";
|
||||
if (m_maxphys < 2 * m_maxbcachebuf)
|
||||
if (m_maxphys < 2 * (unsigned long )m_maxbcachebuf)
|
||||
GTEST_SKIP() << "MAXPHYS must be at least twice maxbcachebuf"
|
||||
<< " for this test";
|
||||
}
|
||||
|
@ -860,7 +860,8 @@ TEST_F(WriteMaxWrite, write)
|
|||
ssize_t halfbufsize, bufsize;
|
||||
|
||||
halfbufsize = m_mock->m_maxwrite;
|
||||
if (halfbufsize >= m_maxbcachebuf || halfbufsize >= m_maxphys)
|
||||
if (halfbufsize >= m_maxbcachebuf ||
|
||||
(unsigned long )halfbufsize >= m_maxphys)
|
||||
GTEST_SKIP() << "Must lower m_maxwrite for this test";
|
||||
bufsize = halfbufsize * 2;
|
||||
contents = new int[bufsize / sizeof(int)];
|
||||
|
|
|
@ -23,4 +23,7 @@ MAN= ctladm.8
|
|||
CFLAGS+= -DWANT_ISCSI
|
||||
.endif
|
||||
|
||||
HAS_TESTS=
|
||||
SUBDIR.${MK_TESTS}+= tests
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
.\"
|
||||
.\" $Id: //depot/users/kenm/FreeBSD-test2/usr.sbin/ctladm/ctladm.8#3 $
|
||||
.\"
|
||||
.Dd December 27, 2023
|
||||
.Dd June 6, 2024
|
||||
.Dt CTLADM 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -162,10 +162,11 @@
|
|||
.Nm
|
||||
.Ic port
|
||||
.Op Fl c
|
||||
.Op Fl d Ar driver
|
||||
.Op Fl o Ar on|off
|
||||
.Op Fl w Ar wwpn
|
||||
.Op Fl W Ar wwnn
|
||||
.Op Fl O Ar pp|vp
|
||||
.Op Fl O Ar name=value
|
||||
.Op Fl p Ar targ_port
|
||||
.Op Fl r
|
||||
.Op Fl t Ar fe_type
|
||||
|
@ -591,6 +592,10 @@ The serial number is returned when the error is injected.
|
|||
Perform one of several CTL frontend port operations.
|
||||
Either get a list of frontend ports
|
||||
.Pq Fl l ,
|
||||
create a new frontend port
|
||||
.Pq Fl c ,
|
||||
destroy a frontend port
|
||||
.Pq Fl r ,
|
||||
turn one or more frontends on
|
||||
or off
|
||||
.Pq Fl o Ar on|off ,
|
||||
|
@ -600,6 +605,8 @@ or World Wide Port Name
|
|||
.Pq Fl W Ar wwpn
|
||||
for a given port.
|
||||
One of
|
||||
.Fl c ,
|
||||
.Fl r ,
|
||||
.Fl l ,
|
||||
.Fl o ,
|
||||
or
|
||||
|
@ -611,22 +618,62 @@ The WWNN and WWPN may both be specified at the same time, but cannot be
|
|||
combined with enabling/disabling or listing ports.
|
||||
.Bl -tag -width 12n
|
||||
.It Fl c
|
||||
Create new frontend port using free pp and vp=0.
|
||||
Create new frontend port.
|
||||
.It Fl d Ar driver
|
||||
Specify the name of the frontend driver used by the
|
||||
.Pq Fl c
|
||||
or
|
||||
.Pq Fl r
|
||||
subcommands.
|
||||
Valid driver names include
|
||||
.Dq ioctl ,
|
||||
.Dq iscsi ,
|
||||
and
|
||||
.Dq nvmf ,
|
||||
but more can be added by external kernel modules.
|
||||
.It Fl o Ar on|off
|
||||
Turn the specified CTL frontend ports on or off.
|
||||
If no port number or port type is specified, all ports are turned on or
|
||||
off.
|
||||
.It Fl O Ar pp|vp
|
||||
Specify generic options on the ioctl frontend port.
|
||||
At present, only pp and vp port numbers can be set.
|
||||
The list of recognized options is driver-dependent.
|
||||
The
|
||||
.Dq ioctl
|
||||
driver recognizes
|
||||
.Dq pp
|
||||
and
|
||||
.Dq vp .
|
||||
The
|
||||
.Dq iscsi
|
||||
driver recongizes
|
||||
.Dq cfiscsi_portal_group_tag ,
|
||||
.Dq cfiscsi_target ,
|
||||
and
|
||||
.Dq cfiscsi_target_alias .
|
||||
The
|
||||
.Dq nvmf
|
||||
driver recognizes
|
||||
.Dq subnqn ,
|
||||
.Dq portid ,
|
||||
.Dq max_io_qsize ,
|
||||
.Dq enable_timeout ,
|
||||
.Dq ioccsz ,
|
||||
.Dq nn ,
|
||||
and
|
||||
.Dq serial .
|
||||
.It Fl p Ar targ_port
|
||||
Specify the frontend port number.
|
||||
The port numbers can be found in the frontend port list.
|
||||
.It Fl r
|
||||
Remove port specified with
|
||||
.Pq Fl p Ar targ_port .
|
||||
Remove a port.
|
||||
.It Fl t Ar fe_type
|
||||
Specify the frontend type.
|
||||
Specify the frontend type used by the
|
||||
.Pq Fl o ,
|
||||
.Pq Fl w ,
|
||||
and
|
||||
.Pq Fl W
|
||||
subcommands.
|
||||
Currently defined port types are
|
||||
.Dq fc
|
||||
(Fibre Channel),
|
||||
|
@ -640,7 +687,7 @@ and
|
|||
.It Fl w Ar wwnn
|
||||
Set the World Wide Node Name for the given port.
|
||||
The
|
||||
.Fl n
|
||||
.Fl p
|
||||
argument must be specified, since this is only possible to implement on a
|
||||
single port.
|
||||
As a general rule, the WWNN should be the same across all ports on the
|
||||
|
@ -648,7 +695,7 @@ system.
|
|||
.It Fl W Ar wwpn
|
||||
Set the World Wide Port Name for the given port.
|
||||
The
|
||||
.Fl n
|
||||
.Fl p
|
||||
argument must be specified, since this is only possible to implement on a
|
||||
single port.
|
||||
As a general rule, the WWPN must be different for every port in the system.
|
||||
|
|
|
@ -392,7 +392,9 @@ static struct ctladm_opts cctl_fe_table[] = {
|
|||
static int
|
||||
cctl_port(int fd, int argc, char **argv, char *combinedopt)
|
||||
{
|
||||
char result_buf[1024];
|
||||
int c;
|
||||
uint64_t created_port = -1;
|
||||
int32_t targ_port = -1;
|
||||
int retval = 0;
|
||||
int wwnn_set = 0, wwpn_set = 0;
|
||||
|
@ -541,7 +543,7 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
|
|||
* we'll throw an error, since that only works on one port at a time.
|
||||
*/
|
||||
if ((port_type != CTL_PORT_NONE) && (targ_port != -1)) {
|
||||
warnx("%s: can only specify one of -t or -n", __func__);
|
||||
warnx("%s: can only specify one of -t or -p", __func__);
|
||||
retval = 1;
|
||||
goto bailout;
|
||||
} else if ((targ_port == -1) && (port_type == CTL_PORT_NONE))
|
||||
|
@ -573,20 +575,18 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
|
|||
break;
|
||||
}
|
||||
case CCTL_PORT_MODE_REMOVE:
|
||||
if (targ_port == -1) {
|
||||
warnx("%s: -r requires -p", __func__);
|
||||
retval = 1;
|
||||
goto bailout;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case CCTL_PORT_MODE_CREATE: {
|
||||
bzero(&req, sizeof(req));
|
||||
strlcpy(req.driver, driver, sizeof(req.driver));
|
||||
req.result = result_buf;
|
||||
req.result_len = sizeof(result_buf);
|
||||
|
||||
if (port_mode == CCTL_PORT_MODE_REMOVE) {
|
||||
req.reqtype = CTL_REQ_REMOVE;
|
||||
nvlist_add_stringf(option_list, "port_id", "%d",
|
||||
targ_port);
|
||||
if (targ_port != -1)
|
||||
nvlist_add_stringf(option_list, "port_id", "%d",
|
||||
targ_port);
|
||||
} else
|
||||
req.reqtype = CTL_REQ_CREATE;
|
||||
|
||||
|
@ -614,6 +614,20 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
|
|||
warnx("warning: %s", req.error_str);
|
||||
break;
|
||||
case CTL_LUN_OK:
|
||||
if (port_mode == CCTL_PORT_MODE_CREATE) {
|
||||
req.result_nvl = nvlist_unpack(result_buf, req.result_len, 0);
|
||||
if (req.result_nvl == NULL) {
|
||||
warnx("error unpacking result nvlist");
|
||||
break;
|
||||
}
|
||||
created_port = nvlist_get_number(req.result_nvl, "port_id");
|
||||
printf("Port created successfully\n"
|
||||
"frontend: %s\n"
|
||||
"port: %ju\n", driver,
|
||||
(uintmax_t) created_port);
|
||||
nvlist_destroy(req.result_nvl);
|
||||
} else
|
||||
printf("Port destroyed successfully\n");
|
||||
break;
|
||||
default:
|
||||
warnx("unknown status: %d", req.status);
|
||||
|
@ -625,7 +639,7 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
|
|||
}
|
||||
case CCTL_PORT_MODE_SET:
|
||||
if (targ_port == -1) {
|
||||
warnx("%s: -w and -W require -n", __func__);
|
||||
warnx("%s: -w and -W require -p", __func__);
|
||||
retval = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
@ -674,7 +688,8 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
|
|||
return (retval);
|
||||
|
||||
bailout_badarg:
|
||||
warnx("%s: only one of -l, -o or -w/-W may be specified", __func__);
|
||||
warnx("%s: only one of -c, -r, -l, -o or -w/-W may be specified",
|
||||
__func__);
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
|
6
usr.sbin/ctladm/tests/Makefile
Normal file
6
usr.sbin/ctladm/tests/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
PACKAGE= tests
|
||||
|
||||
ATF_TESTS_SH= port
|
||||
|
||||
.include <bsd.test.mk>
|
336
usr.sbin/ctladm/tests/port.sh
Normal file
336
usr.sbin/ctladm/tests/port.sh
Normal file
|
@ -0,0 +1,336 @@
|
|||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
#
|
||||
# Copyright (c) 2024 Axcient
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Things that aren't tested due to lack of kernel support:
|
||||
# * Creating camsim ports
|
||||
# * Creating tpc ports
|
||||
# * Creating camtgt ports
|
||||
# * Creating umass ports
|
||||
|
||||
# TODO
|
||||
# * Creating nvmf ports
|
||||
# * Creating ha ports
|
||||
# * Creating fc ports
|
||||
|
||||
# The PGTAG can be any 16-bit number. The only constraint is that each
|
||||
# PGTAG,TARGET pair must be globally unique.
|
||||
PGTAG=30257
|
||||
|
||||
load_cfiscsi() {
|
||||
if ! kldstat -q -m cfiscsi; then
|
||||
kldload cfiscsi || atf_skip "could not load cfscsi kernel mod"
|
||||
fi
|
||||
}
|
||||
|
||||
skip_if_ctld() {
|
||||
if service ctld onestatus > /dev/null; then
|
||||
# If ctld is running on this server, let's not interfere.
|
||||
atf_skip "Cannot run this test while ctld is running"
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
driver=$1
|
||||
|
||||
if [ -e port-create.txt ]; then
|
||||
case "$driver" in
|
||||
"ioctl")
|
||||
PORTNUM=`awk '/port:/ {print $2}' port-create.txt`
|
||||
ctladm port -r -d $driver -p $PORTNUM
|
||||
;;
|
||||
"iscsi")
|
||||
TARGET=`awk '/target:/ {print $2}' port-create.txt`
|
||||
ctladm port -r -d $driver -p "$PORTNUM" -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target=$TARGET
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
atf_test_case create_ioctl cleanup
|
||||
create_ioctl_head()
|
||||
{
|
||||
atf_set "descr" "ctladm can create a new ioctl port"
|
||||
atf_set "require.user" "root"
|
||||
}
|
||||
create_ioctl_body()
|
||||
{
|
||||
skip_if_ctld
|
||||
|
||||
atf_check -o save:port-create.txt ctladm port -c -d "ioctl"
|
||||
atf_check egrep -q "Port created successfully" port-create.txt
|
||||
atf_check egrep -q "frontend: *ioctl" port-create.txt
|
||||
atf_check egrep -q "port: *[0-9]+" port-create.txt
|
||||
portnum=`awk '/port:/ {print $2}' port-create.txt`
|
||||
atf_check -o save:portlist.txt ctladm portlist -qf ioctl
|
||||
atf_check egrep -q "$portnum *YES *ioctl *ioctl" portlist.txt
|
||||
}
|
||||
create_ioctl_cleanup()
|
||||
{
|
||||
cleanup ioctl
|
||||
}
|
||||
|
||||
atf_test_case remove_ioctl_without_required_args cleanup
|
||||
remove_ioctl_without_required_args_head()
|
||||
{
|
||||
atf_set "descr" "ctladm will gracefully fail to remove an ioctl target if required arguments are missing"
|
||||
atf_set "require.user" "root"
|
||||
}
|
||||
remove_ioctl_without_required_args_body()
|
||||
{
|
||||
skip_if_ctld
|
||||
|
||||
atf_check -o save:port-create.txt ctladm port -c -d "ioctl"
|
||||
atf_check egrep -q "Port created successfully" port-create.txt
|
||||
atf_check -s exit:1 -e match:"Missing required argument: port_id" ctladm port -r -d "ioctl"
|
||||
}
|
||||
remove_ioctl_without_required_args_cleanup()
|
||||
{
|
||||
cleanup ioctl
|
||||
}
|
||||
|
||||
atf_test_case create_iscsi cleanup
|
||||
create_iscsi_head()
|
||||
{
|
||||
atf_set "descr" "ctladm can create a new iscsi port"
|
||||
atf_set "require.user" "root"
|
||||
}
|
||||
create_iscsi_body()
|
||||
{
|
||||
skip_if_ctld
|
||||
load_cfiscsi
|
||||
|
||||
TARGET=iqn.2018-10.myhost.create_iscsi
|
||||
atf_check -o save:port-create.txt ctladm port -c -d "iscsi" -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target="$TARGET"
|
||||
echo "target: $TARGET" >> port-create.txt
|
||||
atf_check egrep -q "Port created successfully" port-create.txt
|
||||
atf_check egrep -q "frontend: *iscsi" port-create.txt
|
||||
atf_check egrep -q "port: *[0-9]+" port-create.txt
|
||||
atf_check -o save:portlist.txt ctladm portlist -qf iscsi
|
||||
# Unlike the ioctl driver, the iscsi driver creates ports in a disabled
|
||||
# state, so the port's lunmap may be set before enabling it.
|
||||
atf_check egrep -q "$portnum *NO *iscsi *iscsi.*$TARGET" portlist.txt
|
||||
}
|
||||
create_iscsi_cleanup()
|
||||
{
|
||||
cleanup iscsi
|
||||
}
|
||||
|
||||
atf_test_case create_iscsi_alias cleanup
|
||||
create_iscsi_alias_head()
|
||||
{
|
||||
atf_set "descr" "ctladm can create a new iscsi port with a target alias"
|
||||
atf_set "require.user" "root"
|
||||
}
|
||||
create_iscsi_alias_body()
|
||||
{
|
||||
skip_if_ctld
|
||||
load_cfiscsi
|
||||
|
||||
TARGET=iqn.2018-10.myhost.create_iscsi_alias
|
||||
ALIAS="foobar"
|
||||
atf_check -o save:port-create.txt ctladm port -c -d "iscsi" -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target="$TARGET" -O cfiscsi_target_alias="$ALIAS"
|
||||
echo "target: $TARGET" >> port-create.txt
|
||||
atf_check egrep -q "Port created successfully" port-create.txt
|
||||
atf_check egrep -q "frontend: *iscsi" port-create.txt
|
||||
atf_check egrep -q "port: *[0-9]+" port-create.txt
|
||||
atf_check -o save:portlist.txt ctladm portlist -qvf iscsi
|
||||
atf_check egrep -q "cfiscsi_target_alias=$ALIAS" portlist.txt
|
||||
}
|
||||
create_iscsi_alias_cleanup()
|
||||
{
|
||||
cleanup iscsi
|
||||
}
|
||||
|
||||
atf_test_case create_iscsi_without_required_args
|
||||
create_iscsi_without_required_args_head()
|
||||
{
|
||||
atf_set "descr" "ctladm will gracefully fail to create an iSCSI target if required arguments are missing"
|
||||
atf_set "require.user" "root"
|
||||
}
|
||||
create_iscsi_without_required_args_body()
|
||||
{
|
||||
skip_if_ctld
|
||||
load_cfiscsi
|
||||
|
||||
TARGET=iqn.2018-10.myhost.create_iscsi
|
||||
atf_check -s exit:1 -e match:"Missing required argument: cfiscsi_target" ctladm port -c -d "iscsi" -O cfiscsi_portal_group_tag=$PGTAG
|
||||
atf_check -s exit:1 -e match:"Missing required argument: cfiscsi_portal_group_tag" ctladm port -c -d "iscsi" -O cfiscsi_target=$TARGET
|
||||
}
|
||||
|
||||
atf_test_case create_ioctl_options cleanup
|
||||
create_ioctl_options_head()
|
||||
{
|
||||
atf_set "descr" "ctladm can set options when creating a new ioctl port"
|
||||
atf_set "require.user" "root"
|
||||
}
|
||||
create_ioctl_options_body()
|
||||
{
|
||||
skip_if_ctld
|
||||
|
||||
atf_check -o save:port-create.txt ctladm port -c -d "ioctl" -O pp=101 -O vp=102
|
||||
atf_check egrep -q "Port created successfully" port-create.txt
|
||||
atf_check egrep -q "frontend: *ioctl" port-create.txt
|
||||
atf_check egrep -q "port: *[0-9]+" port-create.txt
|
||||
portnum=`awk '/port:/ {print $2}' port-create.txt`
|
||||
atf_check -o save:portlist.txt ctladm portlist -qf ioctl
|
||||
if ! egrep -q '101[[:space:]]+102' portlist.txt; then
|
||||
ctladm portlist
|
||||
atf_fail "Did not create the port with the specified options"
|
||||
fi
|
||||
}
|
||||
create_ioctl_options_cleanup()
|
||||
{
|
||||
cleanup ioctl
|
||||
}
|
||||
|
||||
|
||||
atf_test_case disable_ioctl cleanup
|
||||
disable_ioctl_head()
|
||||
{
|
||||
atf_set "descr" "ctladm can disable an ioctl port"
|
||||
atf_set "require.user" "root"
|
||||
}
|
||||
disable_ioctl_body()
|
||||
{
|
||||
skip_if_ctld
|
||||
|
||||
atf_check -o save:port-create.txt ctladm port -c -d "ioctl"
|
||||
portnum=`awk '/port:/ {print $2}' port-create.txt`
|
||||
atf_check -o save:portlist.txt ctladm portlist -qf ioctl
|
||||
atf_check -o ignore ctladm port -o off -p $portnum
|
||||
atf_check -o match:"^$portnum *NO" ctladm portlist -qf ioctl
|
||||
}
|
||||
disable_ioctl_cleanup()
|
||||
{
|
||||
cleanup ioctl
|
||||
}
|
||||
|
||||
atf_test_case enable_ioctl cleanup
|
||||
enable_ioctl_head()
|
||||
{
|
||||
atf_set "descr" "ctladm can enable an ioctl port"
|
||||
atf_set "require.user" "root"
|
||||
}
|
||||
enable_ioctl_body()
|
||||
{
|
||||
skip_if_ctld
|
||||
|
||||
atf_check -o save:port-create.txt ctladm port -c -d "ioctl"
|
||||
portnum=`awk '/port:/ {print $2}' port-create.txt`
|
||||
atf_check -o save:portlist.txt ctladm portlist -qf ioctl
|
||||
atf_check -o ignore ctladm port -o off -p $portnum
|
||||
atf_check -o ignore ctladm port -o on -p $portnum
|
||||
atf_check -o match:"^$portnum *YES" ctladm portlist -qf ioctl
|
||||
}
|
||||
enable_ioctl_cleanup()
|
||||
{
|
||||
cleanup ioctl
|
||||
}
|
||||
|
||||
atf_test_case remove_ioctl
|
||||
remove_ioctl_head()
|
||||
{
|
||||
atf_set "descr" "ctladm can remove an ioctl port"
|
||||
atf_set "require.user" "root"
|
||||
}
|
||||
remove_ioctl_body()
|
||||
{
|
||||
skip_if_ctld
|
||||
|
||||
# Specify exact pp and vp to make the post-removal portlist check
|
||||
# unambiguous
|
||||
atf_check -o save:port-create.txt ctladm port -c -d "ioctl" -O pp=10001 -O vp=10002
|
||||
portnum=`awk '/port:/ {print $2}' port-create.txt`
|
||||
atf_check -o save:portlist.txt ctladm portlist -qf ioctl
|
||||
atf_check -o inline:"Port destroyed successfully\n" ctladm port -r -d ioctl -p $portnum
|
||||
# Check that the port was removed. A new port may have been added with
|
||||
# the same ID, so match against the pp and vp numbers, too.
|
||||
if ctladm portlist -qf ioctl | egrep -q "^${portnum} .*10001 *10002"; then
|
||||
ctladm portlist -qf ioctl
|
||||
atf_fail "port was not removed"
|
||||
fi
|
||||
}
|
||||
|
||||
atf_test_case remove_iscsi
|
||||
remove_iscsi_head()
|
||||
{
|
||||
atf_set "descr" "ctladm can remove an iscsi port"
|
||||
atf_set "require.user" "root"
|
||||
}
|
||||
remove_iscsi_body()
|
||||
{
|
||||
skip_if_ctld
|
||||
load_cfiscsi
|
||||
|
||||
TARGET=iqn.2018-10.myhost.remove_iscsi
|
||||
atf_check -o save:port-create.txt ctladm port -c -d "iscsi" -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target="$TARGET"
|
||||
portnum=`awk '/port:/ {print $2}' port-create.txt`
|
||||
atf_check -o save:portlist.txt ctladm portlist -qf iscsi
|
||||
atf_check -o inline:"Port destroyed successfully\n" ctladm port -r -d iscsi -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target="$TARGET"
|
||||
# Check that the port was removed. A new port may have been added with
|
||||
# the same ID, so match against the target and tag, too.
|
||||
PGTAGHEX=0x7631 # PGTAG in hex
|
||||
if ctladm portlist -qf iscsi | egrep -q "^${portnum} .*$PGTAG +[0-9]+ +$TARGET,t,$PGTAGHEX"; then
|
||||
ctladm portlist -qf iscsi
|
||||
atf_fail "port was not removed"
|
||||
fi
|
||||
}
|
||||
|
||||
atf_test_case remove_iscsi_without_required_args cleanup
|
||||
remove_iscsi_without_required_args_head()
|
||||
{
|
||||
atf_set "descr" "ctladm will gracefully fail to remove an iSCSI target if required arguments are missing"
|
||||
atf_set "require.user" "root"
|
||||
}
|
||||
remove_iscsi_without_required_args_body()
|
||||
{
|
||||
skip_if_ctld
|
||||
load_cfiscsi
|
||||
|
||||
TARGET=iqn.2018-10.myhost.remove_iscsi_without_required_args
|
||||
atf_check -o save:port-create.txt ctladm port -c -d "iscsi" -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target="$TARGET"
|
||||
echo "target: $TARGET" >> port-create.txt
|
||||
atf_check -s exit:1 -e match:"Missing required argument: cfiscsi_portal_group_tag" ctladm port -r -d iscsi -O cfiscsi_target="$TARGET"
|
||||
atf_check -s exit:1 -e match:"Missing required argument: cfiscsi_target" ctladm port -r -d iscsi -O cfiscsi_portal_group_tag=$PGTAG
|
||||
}
|
||||
remove_iscsi_without_required_args_cleanup()
|
||||
{
|
||||
cleanup iscsi
|
||||
}
|
||||
|
||||
atf_init_test_cases()
|
||||
{
|
||||
atf_add_test_case create_ioctl
|
||||
atf_add_test_case create_iscsi
|
||||
atf_add_test_case create_iscsi_without_required_args
|
||||
atf_add_test_case create_iscsi_alias
|
||||
atf_add_test_case create_ioctl_options
|
||||
atf_add_test_case disable_ioctl
|
||||
atf_add_test_case enable_ioctl
|
||||
atf_add_test_case remove_ioctl
|
||||
atf_add_test_case remove_ioctl_without_required_args
|
||||
atf_add_test_case remove_iscsi
|
||||
atf_add_test_case remove_iscsi_without_required_args
|
||||
}
|
|
@ -2874,6 +2874,7 @@ main(int argc, char **argv)
|
|||
error = conf_apply(oldconf, newconf);
|
||||
if (error != 0)
|
||||
log_warnx("failed to apply configuration");
|
||||
conf_delete(newconf);
|
||||
conf_delete(oldconf);
|
||||
oldconf = NULL;
|
||||
|
||||
|
|
|
@ -615,6 +615,22 @@ conf_new_from_kernel(void)
|
|||
}
|
||||
cp->p_ctl_port = port->port_id;
|
||||
}
|
||||
while ((port = STAILQ_FIRST(&devlist.port_list))) {
|
||||
struct cctl_lun_nv *nv;
|
||||
|
||||
STAILQ_REMOVE_HEAD(&devlist.port_list, links);
|
||||
free(port->port_frontend);
|
||||
free(port->port_name);
|
||||
free(port->cfiscsi_target);
|
||||
free(port->ctld_portal_group_name);
|
||||
while ((nv = STAILQ_FIRST(&port->attr_list))) {
|
||||
STAILQ_REMOVE_HEAD(&port->attr_list, links);
|
||||
free(nv->value);
|
||||
free(nv->name);
|
||||
free(nv);
|
||||
}
|
||||
free(port);
|
||||
}
|
||||
free(name);
|
||||
|
||||
STAILQ_FOREACH(lun, &devlist.lun_list, links) {
|
||||
|
@ -665,6 +681,18 @@ conf_new_from_kernel(void)
|
|||
cl->l_name);
|
||||
}
|
||||
}
|
||||
while ((lun = STAILQ_FIRST(&devlist.lun_list))) {
|
||||
struct cctl_lun_nv *nv;
|
||||
|
||||
STAILQ_REMOVE_HEAD(&devlist.lun_list, links);
|
||||
while ((nv = STAILQ_FIRST(&lun->attr_list))) {
|
||||
STAILQ_REMOVE_HEAD(&lun->attr_list, links);
|
||||
free(nv->value);
|
||||
free(nv->name);
|
||||
free(nv);
|
||||
}
|
||||
free(lun);
|
||||
}
|
||||
|
||||
return (conf);
|
||||
}
|
||||
|
@ -742,12 +770,14 @@ kernel_lun_add(struct lun *lun)
|
|||
|
||||
req.args = nvlist_pack(req.args_nvl, &req.args_len);
|
||||
if (req.args == NULL) {
|
||||
nvlist_destroy(req.args_nvl);
|
||||
log_warn("error packing nvlist");
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
|
||||
free(req.args);
|
||||
nvlist_destroy(req.args_nvl);
|
||||
|
||||
if (error != 0) {
|
||||
|
@ -825,12 +855,14 @@ kernel_lun_modify(struct lun *lun)
|
|||
|
||||
req.args = nvlist_pack(req.args_nvl, &req.args_len);
|
||||
if (req.args == NULL) {
|
||||
nvlist_destroy(req.args_nvl);
|
||||
log_warn("error packing nvlist");
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
|
||||
free(req.args);
|
||||
nvlist_destroy(req.args_nvl);
|
||||
|
||||
if (error != 0) {
|
||||
|
@ -1053,6 +1085,7 @@ kernel_port_add(struct port *port)
|
|||
|
||||
req.args = nvlist_pack(req.args_nvl, &req.args_len);
|
||||
if (req.args == NULL) {
|
||||
nvlist_destroy(req.args_nvl);
|
||||
log_warn("error packing nvlist");
|
||||
return (1);
|
||||
}
|
||||
|
@ -1060,6 +1093,7 @@ kernel_port_add(struct port *port)
|
|||
req.result = result_buf;
|
||||
req.result_len = sizeof(result_buf);
|
||||
error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
|
||||
free(req.args);
|
||||
nvlist_destroy(req.args_nvl);
|
||||
|
||||
if (error != 0) {
|
||||
|
@ -1203,11 +1237,13 @@ kernel_port_remove(struct port *port)
|
|||
|
||||
req.args = nvlist_pack(req.args_nvl, &req.args_len);
|
||||
if (req.args == NULL) {
|
||||
nvlist_destroy(req.args_nvl);
|
||||
log_warn("error packing nvlist");
|
||||
return (1);
|
||||
}
|
||||
|
||||
error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
|
||||
free(req.args);
|
||||
nvlist_destroy(req.args_nvl);
|
||||
|
||||
if (error != 0) {
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd March 12, 2023
|
||||
.Dd June 24, 2024
|
||||
.Dt JAIL 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -757,7 +757,15 @@ the jail cannot perform any sysvmsg-related system calls.
|
|||
.It Va sysvsem, sysvshm
|
||||
Allow access to SYSV IPC semaphore and shared memory primitives, in the
|
||||
same manner as
|
||||
.Va sysvmsg.
|
||||
.Va sysvmsg .
|
||||
.It Va zfs.mount_snapshot
|
||||
Allow jailed users to access the contents of ZFS snapshots under the
|
||||
filesystem's
|
||||
.Pa .zfs
|
||||
directory.
|
||||
If
|
||||
.Va allow.mount.zfs
|
||||
is set, the snapshots may also be mounted.
|
||||
.El
|
||||
.Pp
|
||||
There are pseudo-parameters that are not passed to the kernel, but are
|
||||
|
|
Loading…
Reference in a new issue