Contenu | Rechercher | Menus

Annonce

DVD, clés USB et t-shirts Ubuntu-fr disponibles sur la boutique En Vente Libre

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#426 Le 27/08/2022, à 19:11

grosjean

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Ah bien vu!
Je teste avec un seul thread, et je te redis.
Merci !

Hors ligne

#427 Le 03/09/2022, à 08:43

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Bonjour @grosjean

Le "use after free" était effectivement assez évident, et il est assez facile à corriger.

Malheureusement il révèle un bug de parallélisme qui est largement plus subtil et va me prendre un peu de réflexion pour être sûr que j'ai fait quelque chose de correct.

En deux mots, le nouveau moteur de lecture fonctionne sans aucun "lock". Comme il n'y a pas de mystère, tout ce qui nécessiterait des "locks" passe par un thread "orchestrateur" qui s'occupe de ça (O_THREAD défini comme 0). Par exemple quand un appel arrive sur un fh (file handle) inconnu, seul l'orchestrateur a la liste des fh connus et peut déterminer que le nouveau est inconnu. A ce moment là l'orchestrateur va choisir un thread pour traiter ce fh et envoyer le flux à ce thread là. Pour éviter les rebonds de threads à chaque lecture successive, il y a un mécanisme de mémorisation pour la lecture.

Pour éviter de gérer des tables de correspondance entre les fh et la liste des fichiers stockés (je simplifie) 1fichierfs utilise simplement l'adresse mémoire du fichier ouvert comme fh.
Or dans ton cas les adresses mémoire ont été réutilisées.
Le cas est bien prévu.
En effet, quand un fichier est ouvert, avant tout usage de celui-ci, 1fichierfs envoie un "close" explicite avec ce fh (donc une adresse mémoire).
Ce close explicite (aw_close dans le nouveau moteur) sert précisément à dire : s'il y avait des choses ouvertes avec ce fh, il faut les oublier car désormais c'est un fichier au contenu différent qui possède ce fh.

Le close explicite est toujours géré par l'orchestrateur pour les raisons ci-dessus. Il va donc déjà regarder si le fh en question existait déjà, si ce n'est pas le cas, il n'y a rien à faire (rien à oublier) puisqu'on ne connaissait pas ce fh.
Si c'est le cas, soit ce fh était servi par l'orchestrateur lui-même, car l'orchestrateur peut aussi faire des lectures comme les autres threads, et dans ce cas on est content puisqu'on est dans le même thread on peut nettoyer sans locks, soit c'est un autre thread.
Là était le bug, parce que dans ce dernier cas, on passe alors le "close" à cet autre thread, mais le code existant libère aussi la mémoire (free) de l'action "close" qu'on vient de passer. Et donc que le thread qui traitait le fh reçoit l'action, celle-ci a déjà été libérée.

Cette correction là est assez facile : on ne fait pas le free() dans ce cas-là.

Mais on tombe alors dans un micmac de parallélisme !
En effet, il n'y a pas d'instruction pour activement fermer un stream/fichier (à part la sortie du programme, fonction destroy() de fuse). Les streams se ferment tout seuls après le time-out défini (environ une minute pour 1fichier) et lorsqu'un thread ferme le dernier stream d'un fichier (fh), il prévient l'orchestrateur par l'action ACTION_FREE pour lui dire qu'il n'utilise plus ce fh.

Le micmac est que si par le plus grand des hasards (et tout hasard arrive un jour en parallélisme !) l'ordre "close" navigue de l'orchestrateur au thread 1 (pour l'exemple) et juste en même temps le thread 1 envoie un ACTION_FREE à l'orchestrateur, on se trouve dans un état totalement incohérent...

Ajouter à cela que pour des raisons de performance aw_close et donc l'action "close" (ACTION_CLOSE) est non bloquant dans le sens où ça rend la main tout de suite à l'appelant et l'action est faite en parallèle. aw_close est appelé dans la fonction open() de fuse. Les actions de lecture sont appelées normalement dans les fonctions read() et read_buf() de fuse. C'est pourquoi la boucle do_action() traite les "close" en premier, pour le cas où open() et read() se soit succédés rapidement avant que l'action "close" n'ait été traitée en parallèle. On rajoute donc à cette complexité le fait que le "close" peut en réalité être déporté sur un autre thread qui vit aussi sa vie et peut en parallèle avoir aussi décidé qu'il n'a plus besoin de ce fh (ACTION_FREE). mad

Le parallélisme est décidément un joli puzzle, et là il y a des pièces pas tout à fait alignées encore !

Tout ce micmac de parallélisme (et aussi le "use after free") ne se produit pas si tu tournes sur un seul thread. C'est avantageux pour les ressources, mais peut faire perdre en performance de lecture. Dans ton cas puisqu'on est sur des accès presque aléatoires sur une indexation à l'intérieur de fichiers, ça a très peu d'importance par rapport au "coût" d'ouvrir des streams différents pour lire à des endroits différents du fichier.

Dernière modification par Zakhar (Le 03/09/2022, à 08:54)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#428 Le 05/09/2022, à 07:19

grosjean

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Excellent, merci pour l'explication.
J'ai lu une bonne partie du code, bien fait, mais je me suis un peu perdu dans les threads.
Et ma connaissance interne de la librairie fuse est limitee.
Effectivement, avec un seul thread, plus de crash, par contre toujours le probleme de la boucle, du coup un thread bloque a 100% de cpu.
Je continue d'investiguer.

Hors ligne

#429 Le 05/09/2022, à 08:00

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Oui sans doute que la "boucle" est un autre bug, pas encore eu le temps de regarder entre la fin des vacances et mon chantier de rénovation. L'idée mûrit pour la réparation propre du crash "use after free". cool

Juste une astuce pour la prochaine trace, je te conseille d'activer les statistiques :

$ man 1fichierfs

(...)
       --stat-file=nom_du_fichier
              Fichier virtuel qui contient les statistiques en temps réel de 1fichierfs. Les mêmes règles que pour --refresh-file s'appliquent. Voir la section plus haut pour le détail des statistiques. Si
              l'option n'est pas précisée, les statistiques ne sont pas collectées. Lorsque l'exécutable a été construit avec l'option NOSTATS=1 pour le rendre plus léger, cette option n'est pas disponible
              et retournera une erreur.

Outre que l'examen des stats peut donner une piste/idée... et que c'est joli... le fait de mettre les statistiques rajoute des points d'entrée/sortie dans la sortie debug qui peuvent aider.
Le build standard est fait avec les statistiques, mais si on ne les active pas, les appels de toutes les fonctions statistiques sont "bouchonnés" (remplacés par des fonctions qui ne font rien) et ne sont pas non plus tracés dans le debug.

Le fichier virtuel de statistiques sera situé à la racine du montage.

Il suffit de faire un "cat" pour examiner les statistiques.

Personnellement je fais, dans un terminal :

$ watch -n3 cat ~/1fichier/.stats

Toutes les 3,0s: cat /home/zakhar/1fichier/.stats

Uptime: 02:43:03

Readers:
    Files    Streams  Start streams             Fuse requests
   Cur/Tot.  Cur/Tot. AvgTim MaxTim Down. N.Req AvgTim MaxTim Speed Av.Sp Over.
01: 0     6   0     6 .19139 .25044 1440K    24 .04359 .05989     0 1047K     0
02: 1    11   1    25 .13861 .87861 91.5G  732K .00139 .33703 22.3M 79.3M 1512K
└─(   1) 2/0  1     1 .19468 .19468  422M  3380 .00034 .06332 22.3M 33.7M     0
03: 1     9   1     9 .26284 1.0291  259M  2093 .00103 .09513 19.5M 20.9M     0
└─(   2) 2/0  1     1 .19187 .19187  257M  2063 .00043 .09513 19.5M 24.5M     0
04: 0     7   0   183 .07661 .19271 2008M 16626 .00419 .20468     0 12.3M 4440K
05: 0     6   0     6 .18436 .20007 1440K    24 .04366 .05968     0 1047K     0
06: 0     6   0     6 .20482 .26823 1440K    24 .04336 .05899     0 1152K     0
-------------------------------------------------------------------------------
Tt  2    45   2   235 .09929 1.0291 93.7G  750K .00146 .33703 41.8M 71.1M 5952K

Writers:
    Upload N.Req  Avg Time  Max Time   Ref     N.Err Speed Av.Sp
[01] 22.8G 30.4M    0.0000    3.3031  idle         0     0 16.6M
[02] 35.6G 47.5M    0.0000    3.1901  idle         0     0 19.1M
----------------------------------------------------------------
Tot. 58.5G 77.9M    0.0000    3.3031               0     0 20.0M

API:
                        N.Req  Avg Time  Max Time Retry N.Err
folder/ls.cgi         :   120    0.1209    0.4083     0     0
download/get_token.cgi:    41    0.1096    0.3347     0     0
user/info.cgi         :    11    0.0997    0.1105     0     0
file/mv.cgi           :     2    0.0954    0.0975     0     0
file/rm.cgi           :     1    0.0942    0.0942     0     0
folder/rm.cgi         :     2    0.1684    0.2415     0     0
folder/mkdir.cgi      :     2    0.2200    0.3313     0     0
ftp/users/ls.cgi      :     1    0.1181    0.1181     0     0
ftp/process.cgi       :     2    0.0936    0.0952     0     0
--------------------------------------------------------------
Total                 :   182    0.1180    0.4083     0     0

         Trig. Timer Poll. Write   API Error  Auto   Total
Refresh:     0     8    30     3     4     0     0      45

             Number Memory
Allocations:  2298K  96.5G
Frees      :  2298K  96.5G
Difference :    247  1015K
Contentions:     41      1

Live streams: |Write| 0    (Read) 2
  Ref   Size  Left Lnk Er N.Loc N.Stm Path
(   1) 31.7G 29:48   1        1     1 /.crypt/-FzSkQU-2euzGhZsYT7finNL/i,RGySgHjMu3nB44dUl4TU-doWJSVqI3yxwHVpal0USF3G1V9jc-V8IKF6bgdmd9mofQ7M2rIpCHWmI9wPEbxe-a
(   2) 20.3G 29:50   1        1     1 /.crypt/-FzSkQU-2euzGhZsYT7finNL/X3tP8cQ50EpCQ13,JLLORWOpNCFg8gySE,wCsnlg5BcNHi9ODtGleUX4EH83hceIChsCn87xpMoLLEJ9FdlpGu21

Et bravo pour avoir regardé le code.

Oui les "threads" et le parallélisme c'est très complexe, difficile à debugger, mais "amusant" à faire. Je triche une peu sur le "pas de lock" avec quelques instructions "atomiques" comme dans la fonction post_action()

/*
 * @brief post an action to a thread
 *
 * @param the read action to assign
 * @param the thread where to post the action
 * @return none
 */
static void post_action(struct action *a, tid_t tid)
{
	unsigned int spin = 0;

	AW_ASSERT(NULL != a);
	AW_ASSERT(tid < max_threads);

	a->next = atomic_load_explicit(&ts[tid].actions_queue,
				       memory_order_acquire);
	while(!atomic_compare_exchange_strong_explicit( &ts[tid].actions_queue,
							&a->next, a,
							memory_order_acq_rel,
							memory_order_acquire))
		spin++;
	AW_ERROR(eventfd_write(tc[tid].event_fd, 1));
	aw_count_contentions(spin);
}
#undef post_action

En effet, l'action doit être insérée de façon "atomique" sur la file du thread choisi : tout ou rien. Elle est en concurrence avec d'autres threads qui peuvent "poster" en même temps, et avec le thread sur lequel on a "posté" qui est réveillé par le eventfd_write() [s'il était "endormi" !], et va venir relever les actions qui lui ont été postées.

C'est un classique push/pop avec un pattern : plusieurs producteurs (d'actions à "pusher"), un seul consommateur. Le consommateur unique, lui, est sans lock et sans attente (une seule instruction atomique).

La boucle ci-dessus peut souffrir de "live-lock", c'est à dire que le while pourrait en théorie boucler à l'infini. Cela est mesuré par la statistique aw_count_contentions() et voir ci-dessus. L'affichage donne le nombre de fois où on a dû boucler (contention) car quelqu'un d'autre a fait un truc "en même temps", et un seul des deux "gagne". Elle donne aussi le plus long bouclage, c'est à dire si on a dû tourner 1, 2, 3, etc... fois dans le while avant de "gagner".

Je n'ai jamais constaté plus de "1" sur ce chiffre de longueur de contention, donc le "live-lock" reste théorique pour l'instant.

Tu trouveras d'infinies discussions sur le web quant à l'intérêt du spin-lock (ci-dessus) ou du lock "en dur".



Concernant le bouclage, on pourrait mettre une assertion pour quand on boucle plus de 64 fois (c'est impossible vu qu'on n'a que 64 streams possibles), mais ça reviendrait à remplacer un bouclage par un crash, car c'est ce que fait l'assertion (AW_ASSERT) si la condition n'est pas réalisée. C'est sans doute mieux pour debugger, mais pas mieux pour l’utilisateur !..

Si tu veux tester ainsi avec une assertion, tu peux tâcher de la placer, ou je peux te donner le petit bout de code à rajouter.

Dernière modification par Zakhar (Le 05/09/2022, à 09:17)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#430 Le 05/09/2022, à 09:18

grosjean

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Les stats au moment d'un thread bloque:

Every 3.0s: cat /home/jenfi/WORK/1fichier/.stats                                                                                                                                     nuc: Mon Sep  5 10:11:53 2022
Uptime: 00:02:15

Readers:
    Files    Streams  Start streams             Fuse requests   Cur/Tot.  Cur/Tot. AvgTim MaxTim Down. N.Req AvgTim MaxTim Speed Av.Sp Over.
01: 2     3  45   133 .65557 .80287 41.6M   845 .01568 .74803     0 1707K 60.5M
├─(   2) 0/* 22    65 .65151 .77402 21.6M   440 .01116 .74483     0 1883K 31.6M
└─(   1) 0/* 23    67 .65755 .80287 20.0M   405 .02060 .74803     0 1551K 28.9M
-------------------------------------------------------------------------------
Tt  2     3  45   133 .65557 .80287 41.6M   845 .01568 .74803     0 1707K 60.5M

API:                        N.Req  Avg Time  Max Time Retry N.Err
folder/ls.cgi         :     5    0.1287    0.1526     0     0
download/get_token.cgi:     3    0.1062    0.1084     0     0
ftp/users/ls.cgi      :     1    0.3331    0.3331     0     0
--------------------------------------------------------------
Total                 :     9    0.1439    0.3331     0     0

         Trig. Timer Poll. Write   API Error  Auto   Total
Refresh:     0     0     1     0     0     0     0       1

             Number Memory
Allocations:  12780   115M
Frees      :  10508   100M
Difference :   2272  14.8M
Contentions:      1      1

Live streams: |Write| 0    (Read) 3
  Ref   Size  Left Lnk Er N.Loc N.Stm Path
(   1) 45.3M 29:02   0        1    67 /Books/PublicAverti/Dany/Dany - T05 - Ca vous choque.cbz
(   2) 44.8M 28:14   0        1    65 /Books/PublicAverti/Dany/Dany - T04 - Ou voulez vous en venir.cbz
(   3) 75.6M 29:54   1        1     ? /Books/PublicAverti/Dany/Dany - T06 - C'est pas serieux.cbz

Un petit coup de gdb:

0x000055f4516052ba in orig_do_free (fh=140011639805312, tid=0 '\000') at astreamfs_worker.c:907
907             for (p = &o_top[tid].first_idx; o_fh[*p] != fh; p = &o_list[*p]);
(gdb) set print repeats 0
(gdb) print fh
$1 = 140011639805312
(gdb) print o_fh
$2 = {0, 140011509072128, 140011507240848, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 
  18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 
  18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 
  18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 
  18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 
  18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 
  18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615, 18446744073709551615}

La fh n'est pas dans la liste de o_fh, du coup, ca doit boucler a l'infini, non ?

Hors ligne

#431 Le 05/09/2022, à 09:23

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Ah oui, on tient une piste !

Je regarderai sur le log "bouclage" que tu m'avais envoyée.

Possiblement une autre bévue de parallélisme, car le 'fh' est bien sensé être là quand on demande un "free"... et c'est pourquoi la boucle for en question n'est pas "protégée" par une autre condition, comme le fait d'avoir atteint la fin de la liste.

Avec les stats on voit que tu es dans un cas d'usage où 1fichierfs est particulièrement inefficace car tu as plus "d'over-read" que de lecture efficace !..
On ne peut sans doute pas faire beaucoup mieux avec de la lecture assez aléatoire, mais sans doute l'élargissement de la fenêtre de lecture peut aider à la performance en déclenchant moins de streams... au détriment d'encore plus d'over-read, mais si on compte le temps plutôt que les datas échangées (qui n'est pas limité sur une ligne fixe), ce serait avantageux.

P.S.: erreur de copier/coller ou tu as bien cet affichage de statistiques là ? En effet la ligne de "titre" des colonnes a l'air tout à fait décalée, regarde par rapport à mon affichage ci-dessus

Dernière modification par Zakhar (Le 05/09/2022, à 09:32)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#432 Le 05/09/2022, à 09:39

grosjean

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

pour les titres des stats, peut-etre du a un souci de terminal et tmux, je reessaierais sans.
Si tu as le bout de code sous la main, je teste smile
Sinon, je regarderais cet apres-midi.

Hors ligne

#433 Le 05/09/2022, à 09:41

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Désolé, pas encore réfléchi à ce qui pourrait "boucler", et donc pas de bout de code à tester !


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#434 Le 07/09/2022, à 18:15

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Bonjour @Grosjean, pourrais-tu me faire une trace debug du cas où ça bloque dans do_free ?

En effet, l'autre trace que j'ai est un boucle ailleurs : dans do_action.

A priori, selon les stats, tu arrives à le faire en 2 minutes 15... ça devrait donc être facile. big_smile


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#435 Le 10/09/2022, à 18:55

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

L'incohérence découverte à l'occasion du crash rapporté pourrait aussi causer une boucle do_free de la façon suivante :
- Thread 1 ferme le dernier stream de fh et donc envoie un ACTION_FREE à thread 0
- Thread 0, juste au même moment et avant de recevoir le ACTION_FREE reçoit une lecture pour le fh, et l'envoie donc à Thread 1 qui possède ce fh (puisqu'on ne sait pas encore que Thread 1 a décidé de fermer, n'ayant pas encore reçu le message).
- Thread 0, reçoit ensuite le ACTION_FREE et libère la ressource fh dans ses tables
- Thread 1 reçoit la lecture sur fh, re-ouvre un stream qui va bien (mapper sur un endroit libéré... ce qui faussera les stats mais sans autre effet pour le moment)
- Thread 1, puisqu'il n'y a pas d'autres lectures, va, au bout d'un moment, décider d'abandonner le stream et va donc envoyer un ACTION_FREE ... sur un fh que le Thread 0 a déjà fermé !..
- Thread 0 va alors effectivement tâcher de libérer un fh qui n'est déjà plus dans ses tables et tombera en boucle infinie.

D'autres choses bizarres peuvent se produire s'il y a d'autres lectures sur ce fh. Dans ce cas thread 0 va "élire" un thread, qui pourrait bien ne pas être thread 1 puisque le fh a été "oublié". On va donc se trouver potentiellement avec plusieurs threads traitant le même fh ce qui n'est pas prévu du tout et peut donner des choses totalement incohérentes ou des crash, boucles infinies, etc...

... mais en l'occurrence c'est un autre bug. En effet, tout ce que je décris ci-dessus ne peut pas se passer quand on tourne avec un seul thread. Dans ce cas les choses sont "sérialisées" (ce qui fait perdre du temps), mais comme rien n'est "en parallèle", l'incohérence trouvée ne joue pas.

Il faut donc que je rajoute un état de synchronisation , pour les free(), aussi bien dans un sens (0 vers thread N) que dans le sens inverse. Sans doute une variable atomique devrait suffire.

Pour l'autre bug... si je peux avoir une trace debug, ça aidera, mais il faudra de toute façon que j'essaye de reproduire le plantage, ne serait-ce que pour constater si je l'ai bien réparé ou pas !

Dernière modification par Zakhar (Le 11/09/2022, à 07:43)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#436 Le 11/09/2022, à 21:13

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Bonjour @Grosjean, grâce à ta trace debug à propos de la boucle infinie de do_action, j'ai trouvé le bug.

Il se situe dans la fonction reclaim_streams()

/*
 * @brief reclaim streams from greedy files
 *
 * This is part of the starvation handling mechanism with max_streams.
 * Each time a new file is allocated to a thread, reclaim streams from files
 * on the same thread that were too greedy.
 *
 * @param private thread data
 * @return none
 */
static void reclaim_streams(struct private_data *pdata)
{
	(...)
	/* Construct the number of streams by fh, skip the owned_stream head
	 * which is the new file added by get_new_stream */

Le commentaire explique qu'on saute le "head" puisque le nouveau stream à partir duquel on fait le reclaim_streams est toujours en tête.
Or cette affirmation est fausse, précisément lorsqu'on a déjà épuisé tous les streams possible (64 en 64 bits). Dans ce cas là, on essaye d'en fermer un et c'est celui-là qui est choisi, mais il reste où il est dans la liste, donc pas forcément "en tête".

Et le code de cette fonction est assez horrible et mal testé, surtout qu'elle part d'une supposition fausse dans certains cas !

Et voici donc ce que montre ta log debug (noms de certains répertoires/fichiers changés pour raison de confidentialité) :

[1fichierfs    99.106] DEBUG: [**]+ >> aw_read_buf(/Books/Dir1/Dir2/Undisclosed_filename.cbr,0x7f815affcc30,16384,0,0x7f815affccd0)
(...)
[1fichierfs    99.106] DEBUG: [**]+++++ >> post_action(0x7f815affc970,0)
[1fichierfs    99.106] DEBUG: [**]+++++ << post_action(0x7f815affc970,0)
[1fichierfs    99.106] DEBUG: [00]... << wait_for_actions_or_reads(0x7f818c849c00)
[1fichierfs    99.106] DEBUG: [00]... >> do_actions(0x7f818c849c00)
[1fichierfs    99.106] DEBUG: [00]..... >> associate_read_to_stream(0x7f818c849c00,0x7f815affc970)
[1fichierfs    99.106] DEBUG: [00]....... >> associate_read_to_thread(0x7f815affc970)
[1fichierfs    99.106] DEBUG: [00]......... >> find_fh(140193302964944)
[1fichierfs    99.106] DEBUG: [00]......... << find_fh(140193302964944) = 2
[1fichierfs    99.106] DEBUG: [00]......... >> load_balance_threads(140193302964944)
[1fichierfs    99.106] DEBUG: [00]......... << load_balance_threads(140193302964944) = 0
[1fichierfs    99.106] DEBUG: [00]....... << associate_read_to_thread(0x7f815affc970) = 0
[1fichierfs    99.106] DEBUG: [00]....... >> get_new_stream(0x7f818c849c00,140193302964944)
[1fichierfs    99.106] DEBUG: [00]......... >> close_an_owned_stream(0x7f818c849c00)
[1fichierfs    99.106] DEBUG: [00]........... >> can_close(0x7f818c849c00,50,0x7f818c8499f6)
[1fichierfs    99.106] DEBUG: [00]............. >> is_idle(0x7f818c849c00,50,0)
[1fichierfs    99.106] DEBUG: [00]............... >> aw_ops.close_if_idle(140193638326912,0x7f8164071da0,3795162,3795182,0)
[1fichierfs    99.106] DEBUG: [00]............... << aw_ops.close_if_idle(140193638326912,0x7f8164071da0,3795162,3795182,0) = 0
[1fichierfs    99.106] DEBUG: [00]............. << is_idle(0x7f818c849c00,50,0) = 0
[1fichierfs    99.106] DEBUG: [00]........... << can_close(0x7f818c849c00,50,0x7f818c8499f6) = 0
[1fichierfs    99.106] DEBUG: [00]........... >> can_close(0x7f818c849c00,0,0x7f818c8499f6)
[1fichierfs    99.106] DEBUG: [00]............. >> is_idle(0x7f818c849c00,0,0)
[1fichierfs    99.106] DEBUG: [00]............... >> aw_ops.close_if_idle(140193638326912,0x7f815002a1b0,3795162,3795182,0)
[1fichierfs    99.106] DEBUG: [00]............... << aw_ops.close_if_idle(140193638326912,0x7f815002a1b0,3795162,3795182,0) = 0
[1fichierfs    99.106] DEBUG: [00]............. << is_idle(0x7f818c849c00,0,0) = 0
[1fichierfs    99.106] DEBUG: [00]........... << can_close(0x7f818c849c00,0,0x7f818c8499f6) = 0
(...)
[1fichierfs    99.106] DEBUG: [00]........... >> can_close(0x7f818c849c00,52,0x7f818c8499f6)
[1fichierfs    99.106] DEBUG: [00]............. >> is_idle(0x7f818c849c00,52,0)
[1fichierfs    99.106] DEBUG: [00]............... >> aw_ops.close_if_idle(140193906560944,0x7f817001fca0,3795127,3795182,0)
[1fichierfs    99.106] DEBUG: [00]............... << aw_ops.close_if_idle(140193906560944,0x7f817001fca0,3795127,3795182,0) = 1
[1fichierfs    99.106] DEBUG: [00]............... >> free_all_gaps(52)
[1fichierfs    99.106] DEBUG: [00]................. >> free_gap(0x7f81440010f0,1)
[1fichierfs    99.106] DEBUG: [00]................... >> aw_ops.free(0x7f817412f000)
[1fichierfs    99.106] DEBUG: [00]................... << aw_ops.free(0x7f817412f000)
[1fichierfs    99.106] DEBUG: [00]................... >> aw_ops.free(0x7f81440010f0)
[1fichierfs    99.106] DEBUG: [00]................... << aw_ops.free(0x7f81440010f0)
[1fichierfs    99.106] DEBUG: [00]................. << free_gap(0x7f81440010f0,1)
[1fichierfs    99.106] DEBUG: [00]............... << free_all_gaps(52)
[1fichierfs    99.106] DEBUG: [00]............... >> set_fd(0x7f818c849c00,52,4,4,-1)
[1fichierfs    99.106] DEBUG: [00]............... << set_fd(0x7f818c849c00,52,4,4,-1)
[1fichierfs    99.106] DEBUG: [00]............. << is_idle(0x7f818c849c00,52,0) = 1
[1fichierfs    99.106] DEBUG: [00]........... << can_close(0x7f818c849c00,52,0x7f818c8499f6) = 1
[1fichierfs    99.106] DEBUG: [00]......... << close_an_owned_stream(0x7f818c849c00) = 52
[1fichierfs    99.106] DEBUG: [00]......... >> detach_stream(0x7f818c849c00,52)
[1fichierfs    99.106] DEBUG: [00]......... << detach_stream(0x7f818c849c00,52)
[1fichierfs    99.106] DEBUG: [00]....... << get_new_stream(0x7f818c849c00,140193302964944) = 52

Il s'agit d'un nouveau fichier dont on veut lire les 16K à l'offset 0.

Comme c'est un nouveau fichier, on lui associe un thread, en l'occurrence c'est le thread 0 qui est choisi.
On rentre ensuite dans la fonction get_new_stream pour trouver un stream pour cette lecture. Comme on n'a plus de streams disponibles (les 64 streams sont déjà actif ailleurs), on entre dans la fonction close_an_owned_stream() qui tente de fermer un des streams de ce thread.
On voit que les premiers can_close() qui sont essayés échouent (retour 0 = false = on ne peut pas fermer), puis on trouve finalement le stream 52 qui veut bien se libérer.

La lecture en cours va donc se passer sur thread 0 / stream 52.

Jusque là, tout est normal et "work as design" !..

Maintenant on appelle toujours reclaim_streams() quand on vient d'attribuer un nouveau stream. Pour des questions de "starvation", il faut éviter qu'un seul fichier s'accapare tous les streams et que les autres n'aient plus rien !.. Donc cela sert à fermer (si possible) des streams aux fichiers qui seraient trop "gourmands". Le nombre de streams autorisés pour chaque fichier varie en fonction du nombre de fichiers actifs et du nombre de threads, et il est donné par la fonction max_streams().

Et là voici ce qu'il se passe.

[1fichierfs    99.106] DEBUG: [00]....... >> reclaim_streams(0x7f818c849c00)
[1fichierfs    99.106] DEBUG: [00]......... >> max_streams(0x7f818c849c00)
[1fichierfs    99.106] DEBUG: [00]......... << max_streams(0x7f818c849c00) = 6
[1fichierfs    99.106] DEBUG: [00]......... >> find_owned_fh(0x7f818c849c00,140193638236464)
[1fichierfs    99.106] DEBUG: [00]......... << find_owned_fh(0x7f818c849c00,140193638236464) = 59
[1fichierfs    99.106] DEBUG: [00]......... >> close_fh_stream(0x7f818c849c00,59)
[1fichierfs    99.106] DEBUG: [00]........... >> can_close(0x7f818c849c00,59,0x7f818c849996)
[1fichierfs    99.106] DEBUG: [00]............. >> is_idle(0x7f818c849c00,59,0)
[1fichierfs    99.106] DEBUG: [00]............... >> aw_ops.close_if_idle(140193638236464,0x7f817c04e200,3795157,3795182,0)
[1fichierfs    99.106] DEBUG: [00]............... << aw_ops.close_if_idle(140193638236464,0x7f817c04e200,3795157,3795182,0) = 0
[1fichierfs    99.106] DEBUG: [00]............. << is_idle(0x7f818c849c00,59,0) = 0
[1fichierfs    99.106] DEBUG: [00]........... << can_close(0x7f818c849c00,59,0x7f818c849996) = 0
[1fichierfs    99.106] DEBUG: [00]........... >> can_close(0x7f818c849c00,55,0x7f818c849996)
[1fichierfs    99.106] DEBUG: [00]............. >> is_idle(0x7f818c849c00,55,0)
[1fichierfs    99.106] DEBUG: [00]............... >> aw_ops.close_if_idle(140193638236464,0x7f814c0e28d0,3795152,3795182,0)
[1fichierfs    99.106] DEBUG: [00]............... << aw_ops.close_if_idle(140193638236464,0x7f814c0e28d0,3795152,3795182,0) = 0
[1fichierfs    99.106] DEBUG: [00]............. << is_idle(0x7f818c849c00,55,0) = 0
[1fichierfs    99.106] DEBUG: [00]........... << can_close(0x7f818c849c00,55,0x7f818c849996) = 0
(...)
[1fichierfs    99.106] DEBUG: [00]........... >> is_idle(0x7f818c849c00,19,1)
[1fichierfs    99.106] DEBUG: [00]............. >> aw_ops.close_if_idle(140193638236464,0x7f81600051e0,3795152,3795182,1)
[1fichierfs    99.106] DEBUG: [00]............. << aw_ops.close_if_idle(140193638236464,0x7f81600051e0,3795152,3795182,1) = 1
[1fichierfs    99.106] DEBUG: [00]............. >> free_all_gaps(19)
[1fichierfs    99.106] DEBUG: [00]............. << free_all_gaps(19)
[1fichierfs    99.106] DEBUG: [00]............. >> set_fd(0x7f818c849c00,19,4,4,-1)
[1fichierfs    99.106] DEBUG: [00]............. << set_fd(0x7f818c849c00,19,4,4,-1)
[1fichierfs    99.106] DEBUG: [00]........... << is_idle(0x7f818c849c00,19,1) = 1
[1fichierfs    99.106] DEBUG: [00]......... << close_fh_stream(0x7f818c849c00,59) = 19
[1fichierfs    99.106] DEBUG: [00]......... >> release_stream(0x7f818c849c00,19)
[1fichierfs    99.106] DEBUG: [00]........... >> detach_stream(0x7f818c849c00,19)
[1fichierfs    99.106] DEBUG: [00]........... << detach_stream(0x7f818c849c00,19)
[1fichierfs    99.106] DEBUG: [00]......... << release_stream(0x7f818c849c00,19)

[1fichierfs    99.106] DEBUG: [00]......... >> close_fh_stream(0x7f818c849c00,59)
[1fichierfs    99.106] DEBUG: [00]........... >> can_close(0x7f818c849c00,59,0x7f818c849996)
[1fichierfs    99.106] DEBUG: [00]............. >> is_idle(0x7f818c849c00,59,0)
[1fichierfs    99.106] DEBUG: [00]............... >> aw_ops.close_if_idle(140193638236464,0x7f817c04e200,3795157,3795182,0)
[1fichierfs    99.106] DEBUG: [00]............... << aw_ops.close_if_idle(140193638236464,0x7f817c04e200,3795157,3795182,0) = 0
[1fichierfs    99.106] DEBUG: [00]............. << is_idle(0x7f818c849c00,59,0) = 0
[1fichierfs    99.106] DEBUG: [00]........... << can_close(0x7f818c849c00,59,0x7f818c849996) = 0
[1fichierfs    99.106] DEBUG: [00]........... >> can_close(0x7f818c849c00,55,0x7f818c849996)
[1fichierfs    99.106] DEBUG: [00]............. >> is_idle(0x7f818c849c00,55,0)
[1fichierfs    99.106] DEBUG: [00]............... >> aw_ops.close_if_idle(140193638236464,0x7f814c0e28d0,3795152,3795182,0)
[1fichierfs    99.106] DEBUG: [00]............... << aw_ops.close_if_idle(140193638236464,0x7f814c0e28d0,3795152,3795182,0) = 0
[1fichierfs    99.106] DEBUG: [00]............. << is_idle(0x7f818c849c00,55,0) = 0
[1fichierfs    99.106] DEBUG: [00]........... << can_close(0x7f818c849c00,55,0x7f818c849996) = 0
(...)
[1fichierfs    99.106] DEBUG: [00]........... >> is_idle(0x7f818c849c00,55,1)
[1fichierfs    99.106] DEBUG: [00]............. >> aw_ops.close_if_idle(140193638236464,0x7f814c0e28d0,3795152,3795182,1)
[1fichierfs    99.106] DEBUG: [00]............. << aw_ops.close_if_idle(140193638236464,0x7f814c0e28d0,3795152,3795182,1) = 1
[1fichierfs    99.106] DEBUG: [00]............. >> free_all_gaps(55)
[1fichierfs    99.106] DEBUG: [00]............... >> free_gap(0x7f8144002770,1)
[1fichierfs    99.106] DEBUG: [00]................. >> aw_ops.free(0x7f8174263000)
[1fichierfs    99.106] DEBUG: [00]................. << aw_ops.free(0x7f8174263000)
[1fichierfs    99.106] DEBUG: [00]................. >> aw_ops.free(0x7f8144002770)
[1fichierfs    99.106] DEBUG: [00]................. << aw_ops.free(0x7f8144002770)
[1fichierfs    99.106] DEBUG: [00]............... << free_gap(0x7f8144002770,1)
[1fichierfs    99.106] DEBUG: [00]............... >> free_gap(0x7f8144002500,1)
[1fichierfs    99.106] DEBUG: [00]................. >> aw_ops.free(0x7f8174119000)
[1fichierfs    99.106] DEBUG: [00]................. << aw_ops.free(0x7f8174119000)
[1fichierfs    99.106] DEBUG: [00]................. >> aw_ops.free(0x7f8144002500)
[1fichierfs    99.106] DEBUG: [00]................. << aw_ops.free(0x7f8144002500)
[1fichierfs    99.106] DEBUG: [00]............... << free_gap(0x7f8144002500,1)
[1fichierfs    99.106] DEBUG: [00]............... >> free_gap(0x7f815c011eb0,1)
[1fichierfs    99.106] DEBUG: [00]................. >> aw_ops.free(0x7f817413c000)
[1fichierfs    99.106] DEBUG: [00]................. << aw_ops.free(0x7f817413c000)
[1fichierfs    99.106] DEBUG: [00]................. >> aw_ops.free(0x7f815c011eb0)
[1fichierfs    99.106] DEBUG: [00]................. << aw_ops.free(0x7f815c011eb0)
[1fichierfs    99.106] DEBUG: [00]............... << free_gap(0x7f815c011eb0,1)
[1fichierfs    99.106] DEBUG: [00]............... >> free_gap(0x7f8160023d50,1)
[1fichierfs    99.106] DEBUG: [00]................. >> aw_ops.free(0x7f8174221000)
[1fichierfs    99.106] DEBUG: [00]................. << aw_ops.free(0x7f8174221000)
[1fichierfs    99.106] DEBUG: [00]................. >> aw_ops.free(0x7f8160023d50)
[1fichierfs    99.106] DEBUG: [00]................. << aw_ops.free(0x7f8160023d50)
[1fichierfs    99.106] DEBUG: [00]............... << free_gap(0x7f8160023d50,1)
[1fichierfs    99.106] DEBUG: [00]............... >> free_gap(0x7f8148001760,1)
[1fichierfs    99.106] DEBUG: [00]................. >> aw_ops.free(0x7f8174200000)
[1fichierfs    99.106] DEBUG: [00]................. << aw_ops.free(0x7f8174200000)
[1fichierfs    99.106] DEBUG: [00]................. >> aw_ops.free(0x7f8148001760)
[1fichierfs    99.106] DEBUG: [00]................. << aw_ops.free(0x7f8148001760)
[1fichierfs    99.106] DEBUG: [00]............... << free_gap(0x7f8148001760,1)
[1fichierfs    99.106] DEBUG: [00]............. << free_all_gaps(55)
[1fichierfs    99.106] DEBUG: [00]............. >> set_fd(0x7f818c849c00,55,4,4,-1)
[1fichierfs    99.106] DEBUG: [00]............. << set_fd(0x7f818c849c00,55,4,4,-1)
[1fichierfs    99.106] DEBUG: [00]........... << is_idle(0x7f818c849c00,55,1) = 1
[1fichierfs    99.106] DEBUG: [00]......... << close_fh_stream(0x7f818c849c00,59) = 55
[1fichierfs    99.106] DEBUG: [00]......... >> release_stream(0x7f818c849c00,55)
[1fichierfs    99.106] DEBUG: [00]........... >> detach_stream(0x7f818c849c00,55)
[1fichierfs    99.106] DEBUG: [00]........... << detach_stream(0x7f818c849c00,55)
[1fichierfs    99.106] DEBUG: [00]......... << release_stream(0x7f818c849c00,55)

On voit qu'on en droit pour le moment à 6 streams par fichier.
Le premier fichier traité dont le fh est 140193638236464, possède actuellement 8 streams. Aucun ne voulant se fermer "naturellement", on force la fermeture des plus anciens, qui sont les streams 19 et 55.

Jusque là tout est normal...

Mais ensuite on continue avec le deuxième fh de ce thread :

[1fichierfs    99.106] DEBUG: [00]......... >> find_owned_fh(0x7f818c849c00,140193906560944)
[1fichierfs    99.106] DEBUG: [00]......... << find_owned_fh(0x7f818c849c00,140193906560944) = 52
[1fichierfs    99.106] DEBUG: [00]......... >> close_fh_stream(0x7f818c849c00,52)
[1fichierfs    99.106] DEBUG: [00]........... >> can_close(0x7f818c849c00,52,0x7f818c849996)
[1fichierfs    99.106] DEBUG: [00]............. >> is_idle(0x7f818c849c00,52,0)
[1fichierfs    99.106] DEBUG: [00]............... >> free_all_gaps(52)
[1fichierfs    99.106] DEBUG: [00]............... << free_all_gaps(52)
[1fichierfs    99.106] DEBUG: [00]............... >> set_fd(0x7f818c849c00,52,4,4,-1)
[1fichierfs    99.106] DEBUG: [00]............... << set_fd(0x7f818c849c00,52,4,4,-1)
[1fichierfs    99.106] DEBUG: [00]............. << is_idle(0x7f818c849c00,52,0) = 1
[1fichierfs    99.106] DEBUG: [00]........... << can_close(0x7f818c849c00,52,0x7f818c849996) = 1
[1fichierfs    99.106] DEBUG: [00]......... << close_fh_stream(0x7f818c849c00,52) = 52
[1fichierfs    99.106] DEBUG: [00]......... >> release_stream(0x7f818c849c00,52)
[1fichierfs    99.106] DEBUG: [00]........... >> detach_stream(0x7f818c849c00,52)
[1fichierfs    99.106] DEBUG: [00]............. >> do_free(140193906560944,0)
[1fichierfs    99.106] DEBUG: [00]............. << do_free(140193906560944,0)
[1fichierfs    99.106] DEBUG: [00]........... << detach_stream(0x7f818c849c00,52)
[1fichierfs    99.106] DEBUG: [00]......... << release_stream(0x7f818c849c00,52)
[1fichierfs    99.106] DEBUG: [00]....... << reclaim_streams(0x7f818c849c00)

Voila, le fh est 140193906560944, précisément celui qu'on a ouvert précédemment et pour lequel on avait attribué le stream 52... et là on ferme joyeusement (à tort = BUG) le stream 52 qu'on vient de s'attribuer.
La lecture va essayer de se faire sur un stream qui a été fermé, et donc la machine à états est totalement incohérente et ça produit une boucle infinie.

Je suis en train d'essayer de réparer, surtout de coder de façon plus logique ce reclaim_streams().
Telle qu'aujourd'hui, reclaim_stream() produit à tous les coups un écrasement interne sur la variable de pile n_streams car on initialise à zéro l'élément suivant. On va donc à un certain moment remettre idx à zéro et la boucle va aller taper sur des streams qui sont potentiellement à d'autres threads... comme tout cela n'est pas prévu, donc pas protégé par des "atomiques" ou autres... c'est de la chance que ça marche ainsi sans que j'aie eu de bug !..

Au passage max_streams n'est pas fini de coder car il ne tient pas compte du minimum. Et pour éviter la confusion avec des macros à noms similaire, je vais changer son nom en max_streams_allowed().

Le test n'est pas facile car il faut déjà arriver à consommer tous les streams... et je fais rarement ça !.. Et en plus tomber dans le bug (qui n'est sans doute pas totalement systématique !).


P.S.:  tu vois la trace debug aide beaucoup à voir ce qu'il se passe et trouver les bugs... elle est d'ailleurs faite pour ça !.. Donc si tu as le temps de me faire une trace debug pour le do_free() qui boucle je prends. cool

Dernière modification par Zakhar (Le 11/09/2022, à 21:43)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#437 Le 24/09/2022, à 10:20

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Bonjour @grosjean

J'ai poussé sur le GitLab une correction partielle du
- use after free (crash)
- bug dans reclaim_stream (boucle dans do_action)

Il me reste la correction "propre" de la synchronisation manquante qui pourrait provoquer des incohérences.

Cependant, compte tenu du logiciel que tu utilises qui fait beaucoup de lectures "aléatoires", tu risques fort d'arriver sur une limite de 1fichier.com : maximum 50 flux ouverts par client.

Il n'est rien que 1fichierfs puisse faire pour cette limite, puisque c'est le serveur qui pose ces limites afin d'éviter qu'un seul client "bouffe toutes les ressources".

En réalité, en debuggant, je suis arrivé à ces limites là.

La future voie d'élargir la fenêtre de lecture pourrait améliorer la situation dans ton cas, car cela fera sans doute moins de flux (stream) au détriment d'un peu plus "d'over-read" !..

Dernière modification par Zakhar (Le 24/09/2022, à 10:23)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#438 Le 30/09/2022, à 23:25

Asko_D

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Bonjour Zakhar,

Déjà merci pour ton travail ! je l'utilise sur mon serveur depuis désormais 2 ans et demi et le moins qu'on puisse dire c'est que ça marche super bien !

J'ai une question sur l'upload :

Tout d'abords j'ai avec mon serveur une connexion à 2,5 gb/s.

En faisant des test avec les options -4 et -6 pour forcer l'ipv4 ou v6 j'ai remarqué que je n'atteignais pas la même valeur max.

En IPv4 j'atteignais jusqu'à hier une vitesse max de 45 mo/s en upload et en IPv6 22,5 mo/s.

Je dis jusqu'à hier car j'ai fait une demande à la team 1fichier (toujours sans réponse) si cela était normal que les 2 protocoles ai une vitesse max différente au niveau des serveurs FTP et depuis je me retrouve aussi bloqué au alentours de 22,5 mo/s en IPv4. Je sais que ce n'est pas connexion qui a un problème car j'ai pu lancé un maximum de 4 copies simultanés avec chacun un maximum de 20 mo/s qui en cumulé fait 80 mo/s (alors qu'avant j'était plus vers 170-180 mo/s).

Est-ce que toi aussi tu rencontres cette problématique depuis hier ?

Il m'arrive aussi d'avoir des erreurs de copie (je fait des transferts d'environ 6 To au total de temps en temps) avec comme intitulé "erreur de splice". Je n'ai pas le message d'erreur exactement en tête mais je pourrais te le transmettre la prochaine fois que je l'ai. j'utilise aussi les 3 options fuse pour les transferts en splice.

Merci encore une fois pour tout le boulot que tu as fait smile

A bientôt !

Hors ligne

#439 Le 01/10/2022, à 09:10

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Bonjour @grosjean

La vitesse d'upload est effectivement "bizarre".

Je suis chez Free et fonctionne toujours en ipV4 pour éviter les problèmes quand plusieurs PC derrière une même freebox font des transferts.
A Paris j'obtiens bien ce que tu rapporte, dans le meilleur des cas autour de 48Mo/s.
Par contre, le même test à Nice ne dépasse jamais les 28Mo/s et dans la plupart du temps c'est plutôt 18Mo/s

Pour la limite à Nice, on peut déjà en déduire que ce n'est donc dû aux serveurs FTP côté 1fichier.com qui savent visiblement encaisser autour de 50mo/s sur un simple flux upload, et que la limite est donc ailleurs : Free, réseau de transit.

Il faut savoir que Free n'a pas voulu "négocier" avec 1fichier.com et celui-ci utilise donc un transitaire.
Après on peut effectivement constater que quand on fait des uploads en parallèles, on parvient à monter au delà de l'upload tout seul. La limite n'est donc pas non plus par IP, mais sans doute un truc qui cherche à faire de la QoS chez Free sur l'upload, et le fait par flux unitaire TCP.

Bref, cette limite est à rechercher chez Free ou les transitaires, et elle est malheureusement indépendante de la bonne volonté de 1fichierfs ! tongue

Je n'ai pas testé en ipv6, mais la limitation est sans doute aussi dans les mêmes couches : Free ou transitaires.

"Erreur de splicing" est un truc assez générique de fuse qui utilise les fonctionnalité "splicing" du kernel. On peut forcer le moteur à ne pas utiliser le "splicing", auquel cas en lecture les fonctions utilisées seront l'ancien interface aw_read() au lieu de aw_read_buf() qui correspond au splicing. En écriture le no_splice n'a d'effet que sur la communication kernel/fuse, je n'ai pas développé de write_buf(). Mais en réalité c'est sans doute juste une erreur quelconque de réseau qui fait que l'upload FTP s'est cassé, et tu auras juste une autre erreur en faisant ça qui n'aidera pas davantage !

Les options pour ne pas "splicer" sont standard à tout driver fuse, tu les trouveras en affichant l'aide par exemple :

-o no_splice_write

Mais encore une fois ce n'est pas très utile, ça va juste consommé plus de CPU et s'il y a un plantage FTP il n'y a pas de miracle, tu auras une erreur, elle sera juste autre chose !


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#440 Le 03/10/2022, à 17:00

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Bonjour @grosjean

Je viens de pousser sur le gitlab les "fix" concernant les deux bugs que tu m'as aidé à corriger, merci à toi !

-1) reclaim_stream() était mal codé, et effectivement dans certain cas mettait le thread dans un état incohérent qui provoquait la boucle infinie dans do_action() avec consommation à 100% d'un des threads
-2) bug dans do_free qui était double : un "use after free" assez évident et simple à corriger, mais surtout une erreur dans la synchronisation qui m'a demandé de rajouter des variables de suivi des états.

Le deuxième bug, et ce que j'ai vu dans ton premier fichier log que tu m'as envoyé en MP est à peu près non-reproductible !..
En effet, quand 1fichierfs ouvre un des fichiers du stockage, il alloue une zone mémoire pour gérer celui-ci, et une autre zone mémoire pour chaque open() que fuse envoie qui pointe vers la première zone allouée pour le fichier.
En effet, plusieurs logiciels peuvent très bien ouvrir le même fichier en même temps (en lecture), et d'ailleurs certains logiciels (vlc par exemple) peuvent ouvrir plusieurs fois le même fichier en cours de visionnage.

Tant que le fichier est ouvert, qu'il n'y a pas d'erreur, et que le "ticket de téléchargement" est valide (30 minutes pour être conservatif), on garde la zone mémoire allouée.
C'est donc l'adresse de cette zone qui dit au moteur de lecture qu'il s'agit du "même fichier", même si fuse a envoyé plusieurs open() différents.

On aurait pu faire "mieux" en liant cela à l'URL du fichier qui est un meilleur critère... mais ça obligerait à faire des algorithmes potentiellement assez complexes pour mémoriser "à vie" (du driver !) des identifiants pour chaque URL.

A chaque fois qu'on alloue une nouvelle zone mémoire pour un fichier qui n'était pas ouvert, on envoie celle-ci, c'est le "fh" au moteur de lecture pour qu'il oublie ce qu'il pourrait avoir avec le même "fh" et éviter de se retrouver avec des bouts d'un autre fichier.

Or dans la première log que tu m'as envoyée, il y a ré-utilisation d'un fh. C'est quasi impossible à reproduire fiablement puisque le fh étant une adresse mémoire qui est allouée par malloc() (ou équivalent), ça veut dire qu'il y a eu des-allocation (free()), puis nouvelle allocation et l'appel à malloc() a rendu exactement la même adresse qui avait été désallouée.
Ça peut sans doute arriver... et ça dépend de l'algorithme à l'intérieur de malloc() qui est indépendant du code de 1fichierfs !..

Le plus probable est que tu sois en fait déjà tombé sur l'erreur 500 qui marque le fait que tu as plus de 50 flux en parallèle, à cause de la façon dont ton logiciel de "Comics" fait ses reads non-séquentiels.
Tu as aussi mis un refresh rapide si j'ai bien vu.
Le refresh va donc "purger" les fichiers en erreur quand il passe.
Avec un peu de chance (ou de malchance !) les fichiers suivants qui vont être ouverts vont récupérer une adresse mémoire qui était déjà préalablement utilisée !..

Donc en gros pour reproduire il faut
- créer des erreurs (on peut facilement reproduire en ouvrant plus de 50 flux)
- faire un refresh
- refaire des ouvertures
- et avoir la chance/malchance que malloc() ré-attribue une adresse mémoire précédemment utilisée.

Bref assez impossible à reproduire de façon fiable hélas.

Je soupçonne aussi que ça t'est arrivé parce que tu as déjà buté dans la limite des "50 flux" de 1fichier.com. Je ne sais pas ce qu'une lecture en erreur provoque sur ton logiciel de "Comics", mais comme expliqué précédemment, il n'est rien que je puisse faire pour corriger cela puisque c'est une "limite serveur".
Donc dans tous les cas, la correction présente devrait éviter les boucles infinies et les crash, mais est impuissante face à la limite imposée par le serveur.

La piste à venir d'augmenter le "read-ahead" te permettra sans doute d'éviter cette erreur, mais ce sera un peu "empirique" car ça dépend de combien sont "espacées" les lectures aléatoires.


Le gitlab est donc à jour de la version 1.9.3 censée corriger le bug#5 (plusieurs bugs en réalité).


Je te laisse tester avec ton environnement qui a réussi à tomber sur des bugs que je n'avais jamais eus... de mon côté je fais tourner un certain temps sur mes machines, et je ferai un package d'ici une quinzaine si je ne vois pas de régression.

Merci encore pour ton signalement précis qui a permis d'améliorer en supprimant deux bugs (dont un crash et un "thread à 100%").

Dernière modification par Zakhar (Le 03/10/2022, à 17:05)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#441 Le 05/10/2022, à 08:58

grosjean

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Hello @Zakhar,

desole, je reviens un peu tard, pas mal occupe ces derniers temps.
Faut que je prenne le temps de lire ton analyse et de la digerer smile

En attendant, je fais tourner la nouvelle version, et je te tiens au courant.
J'ai utilise rclone comme workaround, et meme si ca fait le taf, cote perf rien a voir avec 1fichierfs, du coup, encore un grand merci.

Petite question qui n'a rien a voir, le passage a fuse3 necessiterai t'il beaucoup de changement cote astreamfs?
Et cela apporterait il quelque chose ?

Merci encore.
A+

Hors ligne

#442 Le 05/10/2022, à 12:26

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

grosjean a écrit :

En attendant, je fais tourner la nouvelle version, et je te tiens au courant.
J'ai utilise rclone comme workaround, et meme si ca fait le taf, cote perf rien a voir avec 1fichierfs, du coup, encore un grand merci.

Oui, le principe de rclone est assez différent. Son fonctionnement standard est de copier les fichiers accédés en local (au moins par "gros morceaux") et ensuite de servir les requêtes de lecture/écriture à partir du cache de copie local.
Cela présente des avantages et inconvénients.
L'avantage, une fois quel le fichier est dans la cache locale, c'est que c'est beaucoup plus rapide (enfin si le support local est plus rapide que la connexion web... ce qui n'est pas toujours le cas !). Cela permet aussi plus de souplesse pour la partie écriture en ouvrant la possibilité aux écritures aléatoires, mais ensuite il faut re-uploader tout le fichier. Aussi l'avantage que si par hasard le fichier se trouve "en cache", ça peut fonctionner également "off line".
Au niveau des inconvénients, le premier qui était rédhibitoire pour moi est qu'il faut un support local conséquent !.. Un de mes besoins était de faire fonctionner sur Raspberry PI4 avec sa seule toute petite carte SD contenant l'O.S., et sans avoir besoin de brancher un support externe en USB.

1fichierfs est donc plus "réactif" puisqu'il ne va pas chercher à copier des "gros morceaux" de fichier (je crois que par défaut rclone fait du 100Mo), mais va juste lire ce qui est demandé, en essayant dans la mesure du possible de faire du séquentiel, ce qui est plus rapide et moins limité par le serveur. Bien sûr 1fichierfs nécessite exactement zéro espace local pour les fichiers, c'est son design puisque tout accès est fait en ligne. Le revers de la médaille est que bien sûr ça ne fonctionne pas offline... ou quand 1fichier.com est planté (ce qui est heureusement assez rare).

Cela dit, je crois bien qu'on peu configurer rclone pour ne pas du tout utiliser d'espace de cache local. Dans ce cas le fonctionnement va se rapprocher de 1fichierfs, mais pas sûr des "optimisations" de rclone dans ce mode de fonctionnement là.

Aussi, rclone est un outil générique qui fonctionne avec tout plein de backends, 1fichier.com n'est qu'un parmi d'autres.
En ce sens il serait plus comparable à astreamfs qui est de "driver générique" dont est issu 1fichiefs. astreamfs fonctionne avec n'importe quelles URL http ou https, pourvu que le serveur sache servir des "range". Bien sûr on est alors en "read-only" parce que le web n'a pas de véritable standard très répandu pour écrire (à part WebDAV mais il est peu répandu précisément).

1fichiefs, bien que dérivé de astreamfs est adapté aux petits oignons à 1fichier.com, bien plus qu'un rclone "générique" ne peut le faire.

grosjean a écrit :

Petite question qui n'a rien a voir, le passage a fuse3 necessiterai t'il beaucoup de changement cote astreamfs?
Et cela apporterait il quelque chose ?

Merci encore.
A+

J'ai vu, mais pas encore analysé les questions parce qu'égoïstement j'utilise la 20.04 qui n'a pas fuse 3 !..
L'ordre des questions est de commencer par "qu'est-ce que ça apporte".
Si ça n'apporte rien de vraiment concret, on peut rester en fuse 2 (niveau 26 requis pas 1fichierfs) et ça continue de fonctionner avec fuse 3 qui est "rétro-compatible", avec exactement zéro modification !..

Je vais sans doute sauter la 22.04
Des ajouts comme Wayland par défaut sont un peu trop déstructurants. J'ai essayé sur le Raspberry Pi 4 et ça casse par exemple l'accès à distance par VNC alors que ça fonctionnait parfaitement avec X11.
X11 est sans doute antique et plein de "verrues", mais d'un fonctionnement sans faille avec tous les bugs "fixés" par ses décennies d'expérience. Wayland est encore "un peu jeune".
Avec la 24.04 j'ose espérer qu'ils n'auront plus besoin de proposer en parallèle une "Session sous X11" et que je pourrais y aller... avec fuse 3 qui sera alors bien peaufiné aussi.

J'aurai peut-être regardé la question "qu'est-ce que ça apporte" avant ça quand même. lol

Il est toutefois à noter que même avec fuse 2, certaines fonctions sont inutilisées car inutiles ou impossibles avec 1fichier.com.
Certaines fonctions sont juste simulées.

Exemple :

/*
 * @brief fuse callback for chmod
 *
 * This is only to avoid commands like cp, chmod, etc... to complain about
 * not being able to change modes.
 * It doesn't do anything because there is nothing the driver can do to
 * alter modes since the server does not handle them.
 *
 * @param path to the file
 * @param mode to set
 * @return 0 (always !)
 */

int unfichier_chmod(const char *path, mode_t mode)
{
	return FAKE_CALLBACK;
}

Donc en fait le code existe. Quand un utilitaire comme cp (copie) appelle chmod pour régler le mode du fichier copié, on lui répond que tout va bien et qu'on a fait ce qui était demandé.
Je pense qu'à peu près tout le monde est capable de voir qu'en réalité on n'a rien fait du tout, on a juste répondu Ok (zéro en réalité, FAKE_CALLBACk étant défini à 0) pour que "cp" ne râle pas en disant qu'il ne peut pas régler certains détails de la copie.

Pareil pour utimens (régler date/heure), chown.

Donc tu vois, déjà fuse 2 on n'utilise pas tout... et parfois on "triche" !.. lol

Dernière modification par Zakhar (Le 05/10/2022, à 12:43)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#443 Le 16/10/2022, à 11:57

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Évidemment, le nouvel algorithme pour do_free() a créé de nouveaux bugs. Je tâche de les "fixer" avant de faire un 1.9.3


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#444 Le 23/10/2022, à 13:11

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

(23 octobre 2023) Version 1.9.3

Nouveautés par rapport à la 1.9.2

  • Fix crash : dans l'algorithme de libération do_free() avec un "use after free".

  • Fix architecture : remaniement de l'algorithme de libération, il manquait une synchronisation.

  • Fix bug : la fonction reclaim_stream() était très mal codée et présentait plusieurs bugs dont un entraînant une boucle infinie.

Correction de bugs
Compte tenu des nombreux bugs et crash corrigés, je suggère d'accepter la mise à jour !
Remerciements à @grosjean pour ses rapports de bug très détaillés qui ont permis de voir rapidement où se situaient les erreurs.

Note importante : certaines limitations sont dues au serveur, de par l'infrastructure mutualisée, comme par exemple la limitation à 50 "streams" ouverts en même temps. Il n'est rien que 1fichierfs puisse faire puisqu'il s'agit d'une limitation de l'hébergeur. Ces limites peuvent se manifester par des erreurs de lecture surtout lorsque les programmes client font beaucoup de lectures aléatoires. Un fichier qui a subi des erreur est marqué en erreur. Il faut forcer un refresh pour retirer l'erreur et tenter à nouveau.

Raspberry Pi OS -ex Raspbian- (Raspbian Buster 32 bits)1fichierfs_1.9.3~buster-1_armhf.deb

$ stat -c "%s %n" 1fichierfs_1.9.3~buster-1_armhf.deb; sha256sum 1fichierfs_1.9.3~buster-1_armhf.deb; md5sum 1fichierfs_1.9.3~buster-1_armhf.deb
96344 1fichierfs_1.9.3~buster-1_armhf.deb
6b8ba3ab5a6615c361c7da822fcb0947b9ddd7aee4489a7bb1ec72879f5aff3b  1fichierfs_1.9.3~buster-1_armhf.deb
8233177ba6e2e00fe154e50614cfd545  1fichierfs_1.9.3~buster-1_armhf.deb

Dernière modification par Zakhar (Le 23/10/2022, à 13:49)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#445 Le 26/10/2022, à 17:45

Jarodd

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Bonjour,
J'ai fait la màj vers la 1.9.3. Mais en l'utilisant, ma connexion a été blacklistée par 1Fichier.

Pour des raisons de sécurité, suite à de trop nombreuses tentatives d'identification erronées, votre adresse IP (xxx) est momentanément bloquée.
Consultez la page d'aide pour davantage d'informations.
erreur #81 

Je faisais un transfert de 26 fichiers, auparavant j'en faisais bien plus sans avoir ce blocage. Est-ce que cela a un rapport avec la version 1.9.3 ?

Edit : pour être plus précis, le montage a été déconnecté, et je ne peux plus m'y reconnecter (ce n'est pas un simple blocage pour les transferts : je n'ai plus accès au montage).

$ 1fichierfs --api-key=@~/.config/1fichier/api.key /media/jarodd/1fichier/
[1fichierfs     0.092] ERROR: Ignoring: (http_code: 403) url=`https://api.1fichier.com/v1/folder/ls.cgi` name=`/`.

(mon fichier de config n'a pas bougé)
Cela me bloque même l'usage du site, je ne peux plus télécharger (en étant connecté ; sans la connexion, le débit est bridé).

Dernière modification par Jarodd (Le 26/10/2022, à 18:48)


Ubuntu 20.04 LTS (64 bits)

Hors ligne

#446 Le 26/10/2022, à 20:20

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Bonjour Jarodd, non ça m'étonnerait que ça vienne d'une modification.

Quand tu dis "transfert de 26 fichiers" tu entends "upload" (écriture) ou "download" (lecture) ?
La partie "upload" n'a pas changé avec le "nouveau moteur", ça reste l'ancien code.

La partie "download" a profondément changé, mais hormis si tu utilises des programmes qui font des accès aléatoires, ça ne doit pas faire de 403 !.. Et même avec "trop" d'accès aléatoire, la consommation de trop de streams donne une erreur 500 et pas 403, donc pas de bannissement.

Ce qui est par contre possible c'est un changement d'adresse IP, comme je vois que tu ne forces pas en ipv4, si tu es chez un opérateur qui fait l'ipv6, il est bien possible que l'ip change, et c'est même certain si tu utilises 2 PC différents derrière ta box, ils auront chacun une IP différente et ça ne plaît pas à 1fichier.com qui voit ça comme un download de plusieurs adresses.
Si tu insistes à faire ça, c'est le blacklist !

Par exemple le phénomène m'arrive quand j'utilise mon Raspberry Pi4 qui reste allumé dans ma résidence principale, et que j'utilise 1fichierfs depuis une autre connexion. Dans ce cas, il y a bien deux IP différentes et 1fichier.com bloque la deuxième avec un 403.

Par contre l'upload n'est pas bloqué de la sorte, tu peux donc uploader d'autant d'IP différentes que tu veux à la fois, la seule chose interdite est le "download" donc "lecture" de deux adresses IP différentes (qu'elles soient V4 ou V6)

Donc ce que je conseille dans tous les cas c'est de rajouter l'option "-4" pour forcer en ipv4. Si tu es chez un opérateur qui ne fait pas ipv6, l'option -4 ne changera rien... sinon elle peut t'éviter des "surprises" !

Après, sans avoir la log, je ne peux que deviner, et le plus probable est l’événement décrit ci-dessus.

Si tu veux reprendre la 1.9.2, tu dois pouvoir la récupérer sur le launchpad... ou essayer de trouver un commande de "downgrade", mais je doute vraiment que ça puisse venir de cela compte tenu de la différence entre la 1.9.2 et la 1.9.3 qui ne concerne que des "détails" dans la libération des ressources.... "détails" qui provoquaient toutefois des crash !..

EDIT: la commande de dowgrade semble:

sudo apt install 1fichierfs=1.9.2~Focal-1

(Remplacer 'Focal' par la version que tu utilises, donc Xenial, Bionic ou Jammy)

Si tu ne sais pas si ton opérateur fait ipV6, tu peux essayer un truc de ce genre :

$ curl http://icanhazip.com
109.190.253.14

Dans l'exemple ci-dessus on voit que c'est de l'IPV4 (pas de secret international, c'est la Wifi SNCF en l'occurence).

Le "blacklistage" est temporaire, ça devrait revenir en fonctionnement au bout de quelque temps (variable selon ton insistance à utiliser des IP différentes en parallèle !)

Dernière modification par Zakhar (Le 26/10/2022, à 21:00)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne

#447 Le 26/10/2022, à 21:12

Jarodd

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

C'était de l'upload (je sauvegardais 26 fichiers).
Je suis chez Free, avec une IP fixe (obligé pour utiliser le VPN du boulot). C'est possible que cela vienne de là alors (je n'ai pas utilisé 1Fichier depuis plusieurs jours).
Je vais tester l'option "-4", merci. C'est à mettre sur la ligne de comande de l'api ?


Ubuntu 20.04 LTS (64 bits)

Hors ligne

#448 Le 26/10/2022, à 22:48

Zakhar

Re : [1fichier] "Montez" votre stockage 1fichier en une commande simple !

Ok, l'upload n'a pas changé depuis la 1.8.x, et comme en upload tu n'as pas de limitation d'IP unique, ça ne peut pas vraiment venir de ça !.. Enfin à moins que 1fichier.com vienne de mettre une limite, mais je doute qu'ils aient envie de faire une évolution sur la stack FTP qu'on utilise !..

Par contre, oui, Free fait de l'ipv6. C'est même un des premiers. Donc si deux appareils derrière ta Freebox font du download depuis ton compte, ça va apparaître comme deux ip différentes, puisqu'en ipv6 tu as une IP par machine.

Oui pour le forçage en ipV4, tu rajoutes juste ça à la ligne de commande, exemple :

$ 1fichierfs -4 --api-key=@~/.config/1fichier/api.key /media/jarodd/1fichier/

L'ordre des options n'a aucune espèce d'importance, donc tu peux mettre le -4 où tu veux sur la ligne de commande.

A faire sur chaque machine derrière la box qui utilise 1fichier.com

Bien sûr je suppose aussi que tu n'as pas "partagé" ton compte avec quelqu'un d'autre... ça c'est interdit et ça provoque le bannissement si les deux utilisent en même temps.

[EDIT] Il y a aussi dans les "paramètres" du compte 1fichier.com un possibilité de limiter l'API à certaines adresses IP. Si cela a été utilise, il faut vérifier que ton adresse IP est toujours celle déclarée.

Dernière modification par Zakhar (Le 27/10/2022, à 07:42)


"A computer is like air conditioning: it becomes useless when you open windows." (Linus Torvald)

Hors ligne