interrupt.cc (d26d63a4af4f693b23f5ac389cc6e0d8938cd96b) interrupt.cc (ed74f781c9f704092556f860a00b0bb53fdedff7)
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2019 The FreeBSD Foundation
5 *
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
8 *

--- 171 unchanged lines hidden (view full) ---

180
181 sem_destroy(blocked_semaphore);
182 munmap(blocked_semaphore, sizeof(*blocked_semaphore));
183
184 FuseTest::TearDown();
185}
186};
187
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2019 The FreeBSD Foundation
5 *
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
8 *

--- 171 unchanged lines hidden (view full) ---

180
181 sem_destroy(blocked_semaphore);
182 munmap(blocked_semaphore, sizeof(*blocked_semaphore));
183
184 FuseTest::TearDown();
185}
186};
187
188class Intr: public Interrupt {};
189
190class Nointr: public Interrupt {
191 void SetUp() {
192 m_nointr = true;
193 Interrupt::SetUp();
194 }
195};
196
188static void* mkdir0(void* arg __unused) {
189 ssize_t r;
190
191 r = mkdir(FULLDIRPATH0, MODE);
192 if (r >= 0)
193 return 0;
194 else
195 return (void*)(intptr_t)errno;

--- 12 unchanged lines hidden (view full) ---

208 return (void*)(intptr_t)errno;
209}
210
211/*
212 * An interrupt operation that gets received after the original command is
213 * complete should generate an EAGAIN response.
214 */
215/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
197static void* mkdir0(void* arg __unused) {
198 ssize_t r;
199
200 r = mkdir(FULLDIRPATH0, MODE);
201 if (r >= 0)
202 return 0;
203 else
204 return (void*)(intptr_t)errno;

--- 12 unchanged lines hidden (view full) ---

217 return (void*)(intptr_t)errno;
218}
219
220/*
221 * An interrupt operation that gets received after the original command is
222 * complete should generate an EAGAIN response.
223 */
224/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
216TEST_F(Interrupt, already_complete)
225TEST_F(Intr, already_complete)
217{
218 uint64_t ino = 42;
219 pthread_t self;
220 uint64_t mkdir_unique = 0;
221 Sequence seq;
222
223 self = pthread_self();
224

--- 41 unchanged lines hidden (view full) ---

266 EXPECT_EQ(0, access(FULLDIRPATH0, F_OK)) << strerror(errno);
267}
268
269/*
270 * If a FUSE file system returns ENOSYS for a FUSE_INTERRUPT operation, the
271 * kernel should not attempt to interrupt any other operations on that mount
272 * point.
273 */
226{
227 uint64_t ino = 42;
228 pthread_t self;
229 uint64_t mkdir_unique = 0;
230 Sequence seq;
231
232 self = pthread_self();
233

--- 41 unchanged lines hidden (view full) ---

275 EXPECT_EQ(0, access(FULLDIRPATH0, F_OK)) << strerror(errno);
276}
277
278/*
279 * If a FUSE file system returns ENOSYS for a FUSE_INTERRUPT operation, the
280 * kernel should not attempt to interrupt any other operations on that mount
281 * point.
282 */
274TEST_F(Interrupt, enosys)
283TEST_F(Intr, enosys)
275{
276 uint64_t ino0 = 42, ino1 = 43;;
277 uint64_t mkdir_unique;
278 pthread_t self, th0;
279 sem_t sem0, sem1;
280 void *thr0_value;
281 Sequence seq;
282

--- 68 unchanged lines hidden (view full) ---

351 EXPECT_EQ(0, (intptr_t)thr0_value);
352}
353
354/*
355 * A FUSE filesystem is legally allowed to ignore INTERRUPT operations, and
356 * complete the original operation whenever it damn well pleases.
357 */
358/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
284{
285 uint64_t ino0 = 42, ino1 = 43;;
286 uint64_t mkdir_unique;
287 pthread_t self, th0;
288 sem_t sem0, sem1;
289 void *thr0_value;
290 Sequence seq;
291

--- 68 unchanged lines hidden (view full) ---

360 EXPECT_EQ(0, (intptr_t)thr0_value);
361}
362
363/*
364 * A FUSE filesystem is legally allowed to ignore INTERRUPT operations, and
365 * complete the original operation whenever it damn well pleases.
366 */
367/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
359TEST_F(Interrupt, ignore)
368TEST_F(Intr, ignore)
360{
361 uint64_t ino = 42;
362 pthread_t self;
363 uint64_t mkdir_unique;
364
365 self = pthread_self();
366
367 EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)

--- 19 unchanged lines hidden (view full) ---

387 ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
388}
389
390/*
391 * A restartable operation (basically, anything except write or setextattr)
392 * that hasn't yet been sent to userland can be interrupted without sending
393 * FUSE_INTERRUPT, and will be automatically restarted.
394 */
369{
370 uint64_t ino = 42;
371 pthread_t self;
372 uint64_t mkdir_unique;
373
374 self = pthread_self();
375
376 EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)

--- 19 unchanged lines hidden (view full) ---

396 ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
397}
398
399/*
400 * A restartable operation (basically, anything except write or setextattr)
401 * that hasn't yet been sent to userland can be interrupted without sending
402 * FUSE_INTERRUPT, and will be automatically restarted.
403 */
395TEST_F(Interrupt, in_kernel_restartable)
404TEST_F(Intr, in_kernel_restartable)
396{
397 const char FULLPATH1[] = "mountpoint/other_file.txt";
398 const char RELPATH1[] = "other_file.txt";
399 uint64_t ino0 = 42, ino1 = 43;
400 int fd1;
401 pthread_t self, th0, th1;
402 sem_t sem0, sem1;
403 void *thr0_value, *thr1_value;

--- 53 unchanged lines hidden (view full) ---

457 sem_destroy(&sem0);
458}
459
460/*
461 * An operation that hasn't yet been sent to userland can be interrupted
462 * without sending FUSE_INTERRUPT. If it's a non-restartable operation (write
463 * or setextattr) it will return EINTR.
464 */
405{
406 const char FULLPATH1[] = "mountpoint/other_file.txt";
407 const char RELPATH1[] = "other_file.txt";
408 uint64_t ino0 = 42, ino1 = 43;
409 int fd1;
410 pthread_t self, th0, th1;
411 sem_t sem0, sem1;
412 void *thr0_value, *thr1_value;

--- 53 unchanged lines hidden (view full) ---

466 sem_destroy(&sem0);
467}
468
469/*
470 * An operation that hasn't yet been sent to userland can be interrupted
471 * without sending FUSE_INTERRUPT. If it's a non-restartable operation (write
472 * or setextattr) it will return EINTR.
473 */
465TEST_F(Interrupt, in_kernel_nonrestartable)
474TEST_F(Intr, in_kernel_nonrestartable)
466{
467 const char FULLPATH1[] = "mountpoint/other_file.txt";
468 const char RELPATH1[] = "other_file.txt";
469 const char value[] = "whatever";
470 ssize_t value_len = strlen(value) + 1;
471 uint64_t ino0 = 42, ino1 = 43;
472 int ns = EXTATTR_NAMESPACE_USER;
473 int fd1;

--- 54 unchanged lines hidden (view full) ---

528
529/*
530 * A syscall that gets interrupted while blocking on FUSE I/O should send a
531 * FUSE_INTERRUPT command to the fuse filesystem, which should then send EINTR
532 * in response to the _original_ operation. The kernel should ultimately
533 * return EINTR to userspace
534 */
535/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
475{
476 const char FULLPATH1[] = "mountpoint/other_file.txt";
477 const char RELPATH1[] = "other_file.txt";
478 const char value[] = "whatever";
479 ssize_t value_len = strlen(value) + 1;
480 uint64_t ino0 = 42, ino1 = 43;
481 int ns = EXTATTR_NAMESPACE_USER;
482 int fd1;

--- 54 unchanged lines hidden (view full) ---

537
538/*
539 * A syscall that gets interrupted while blocking on FUSE I/O should send a
540 * FUSE_INTERRUPT command to the fuse filesystem, which should then send EINTR
541 * in response to the _original_ operation. The kernel should ultimately
542 * return EINTR to userspace
543 */
544/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
536TEST_F(Interrupt, in_progress)
545TEST_F(Intr, in_progress)
537{
538 pthread_t self;
539 uint64_t mkdir_unique;
540
541 self = pthread_self();
542
543 EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
544 .WillOnce(Invoke(ReturnErrno(ENOENT)));

--- 13 unchanged lines hidden (view full) ---

558 }));
559
560 setup_interruptor(self);
561 ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
562 EXPECT_EQ(EINTR, errno);
563}
564
565/* Reads should also be interruptible */
546{
547 pthread_t self;
548 uint64_t mkdir_unique;
549
550 self = pthread_self();
551
552 EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
553 .WillOnce(Invoke(ReturnErrno(ENOENT)));

--- 13 unchanged lines hidden (view full) ---

567 }));
568
569 setup_interruptor(self);
570 ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
571 EXPECT_EQ(EINTR, errno);
572}
573
574/* Reads should also be interruptible */
566TEST_F(Interrupt, in_progress_read)
575TEST_F(Intr, in_progress_read)
567{
568 const char FULLPATH[] = "mountpoint/some_file.txt";
569 const char RELPATH[] = "some_file.txt";
570 const size_t bufsize = 80;
571 char buf[bufsize];
572 uint64_t ino = 42;
573 int fd;
574 pthread_t self;

--- 21 unchanged lines hidden (view full) ---

596 fd = open(FULLPATH, O_RDONLY);
597 ASSERT_LE(0, fd) << strerror(errno);
598
599 setup_interruptor(self);
600 ASSERT_EQ(-1, read(fd, buf, bufsize));
601 EXPECT_EQ(EINTR, errno);
602}
603
576{
577 const char FULLPATH[] = "mountpoint/some_file.txt";
578 const char RELPATH[] = "some_file.txt";
579 const size_t bufsize = 80;
580 char buf[bufsize];
581 uint64_t ino = 42;
582 int fd;
583 pthread_t self;

--- 21 unchanged lines hidden (view full) ---

605 fd = open(FULLPATH, O_RDONLY);
606 ASSERT_LE(0, fd) << strerror(errno);
607
608 setup_interruptor(self);
609 ASSERT_EQ(-1, read(fd, buf, bufsize));
610 EXPECT_EQ(EINTR, errno);
611}
612
613/*
614 * When mounted with -o nointr, fusefs will block signals while waiting for the
615 * server.
616 */
617TEST_F(Nointr, block)
618{
619 uint64_t ino = 42;
620 pthread_t self;
621 sem_t sem0;
622
623 ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
624 signaled_semaphore = &sem0;
625 self = pthread_self();
626
627 EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
628 .WillOnce(Invoke(ReturnErrno(ENOENT)));
629 EXPECT_CALL(*m_mock, process(
630 ResultOf([=](auto in) {
631 return (in.header.opcode == FUSE_MKDIR);
632 }, Eq(true)),
633 _)
634 ).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
635 /* Let the killer proceed */
636 sem_post(blocked_semaphore);
637
638 /* Wait until after the signal has been sent */
639 sem_wait(signaled_semaphore);
640 /* Allow time for the mkdir thread to receive the signal */
641 nap();
642
643 /* Finally, complete the original op */
644 SET_OUT_HEADER_LEN(out, entry);
645 out.body.create.entry.attr.mode = S_IFDIR | MODE;
646 out.body.create.entry.nodeid = ino;
647 })));
648 EXPECT_CALL(*m_mock, process(
649 ResultOf([&](auto in) {
650 return (in.header.opcode == FUSE_INTERRUPT);
651 }, Eq(true)),
652 _)
653 ).Times(0);
654
655 setup_interruptor(self);
656 ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
657
658 sem_destroy(&sem0);
659}
660
604/* FUSE_INTERRUPT operations should take priority over other pending ops */
661/* FUSE_INTERRUPT operations should take priority over other pending ops */
605TEST_F(Interrupt, priority)
662TEST_F(Intr, priority)
606{
607 Sequence seq;
608 uint64_t ino1 = 43;
609 uint64_t mkdir_unique;
610 pthread_t self, th0;
611 sem_t sem0, sem1;
612
613 ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);

--- 69 unchanged lines hidden (view full) ---

683 * original operation to arrive. If not, it should send EAGAIN to the
684 * INTERRUPT operation, and the kernel should requeue the INTERRUPT.
685 *
686 * In this test, we'll pretend that the INTERRUPT arrives too soon, gets
687 * EAGAINed, then the kernel requeues it, and the second time around it
688 * successfully interrupts the original
689 */
690/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
663{
664 Sequence seq;
665 uint64_t ino1 = 43;
666 uint64_t mkdir_unique;
667 pthread_t self, th0;
668 sem_t sem0, sem1;
669
670 ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);

--- 69 unchanged lines hidden (view full) ---

740 * original operation to arrive. If not, it should send EAGAIN to the
741 * INTERRUPT operation, and the kernel should requeue the INTERRUPT.
742 *
743 * In this test, we'll pretend that the INTERRUPT arrives too soon, gets
744 * EAGAINed, then the kernel requeues it, and the second time around it
745 * successfully interrupts the original
746 */
747/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
691TEST_F(Interrupt, too_soon)
748TEST_F(Intr, too_soon)
692{
693 Sequence seq;
694 pthread_t self;
695 uint64_t mkdir_unique;
696
697 self = pthread_self();
698
699 EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)

--- 34 unchanged lines hidden ---
749{
750 Sequence seq;
751 pthread_t self;
752 uint64_t mkdir_unique;
753
754 self = pthread_self();
755
756 EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)

--- 34 unchanged lines hidden ---