sahlberg / libiscsi

iscsi client library and utilities
Other
192 stars 165 forks source link

lib: reserve the fd on reconnect #432

Closed tianrenz2 closed 3 days ago

tianrenz2 commented 6 days ago

On reconnect case, the iscsi_tcp_connect tries to reuse the fd number of old_iscsi. However, this fd could have been already closed in previous iscsi_tcp_disconnect if iscsi->fd == iscsi->old_iscsi->fd and the fd number might have been allocated to some other caller, in this case the fd reuse in iscsi_tcp_connect is not safe anymore.

Solve this by not closing the fd if iscsi and old_iscsi share the same fd on reconnect to "really" reserve this fd number.

tianrenz2 commented 6 days ago

Here is a script below that could quickly reproduce this issue, when you see any socket connect error output, then it's likely the bug happens, replace xxxxxxx based on your environment :)

Another possible prerequisite: on each reconnect, the iscsi target should return SCSI_STATUS_REDIRECT on the login thus invoke the second connection establishment.

#include <iscsi/iscsi.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

// Function executed by the iSCSI thread
void *iscsi_thread(void *arg) {
    struct iscsi_context *iscsi;
    char *target = "xxxxxxx";
    char *initiator = "xxxxxxxx";
    struct iscsi_url *iscsi_url;
    iscsi = iscsi_create_context(initiator);

    iscsi_url = iscsi_parse_full_url(iscsi, "xxxxxxx");

    iscsi_set_targetname(iscsi, iscsi_url->target);
    iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL);
    iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
    iscsi_set_timeout(iscsi, 5);

    iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun);

    while (1) {
        iscsi_reconnect_sync(iscsi);
    }
    return NULL;
}

// Function executed by the open file thread
void *file_thread(void *arg) {
    while (1) {
        int fd = open("xxxxxxxxx", O_CREAT | O_RDWR, 0644);
        if (fd == -1) {
            perror("[Thread 2] Error opening file");
        } else {
            // printf("[Thread 2] File opened successfully (fd: %d)\n", fd);
            close(fd);
        }
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // Create iSCSI reconnect thread
    if (pthread_create(&thread1, NULL, iscsi_thread, NULL) != 0) {
        perror("Failed to create iSCSI thread");
        return 1;
    }

    // Create file opening thread
    if (pthread_create(&thread2, NULL, file_thread, NULL) != 0) {
        perror("Failed to create file thread");
        return 1;
    }

    // Wait for threads to finish (infinite loop in this case)
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    return 0;
}
sahlberg commented 3 days ago

Merged. Thanks!